1mod enum_definition;
2mod enum_impl;
3mod execution_impl;
4mod state;
5
6use crate::build::enum_definition::{
7 collect_enum_definitions_from_dependencies, process_enum_definition,
8 process_pending_enum_definitions,
9};
10use crate::build::enum_impl::{
11 collect_original_enum_decoding_impls_from_dependencies, process_enum_impl,
12 process_pending_enum_impls,
13};
14use crate::build::execution_impl::{
15 collect_enum_execution_impls_from_dependencies, process_execution_impl,
16 process_pending_enum_execution_impls,
17};
18use crate::build::state::State;
19use ab_riscv_macros_common::code_utils::pre_process_rust_code;
20use anyhow::Context;
21use quote::ToTokens;
22use std::path::{Path, PathBuf};
23use std::rc::Rc;
24use std::{env, fs, io, iter};
25use syn::Item;
26
27pub fn process_instruction_macros() -> anyhow::Result<()> {
29 let manifest_dir = env::var_os("CARGO_MANIFEST_DIR").context(
30 "Failed to retrieve `CARGO_MANIFEST_DIR` environment variable, make sure to call \
31 `process_instruction_macros` from `build.rs`",
32 )?;
33 let out_dir = env::var_os("OUT_DIR").context(
34 "Failed to retrieve `OUT_DIR` environment variable, make sure to call \
35 `process_instruction_macros` from `build.rs`",
36 )?;
37 let out_dir = Path::new(&out_dir);
38
39 let mut state = State::new();
40
41 for maybe_enum_definition in collect_enum_definitions_from_dependencies() {
42 let (item_enum, dependencies, source) = maybe_enum_definition?;
43
44 state.insert_known_enum_definition(item_enum, dependencies, source)?;
45 }
46 for maybe_enum_impl in collect_original_enum_decoding_impls_from_dependencies() {
47 let (item_impl, source) = maybe_enum_impl?;
48 state.insert_known_original_enum_decoding_impl(item_impl, source)?;
49 }
50 for maybe_enum_execution_impl in collect_enum_execution_impls_from_dependencies() {
51 let (item_impl, source) = maybe_enum_execution_impl?;
52 state.insert_known_enum_execution_impl(item_impl, source)?;
53 }
54
55 for maybe_rust_file in rust_files_in(Path::new(&manifest_dir).join("src")) {
56 let rust_file = maybe_rust_file.context("Failed to collect Rust files")?;
57 let rust_file = Rc::<Path>::from(rust_file.into_boxed_path());
58 process_rust_file(rust_file.clone(), out_dir, &mut state)
59 .with_context(|| format!("Failed to process Rust file `{}`", rust_file.display()))?;
60 }
61
62 process_pending_enum_definitions(out_dir, &mut state)?;
63 process_pending_enum_impls(out_dir, &mut state)?;
64 process_pending_enum_execution_impls(out_dir, &mut state)
65}
66
67fn rust_files_in(dir: PathBuf) -> Box<dyn Iterator<Item = io::Result<PathBuf>>> {
68 fn walk(dir: PathBuf) -> Box<dyn Iterator<Item = io::Result<PathBuf>>> {
69 let read_dir = match fs::read_dir(dir) {
70 Ok(iter) => iter,
71 Err(error) => {
72 return Box::new(iter::once(Err(error))) as Box<_>;
73 }
74 };
75
76 Box::new(read_dir.flat_map(move |entry_res| {
77 let entry = match entry_res {
78 Ok(entry) => entry,
79 Err(error) => {
80 return Box::new(iter::once(Err(error))) as Box<_>;
81 }
82 };
83
84 let path = entry.path();
85
86 if path.is_dir() {
87 walk(path)
88 } else if path
89 .extension()
90 .and_then(|ext| ext.to_str())
91 .is_some_and(|ext| ext == "rs")
92 {
93 Box::new(iter::once(Ok(path))) as Box<_>
94 } else {
95 Box::new(iter::empty::<io::Result<PathBuf>>()) as Box<_>
96 }
97 }))
98 }
99
100 walk(dir)
101}
102
103fn process_rust_file(source: Rc<Path>, out_dir: &Path, state: &mut State) -> anyhow::Result<()> {
104 let mut file_contents = fs::read_to_string(&source).context("Failed to read Rust file")?;
105 if !file_contents.contains("#[instruction") {
106 return Ok(());
110 }
111
112 pre_process_rust_code(&mut file_contents);
113
114 let file = syn::parse_file(&file_contents).context("Failed to parse Rust file")?;
115
116 for item in file.items {
117 match item {
118 Item::Enum(item_enum) => {
119 let enum_name = item_enum.ident.clone();
120 process_enum_definition(item_enum, out_dir, state).with_context(|| {
121 format!(
122 "Failed to process enum `{enum_name}` in file `{}`",
123 source.display()
124 )
125 })?;
126 }
127 Item::Impl(item_impl) => {
128 let trait_name = item_impl.trait_.as_ref().map(|(_, path, _)| {
129 path.segments
130 .last()
131 .expect("Path is never empty; qed")
132 .ident
133 .clone()
134 });
135 let type_name = item_impl.self_ty.clone();
136 if let Some(result) = process_enum_impl(item_impl.clone(), out_dir, state) {
137 result.with_context(|| {
138 format!(
139 "Failed to process impl block (`{:?}` for `{}`) in file `{}`",
140 trait_name.to_token_stream(),
141 type_name.to_token_stream(),
142 source.display()
143 )
144 })?;
145 } else if let Some(result) = process_execution_impl(item_impl, out_dir, state) {
146 result.with_context(|| {
147 format!(
148 "Failed to process impl block (`{:?}` for `{}`) in file `{}`",
149 trait_name.to_token_stream(),
150 type_name.to_token_stream(),
151 source.display()
152 )
153 })?;
154 continue;
155 }
156 }
157 _ => {
158 }
160 }
161 }
162
163 Ok(())
164}