1pub mod config;
5pub(crate) mod filters;
7
8pub mod c;
10mod cpp;
11mod dart;
12mod demo_gen;
13mod js;
14mod kotlin;
15mod nanobind;
16
17use colored::*;
18use config::toml_value_from_str;
19use config::{find_top_level_attr, Config};
20use core::mem;
21use core::panic;
22use diplomat_core::hir;
23use std::borrow::Cow;
24use std::cell::RefCell;
25use std::collections::HashMap;
26use std::fmt;
27use std::path::Path;
28
29pub use hir::DocsUrlGenerator;
30
31pub fn gen(
32 entry: &Path,
33 target_language: &str,
34 out_folder: &Path,
35 docs_url_gen: &DocsUrlGenerator,
36 mut config: Config,
37 silent: bool,
38) -> std::io::Result<()> {
39 if !entry.exists() {
40 eprintln!(
41 "{}{}\n{}",
42 "Error: ".red().bold(),
43 if entry.file_name().map(|e| e == "lib.rs").unwrap_or_default() {
44 "Could not find the lib.rs file to process."
45 } else {
46 "The entry file does not exist."
47 },
48 format!("{}", std::env::current_dir().unwrap().join(entry).display()).red()
49 );
50 std::process::exit(1);
51 }
52
53 let target_language = target_language.strip_suffix('2').unwrap_or(target_language);
55 let mut attr_validator = hir::BasicAttributeValidator::new(target_language);
56 attr_validator.support = match target_language {
57 "c" => c::attr_support(),
58 "cpp" => cpp::attr_support(),
59 "dart" => dart::attr_support(),
60 "js" => js::attr_support(),
61 "demo_gen" => {
62 attr_validator.other_backend_names = vec!["js".to_string()];
64 demo_gen::attr_support()
65 }
66 "kotlin" => kotlin::attr_support(),
67 "py-nanobind" | "nanobind" => nanobind::attr_support(),
68 o => panic!("Unknown target: {}", o),
69 };
70
71 let module = syn_inline_mod::parse_and_inline_modules(entry);
72
73 let cfg = find_top_level_attr(module.items.clone());
76 for attr in cfg {
77 for kvp in attr.key_value_pairs {
78 config.set(&kvp.key, toml_value_from_str(&kvp.value));
79 }
80 }
81
82 let config = config.get_overridden(target_language);
83
84 let lowering_config = config.shared_config.lowering_config();
85
86 let tcx =
87 hir::TypeContext::from_syn(&module, lowering_config, attr_validator).unwrap_or_else(|e| {
88 for (ctx, err) in e {
89 eprintln!("Lowering error in {ctx}: {err}");
90 }
91 std::process::exit(1);
92 });
93
94 let (files, errors) = match target_language {
95 "c" => c::run(&tcx, &config, docs_url_gen),
96 "cpp" => cpp::run(&tcx, &config, docs_url_gen),
97 "dart" => dart::run(&tcx, docs_url_gen),
98 "js" => js::run(&tcx, config, docs_url_gen),
99 "py-nanobind" | "nanobind" => nanobind::run(&tcx, config, docs_url_gen),
100 "demo_gen" => {
101 if !(config.demo_gen_config.module_name.is_some()
103 || config.demo_gen_config.relative_js_path.is_some())
104 {
105 gen(
106 entry,
107 "js",
108 &out_folder.join("js"),
109 docs_url_gen,
110 config.clone(),
111 silent,
112 )?;
113 }
114 demo_gen::run(entry, &tcx, docs_url_gen, config.clone())
115 }
116 "kotlin" => kotlin::run(&tcx, config.clone(), docs_url_gen),
117 o => panic!("Unknown target: {}", o),
118 };
119
120 let errors = errors.take_all();
121 if !errors.is_empty() {
122 eprintln!("Found errors whilst generating {target_language}:");
123 for error in errors {
124 eprintln!("\t{}: {}", error.0, error.1);
125 }
126 eprintln!("Not generating files due to errors");
127 std::process::exit(1);
129 }
130
131 if !silent {
132 println!(
133 "{}",
134 format!("Generating {target_language} bindings:")
135 .green()
136 .bold()
137 );
138 }
139 for (subpath, text) in files.take_files() {
140 let out_path = out_folder.join(subpath);
141 if !silent {
142 println!("{}", format!(" {}", out_path.display()).dimmed());
143 }
144 std::fs::create_dir_all(out_path.parent().unwrap()).unwrap();
145 std::fs::write(&out_path, text)?;
146 }
147
148 Ok(())
149}
150
151#[derive(Default, Debug)]
153pub struct FileMap {
154 files: RefCell<HashMap<String, String>>,
158}
159
160impl FileMap {
161 pub fn take_files(self) -> HashMap<String, String> {
162 mem::take(&mut *self.files.borrow_mut())
163 }
164
165 pub fn add_file(&self, name: String, contents: String) {
166 if self.files.borrow().get(&name).is_some() {
167 panic!("File map already contains {}", name)
168 }
169 self.files.borrow_mut().insert(name, contents);
170 }
171}
172
173#[derive(Default)]
181pub struct ErrorStore<'tcx, E> {
182 context: RefCell<ErrorContext<'tcx>>,
184 errors: RefCell<Vec<(ErrorContext<'tcx>, E)>>,
185}
186
187impl<'tcx, E> ErrorStore<'tcx, E> {
188 pub fn set_context_ty<'a>(&'a self, ty: Cow<'tcx, str>) -> ErrorContextGuard<'a, 'tcx, E> {
191 let new = ErrorContext { ty, method: None };
192 let old = mem::replace(&mut *self.context.borrow_mut(), new);
193 ErrorContextGuard(self, old)
194 }
195
196 pub fn set_context_method<'a>(
199 &'a self,
200 method: Cow<'tcx, str>,
201 ) -> ErrorContextGuard<'a, 'tcx, E> {
202 let new = ErrorContext {
203 ty: self.context.borrow().ty.clone(),
204 method: Some(method),
205 };
206
207 let old = mem::replace(&mut *self.context.borrow_mut(), new);
208 ErrorContextGuard(self, old)
209 }
210
211 pub fn push_error(&self, error: E) {
212 self.errors
213 .borrow_mut()
214 .push((self.context.borrow().clone(), error));
215 }
216
217 pub fn take_all(&self) -> Vec<(impl fmt::Display + 'tcx, E)> {
218 mem::take(&mut self.errors.borrow_mut())
219 }
220}
221
222#[derive(Default, Clone)]
224struct ErrorContext<'tcx> {
225 ty: Cow<'tcx, str>,
226 method: Option<Cow<'tcx, str>>,
227}
228
229impl fmt::Display for ErrorContext<'_> {
230 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
231 let ty = &self.ty;
232 if let Some(ref method) = self.method {
233 write!(f, "{ty}::{method}")
234 } else {
235 ty.fmt(f)
236 }
237 }
238}
239
240#[must_use]
242pub struct ErrorContextGuard<'a, 'tcx, E>(&'a ErrorStore<'tcx, E>, ErrorContext<'tcx>);
243
244impl<E> Drop for ErrorContextGuard<'_, '_, E> {
245 fn drop(&mut self) {
246 let _ = mem::replace(&mut *self.0.context.borrow_mut(), mem::take(&mut self.1));
247 }
248}