1#![doc = include_str!(concat!("../", env!("CARGO_PKG_README")))]
19
20use rustdoc_types::Attribute;
21use serde::{Deserialize, Serialize};
22use serde_generate::SourceInstaller;
23use std::borrow::Cow;
24use std::cell::RefCell;
25use std::collections::BTreeMap;
26use std::collections::HashMap;
27use std::ffi::OsStr;
28use std::fmt::Write as _;
29use std::fs;
30use std::fs::File;
31use std::io::BufWriter;
32use std::io::Write as _;
33use std::path::PathBuf;
34use std::path::{Component, Path};
35use std::process::{Output, Stdio};
36
37pub use self::traits::SafeTypeMapping;
38pub use bincode;
39pub use buffi_macro::*;
40
41mod traits;
42
43mod buffi_annotation_attributes;
44
45const FUNCTION_PREFIX: &str = "buffi";
46const MARKER_TRAIT_STR: &str = "#[<cfg_trace>(not(generated_extern_impl))]";
47
48type FieldList = Vec<(
49 String,
50 Vec<(
51 serde_reflection::Format,
52 Option<serde_reflection::ContainerFormat>,
53 )>,
54)>;
55
56#[derive(Debug, serde::Deserialize)]
57struct WorkspaceMetadata {
58 target_directory: String,
59}
60
61#[derive(Serialize, Deserialize, Debug)]
63pub struct Config {
64 pub namespace: String,
66 pub api_lib_name: String,
68 pub parent_crate: String,
70 pub rustdoc_crates: Vec<String>,
72 pub file_prefix: Option<String>,
74 pub copyright_header: Option<String>,
76 pub generated_by_header: Option<String>,
78 pub crate_feature_flags: Option<Vec<String>>,
80 pub rustdoc_flags: Option<Vec<String>>,
82}
83
84impl Config {
85 pub fn new(
87 namespace: String,
88 api_lib_name: String,
89 parent_crate: String,
90 rustdoc_crates: Vec<String>,
91 ) -> Self {
92 Self {
93 namespace,
94 api_lib_name,
95 parent_crate,
96 rustdoc_crates,
97 file_prefix: None,
98 copyright_header: None,
99 generated_by_header: None,
100 crate_feature_flags: None,
101 rustdoc_flags: None,
102 }
103 }
104
105 pub fn extend_rustdoc_flags(&mut self, flags: Vec<String>) {
107 if let Some(rustdoc_flags) = self.rustdoc_flags.as_mut() {
108 rustdoc_flags.extend(flags);
109 } else {
110 self.rustdoc_flags = Some(flags);
111 }
112 }
113}
114
115struct ItemResolver {
116 base_path: String,
117 doc_types: rustdoc_types::Crate,
118 other_crates: RefCell<HashMap<String, rustdoc_types::Crate>>,
119}
120
121impl ItemResolver {
122 fn new(json_path: String, api_lib_name: &str) -> Self {
123 let full_path = json_path.clone() + api_lib_name + ".json";
124 let content = std::fs::read_to_string(full_path.clone())
125 .unwrap_or_else(|_| panic!("Failed to read file: {full_path}"));
126 let doc_types = serde_json::from_str(&content).unwrap();
127 Self {
128 base_path: json_path,
129 doc_types,
130 other_crates: RefCell::new(HashMap::new()),
131 }
132 }
133
134 fn resolve_by_path(
136 &self,
137 path: &str,
138 parent_crate: &str,
139 requested_item: rustdoc_types::ItemKind,
140 ) -> (String, rustdoc_types::Path) {
141 let mut parts = path.split("::").map(|c| c.to_owned()).collect::<Vec<_>>();
142 let id = {
143 let mut other_crates = self.other_crates.borrow_mut();
144 let map = if parts[0] == parent_crate || parts[0] == "crate" || parts.len() == 1 {
147 &self.doc_types
148 } else {
149 other_crates.entry(parts[0].to_owned()).or_insert_with(|| {
150 self.load_extern_crate_doc(&parts[0], &format!("(needed for {path:?})"))
151 })
152 };
153 if parts[0] == "crate" {
154 if let Some(guess) = map.paths.iter().find_map(|(_, i)| {
155 (i.path[0] == parent_crate).then_some(parent_crate.to_owned())
156 }) {
157 parts[0] = guess;
158 } else if let Some(guess) = map
159 .paths
160 .iter()
161 .find_map(|(_, i)| (i.crate_id == 0).then_some(i.path[0].clone()))
162 {
163 parts[0] = guess;
164 } else {
165 panic!("Cannot resolve parent crate");
166 }
167 }
168
169 let (id, summary) = map
170 .paths
171 .iter()
172 .find(|(_, i)| i.kind == requested_item && i.path.ends_with(&parts))
173 .unwrap_or_else(|| panic!("Failed to lookup type {parts:?}"));
174 if summary.kind == requested_item {
175 *id
176 } else {
177 panic!(
178 "Incompatible type: Expected {requested_item:?}, Got {:?}",
179 summary.kind
180 );
181 }
182 };
183 let parent_crate = parts[0].to_owned();
184 (
185 parent_crate,
186 rustdoc_types::Path {
187 path: parts[parts.len() - 1].to_owned(),
188 id,
189 args: None,
190 },
191 )
192 }
193
194 fn resolve_index(
195 &self,
196 t: Option<&rustdoc_types::Path>,
197 id: &rustdoc_types::Id,
198 parent_crate: &str,
199 ) -> rustdoc_types::Item {
200 let mut other_crates = self.other_crates.borrow_mut();
201
202 let candidates = std::iter::once(&self.doc_types)
203 .chain(other_crates.values())
204 .filter_map(|c| c.index.get(id))
205 .filter(|i| extract_crate_from_span(i) == Some(parent_crate.into()))
206 .collect::<Vec<_>>();
207 match &candidates as &[&rustdoc_types::Item] {
208 [i] => return rustdoc_types::Item::clone(i),
209 [] => {
210 }
212 items => {
213 let matches_parent_crate = items
218 .iter()
219 .position(|i| extract_crate_from_span(i) == Some(parent_crate.into()));
220 match matches_parent_crate {
221 Some(t) => {
222 return rustdoc_types::Item::clone(items[t]);
223 }
224 _ => {
225 panic!("Cannot decide what's the correct candidate")
226 }
227 }
228 }
229 }
230
231 let mut matched_ids = Vec::with_capacity(1);
233 if let Some(item) = self.doc_types.paths.get(id) {
234 let input_name = t.and_then(|t| t.path.split("::").last());
235 if input_name == item.path.last().map(|x| x.as_str()) {
236 matched_ids.push(item.clone());
237 }
238 }
239 for c in other_crates.values() {
240 if let Some(s) = c.paths.get(id) {
241 let input_name = t.and_then(|t| t.path.split("::").last());
242 if input_name == s.path.last().map(|x| x.as_str()) {
243 matched_ids.push(s.clone());
244 }
245 }
246 }
247
248 for crate_id in matched_ids {
250 let crate_name = crate_id.path.first().unwrap().clone();
254 let other_index = other_crates.entry(crate_name.clone()).or_insert_with(|| {
255 self.load_extern_crate_doc(&crate_name, &format!("(needed for {t:?})"))
256 });
257
258 let name = crate_id.path.last().unwrap();
263 let item = other_index.index.values().find(|i| {
264 i.name.as_ref() == Some(name)
265 && matches!(
266 (&i.inner, &crate_id.kind),
267 (
268 rustdoc_types::ItemEnum::Struct(_),
269 rustdoc_types::ItemKind::Struct
270 ) | (
271 rustdoc_types::ItemEnum::Enum(_),
272 rustdoc_types::ItemKind::Enum
273 ) | (
274 rustdoc_types::ItemEnum::Function(_),
275 rustdoc_types::ItemKind::Function
276 )
277 )
278 });
279 if let Some(item) = item {
280 return item.clone();
281 }
282 }
283 panic!("Unknown id: {id:?}, crate: {parent_crate:?} (full type:{t:?})");
284 }
285
286 fn load_extern_crate_doc(
287 &self,
288 crate_name: &str,
289 additional_message: &str,
290 ) -> rustdoc_types::Crate {
291 let content = std::fs::read_to_string(self.base_path.clone() + crate_name + ".json")
292 .unwrap_or_else(|_| {
293 panic!(
294 "Failed to find docs for `{}` {}",
295 &crate_name, additional_message
296 );
297 });
298 serde_json::from_str(&content).unwrap()
299 }
300}
301
302#[derive(Debug)]
303enum TypeCache {
304 NeedToPopulate,
305 Cached(
306 Vec<(
307 serde_reflection::Format,
308 Option<serde_reflection::ContainerFormat>,
309 )>,
310 ),
311}
312
313pub fn generate_bindings(out_dir: &Path, config: Config) {
314 if !out_dir.exists() {
315 panic!("Out directory does not exist");
316 }
317
318 let (target_directory, handle) = generate_docs(
319 &config.api_lib_name,
320 &config.rustdoc_crates,
321 config.crate_feature_flags.as_ref().unwrap_or(&Vec::new()),
322 config.rustdoc_flags.as_ref().unwrap_or(&Vec::new()),
323 );
324
325 let mut failed = false;
326 if let Ok(handle) = handle {
327 if handle.status.success() {
328 let resolver = ItemResolver::new(target_directory + "/doc/", &config.api_lib_name);
329 let mut type_map = HashMap::new();
330 let out_dir = out_dir.display().to_string();
331 generate_type_definitions(&resolver, &out_dir, &mut type_map, &config);
332 generate_function_definitions(
333 resolver,
334 &out_dir,
335 &mut type_map,
336 FUNCTION_PREFIX,
337 &config,
338 );
339 } else {
340 failed = true;
341 }
342 } else {
343 failed = true;
344 }
345
346 if !failed {
347 println!("Finished, wrote bindings to `{}`", out_dir.display());
348 }
349
350 if failed {
351 eprintln!("Failed to generate bindings");
352 std::process::exit(1);
353 }
354}
355
356pub fn generate_docs(
357 api_lib_name: &String,
358 rustdoc_crates: &[String],
359 crate_flags: &[String],
360 rustdoc_flags: &[String],
361) -> (String, Result<Output, std::io::Error>) {
362 print!("Gather workspace metadata:");
363 std::io::stdout().flush().expect("Flushing does not fail");
364 let metadata = std::process::Command::new("cargo")
365 .arg("metadata")
366 .arg("--format-version=1")
367 .stderr(Stdio::inherit())
368 .output()
369 .expect("Failed to get workspace metadata");
370 println!(" OK");
371
372 let WorkspaceMetadata { target_directory } = serde_json::from_slice(&metadata.stdout).unwrap();
373 let doc_directory = target_directory.to_owned() + "/doc";
375 if matches!(fs::exists(&doc_directory), Ok(true)) {
376 for entry in fs::read_dir(doc_directory).unwrap() {
377 let file_path = entry.unwrap().path();
378 if file_path.extension().and_then(|s| s.to_str()) == Some("json") {
379 fs::remove_file(file_path).unwrap();
380 }
381 }
382 }
383
384 if rustdoc_crates.is_empty() {
385 eprintln!("Need at least one input crate to create bindings!");
386 std::process::exit(1);
387 }
388
389 let mut args = vec!["--no-deps"];
391 let crate_args: Vec<_> = rustdoc_crates
392 .iter()
393 .flat_map(|crate_name| vec!["-p", crate_name])
394 .collect();
395 let crate_flag_args: Vec<_> = crate_flags
396 .iter()
397 .flat_map(|crate_and_flag| vec!["-F", crate_and_flag])
398 .collect();
399 args.extend(crate_args);
400 args.extend(crate_flag_args);
401 args.extend(rustdoc_flags.iter().map(|s| s as &str));
402
403 let bootstrap_crates = vec![api_lib_name].into_iter().chain(rustdoc_crates).fold(
404 String::new(),
405 |cumulated, crate_name| {
406 let crate_name = crate_name.replace("-", "_");
407 cumulated + &format!(",{crate_name}")
408 },
409 );
410 let bootstrap_crates = &bootstrap_crates[1..bootstrap_crates.len()];
412
413 println!("Compile rustdocs:");
414 let mut rustdoc_command = std::process::Command::new("cargo");
415
416 rustdoc_command
417 .arg("doc")
418 .args(args)
419 .env("RUSTC_BOOTSTRAP", bootstrap_crates)
420 .env("RUSTDOCFLAGS", "-Z unstable-options --output-format json ")
421 .env("CARGO_TARGET_DIR", &target_directory)
422 .stderr(Stdio::inherit())
423 .stdout(Stdio::inherit());
424
425 let handle = rustdoc_command.output();
426 (target_directory, handle)
427}
428
429fn generate_function_definitions(
430 res: ItemResolver,
431 out_dir: &str,
432 type_map: &mut HashMap<rustdoc_types::Type, TypeCache>,
433 function_prefix: &str,
434 config: &Config,
435) {
436 let namespace = &config.namespace;
437 let file_prefix = config.file_prefix.as_ref().unwrap_or(&config.api_lib_name);
438
439 let out_dir = PathBuf::from(out_dir);
440 let mut extern_c_functions = res
441 .doc_types
442 .index
443 .values()
444 .filter_map(|item| {
445 if let rustdoc_types::ItemEnum::Function(ref func) = item.inner {
446 if matches!(func.header.abi, rustdoc_types::Abi::C { .. }) {
447 let s = generate_extern_c_function_def(item.name.as_deref().unwrap(), func);
448 Some(s)
449 } else {
450 None
451 }
452 } else {
453 None
454 }
455 })
456 .collect::<Vec<_>>();
457 extern_c_functions.sort();
459
460 let mut free_standing_functions = res
461 .doc_types
462 .index
463 .values()
464 .filter(is_free_standing_impl)
465 .collect::<Vec<_>>();
466
467 free_standing_functions.sort_by_key(|f| f.name.as_ref());
468
469 let mut relevant_impls = res
470 .doc_types
471 .index
472 .values()
473 .filter(is_relevant_impl)
474 .flat_map(|item| {
475 if let rustdoc_types::ItemEnum::Impl(ref impl_) = item.inner {
476 impl_
477 .items
478 .iter()
479 .map(|id| res.resolve_index(None, id, &config.parent_crate))
480 .filter(|item| matches!(item.inner, rustdoc_types::ItemEnum::Function(_)))
481 .map(move |i| (&impl_.for_, i))
482 } else {
483 unreachable!()
484 }
485 })
486 .fold(HashMap::<_, Vec<_>>::new(), |mut acc, (t, i)| {
487 acc.entry(t).or_default().push(i);
488 acc
489 })
490 .into_iter()
491 .map(|(n, mut items)| {
492 items.sort_by_key(|i| i.name.clone());
493 (n, items)
494 })
495 .collect::<Vec<_>>();
496
497 relevant_impls.sort_by_key(|(t, _)| {
499 if let rustdoc_types::Type::ResolvedPath(p) = t {
500 get_name_without_path(&p.path)
501 } else {
502 unreachable!()
503 }
504 });
505 let extern_c_header = out_dir.join(format!("{file_prefix}_api_functions.hpp"));
506 let mut extern_c_header = BufWriter::new(File::create(extern_c_header).unwrap());
507 write_function_header(&mut extern_c_header, config);
508 writeln!(extern_c_header, "#include <cstdint>").unwrap();
509 writeln!(extern_c_header).unwrap();
510 for (t, _) in relevant_impls.iter() {
511 if let rustdoc_types::Type::ResolvedPath(p) = t {
512 let name = get_name_without_path(&p.path);
513 writeln!(extern_c_header, "struct {name};\n").unwrap();
514 } else {
515 unreachable!()
516 }
517 }
518 for function in extern_c_functions {
519 writeln!(extern_c_header, "{function}").unwrap();
520 }
521 extern_c_header.flush().unwrap();
522
523 for (t, impls) in relevant_impls {
524 if let rustdoc_types::Type::ResolvedPath(p) = t {
525 let name = get_name_without_path(&p.path);
526 let type_header =
527 out_dir.join(format!("{file_prefix}_{}.hpp", name.to_ascii_lowercase()));
528 let mut writer = BufWriter::new(File::create(type_header).unwrap());
529 write_function_header(&mut writer, config);
530 writeln!(writer, "#include \"{file_prefix}_api_functions.hpp\"\n").unwrap();
531 writeln!(writer, "#include \"{namespace}.hpp\"\n").unwrap();
532
533 writeln!(writer).unwrap();
534 writeln!(writer, "namespace {namespace} {{").unwrap();
535 writeln!(writer).unwrap();
536 writeln!(writer, "class {name}Holder {{").unwrap();
537 writeln!(writer, " {name}* inner;").unwrap();
538 writeln!(writer, "public:").unwrap();
539 writeln!(writer, " {name}Holder({name}* ptr) {{").unwrap();
540 writeln!(writer, " this->inner = ptr;").unwrap();
541 writeln!(writer, " }}\n").unwrap();
542 for impl_ in impls {
543 if let rustdoc_types::ItemEnum::Function(ref m) = impl_.inner {
544 generate_function_def(
545 m,
546 &res,
547 &impl_,
548 &mut writer,
549 type_map,
550 function_prefix,
551 config,
552 Some(t),
553 );
554 }
555 }
556 writeln!(writer, "}};\n").unwrap();
557 writeln!(writer, "}} // end of namespace {namespace}").unwrap();
558 writer.flush().unwrap();
559 }
560 }
561
562 let free_standing_function_header =
563 out_dir.join(format!("{file_prefix}_free_standing_functions.hpp"));
564 let mut free_standing_function_header =
565 BufWriter::new(File::create(free_standing_function_header).unwrap());
566
567 write_function_header(&mut free_standing_function_header, config);
568 writeln!(
569 free_standing_function_header,
570 "#include \"{file_prefix}_api_functions.hpp\"\n"
571 )
572 .unwrap();
573 writeln!(
574 free_standing_function_header,
575 "#include \"{namespace}.hpp\"\n"
576 )
577 .unwrap();
578
579 writeln!(free_standing_function_header).unwrap();
580 writeln!(free_standing_function_header, "namespace {namespace} {{").unwrap();
581 writeln!(free_standing_function_header).unwrap();
582
583 for item in &free_standing_functions {
584 if let rustdoc_types::ItemEnum::Function(ref f) = item.inner {
585 generate_function_def(
586 f,
587 &res,
588 item,
589 &mut free_standing_function_header,
590 type_map,
591 function_prefix,
592 config,
593 None,
594 );
595 writeln!(free_standing_function_header).unwrap();
596 }
597 }
598
599 writeln!(
600 free_standing_function_header,
601 "}} // end of namespace {namespace}"
602 )
603 .unwrap();
604 free_standing_function_header.flush().unwrap();
605}
606
607fn write_function_header(out_functions: &mut BufWriter<File>, config: &Config) {
608 if let Some(copyright_header) = &config.copyright_header {
609 writeln!(out_functions, "// {copyright_header}").unwrap();
610 }
611 if let Some(generated_by) = &config.generated_by_header {
612 writeln!(out_functions, "// {generated_by}").unwrap();
613 }
614 if config.copyright_header.is_some() || config.generated_by_header.is_some() {
615 writeln!(out_functions).unwrap();
616 }
617 writeln!(out_functions, "#pragma once\n").unwrap();
618 writeln!(out_functions, "#include <cstddef>").unwrap();
619 writeln!(out_functions, "#include <limits>").unwrap();
620}
621
622#[allow(clippy::too_many_arguments)]
623fn generate_function_def(
624 m: &rustdoc_types::Function,
625 res: &ItemResolver,
626 item: &rustdoc_types::Item,
627 out_functions: &mut BufWriter<File>,
628 type_map: &mut HashMap<rustdoc_types::Type, TypeCache>,
629 prefix: &str,
630 config: &Config,
631 impl_type: Option<&rustdoc_types::Type>,
632) {
633 let output_type = if let Some(ref tpe) = m.sig.output {
634 let tpe = to_serde_reflect_type(
635 tpe,
636 res,
637 &mut None,
638 Vec::new(),
639 &config.parent_crate,
640 &config.namespace,
641 type_map,
642 );
643 to_cpp_type_name(&tpe.last().unwrap().0)
644 } else {
645 unimplemented!()
646 };
647 let inputs = m
648 .sig
649 .inputs
650 .iter()
651 .map(|(name, tpe)| {
652 if name == "self" {
653 let impl_type_path = impl_type
654 .map(|tpe| {
655 let rustdoc_types::Type::ResolvedPath(path) = tpe else {
656 panic!("Impl type must be a resolved path");
657 };
658 path
659 })
660 .expect("we have an impl type for impl functions");
661 return (name, get_name_without_path(&impl_type_path.path).to_owned());
662 }
663 let reflect_type = to_serde_reflect_type(
664 tpe,
665 res,
666 &mut None,
667 Vec::new(),
668 &config.parent_crate,
669 &config.namespace,
670 type_map,
671 );
672 let type_string = reflect_type
673 .last()
674 .map(|(f, _)| to_cpp_type_name(f))
675 .unwrap_or_else(|| panic!("Unknown type: {tpe:?}"));
676 (name, type_string)
677 })
678 .collect::<Vec<_>>();
679 let return_output_type = match m.sig.output {
680 Some(rustdoc_types::Type::ResolvedPath(ref p))
681 if get_name_without_path(&p.path) == "Result" =>
682 {
683 if let Some(rustdoc_types::GenericArgs::AngleBracketed { args, .. }) = p.args.as_deref()
684 {
685 if let rustdoc_types::GenericArg::Type(tpe) = &args[0] {
686 let tpe = to_serde_reflect_type(
687 tpe,
688 res,
689 &mut None,
690 Vec::new(),
691 &config.parent_crate,
692 &config.namespace,
693 type_map,
694 );
695 Cow::Owned(to_cpp_type_name(&tpe.last().unwrap().0))
696 } else {
697 unreachable!()
698 }
699 } else {
700 unreachable!()
701 }
702 }
703 Some(rustdoc_types::Type::ResolvedPath(ref p))
704 if get_name_without_path(&p.path) == "String" =>
705 {
706 Cow::Owned(to_cpp_type_name(&serde_reflection::Format::Str))
707 }
708 _ => Cow::Borrowed(&output_type as &str),
709 };
710 if let Some(ref docs) = item.docs {
711 for line in docs.lines() {
712 writeln!(out_functions, " // {line}").unwrap()
713 }
714 }
715 write!(
716 out_functions,
717 " inline {return_output_type} {}(",
718 item.name.as_ref().unwrap()
719 )
720 .unwrap();
721 for (idx, (name, tpe)) in inputs.iter().filter(|(n, _)| *n != "self").enumerate() {
722 if idx != 0 {
723 write!(out_functions, ", ").unwrap();
724 }
725 write!(out_functions, "const {tpe}& {name}").unwrap();
726 }
727 writeln!(out_functions, ") {{").unwrap();
728 for (name, tpe) in &inputs {
729 if *name == "self" {
730 continue;
731 }
732 writeln!(
733 out_functions,
734 " auto serializer_{name} = serde::BincodeSerializer();"
735 )
736 .unwrap();
737 writeln!(
738 out_functions,
739 " serde::Serializable<{tpe}>::serialize({name}, serializer_{name});"
740 )
741 .unwrap();
742 writeln!(
743 out_functions,
744 " std::vector<uint8_t> {name}_serialized = std::move(serializer_{name}).bytes();"
745 )
746 .unwrap();
747 }
748 writeln!(out_functions, " uint8_t* out_ptr = nullptr;").unwrap();
749 writeln!(out_functions).unwrap();
750 write!(
751 out_functions,
752 " size_t res_size = {}_{}(",
753 prefix,
754 item.name.as_deref().unwrap(),
755 )
756 .unwrap();
757 for (name, _) in inputs.iter() {
758 if *name == "self" {
759 write!(out_functions, "this->inner, ").unwrap();
760 } else {
761 write!(
762 out_functions,
763 "{name}_serialized.data(), {name}_serialized.size(), "
764 )
765 .unwrap();
766 }
767 }
768 writeln!(out_functions, "&out_ptr);").unwrap();
769 writeln!(out_functions).unwrap();
770 writeln!(
771 out_functions,
772 " std::vector<uint8_t> serialized_result(out_ptr, out_ptr + res_size);"
773 )
774 .unwrap();
775 writeln!(
776 out_functions,
777 " {output_type} out = {output_type}::bincodeDeserialize(serialized_result);"
778 )
779 .unwrap();
780 writeln!(
781 out_functions,
782 " {prefix}_free_byte_buffer(out_ptr, res_size);"
783 )
784 .unwrap();
785 writeln!(out_functions).unwrap();
786 if matches!(m.sig.output, Some(rustdoc_types::Type::ResolvedPath(ref p)) if get_name_without_path(&p.path) == "Result")
787 {
788 writeln!(
789 out_functions,
790 " if (out.value.index() == 0) {{ // Ok"
791 )
792 .unwrap();
793 if return_output_type == "void" {
794 writeln!(out_functions, " return;").unwrap();
795 } else {
796 writeln!(
797 out_functions,
798 " auto ok = std::get<0>(out.value);"
799 )
800 .unwrap();
801 writeln!(out_functions, " return std::get<0>(ok.value);").unwrap();
802 }
803 writeln!(out_functions, " }} else {{ // Err").unwrap();
804 writeln!(
805 out_functions,
806 " auto err = std::get<1>(out.value);"
807 )
808 .unwrap();
809 writeln!(
810 out_functions,
811 " auto error = std::get<0>(err.value);"
812 )
813 .unwrap();
814 writeln!(out_functions, " throw error;").unwrap();
815 writeln!(out_functions, " }}").unwrap();
816 } else {
817 writeln!(out_functions, " return out;").unwrap();
818 }
819 writeln!(out_functions, " }}\n").unwrap();
820}
821
822fn generate_type_definitions(
823 res: &ItemResolver,
824 out_types: &str,
825 type_map: &mut HashMap<rustdoc_types::Type, TypeCache>,
826 config: &Config,
827) {
828 let comments = serde_generate::DocComments::new();
829 let mut comments = Some(comments);
830 let mut types_for_impls = res
831 .doc_types
832 .index
833 .values()
834 .filter(|i| is_relevant_impl(i) || is_free_standing_impl(i))
835 .flat_map(|item| {
836 if let rustdoc_types::ItemEnum::Impl(ref impl_) = item.inner {
837 impl_
838 .items
839 .iter()
840 .map(|id| res.resolve_index(None, id, &config.parent_crate))
841 .filter(|item| matches!(item.inner, rustdoc_types::ItemEnum::Function(_)))
842 .collect()
843 } else if let rustdoc_types::ItemEnum::Function(ref _f) = item.inner {
844 vec![item.clone()]
845 } else {
846 unreachable!()
847 }
848 })
849 .flat_map(|m| {
850 if let rustdoc_types::ItemEnum::Function(ref m) = m.inner {
851 m.sig
852 .inputs
853 .iter()
854 .map(|(_, t)| t.clone())
855 .chain(
856 m.sig
857 .output
858 .as_ref()
859 .map(|e| vec![e.clone()])
860 .unwrap_or_default(),
861 )
862 .collect::<Vec<_>>()
863 } else {
864 unreachable!()
865 }
866 })
867 .collect::<Vec<_>>();
868 types_for_impls.dedup();
869 let registry = types_for_impls
870 .into_iter()
871 .map(|t| {
872 to_serde_reflect_type(
873 &t,
874 res,
875 &mut comments,
876 Vec::new(),
877 &config.parent_crate,
878 &config.namespace,
879 type_map,
880 )
881 })
882 .flat_map(|types| {
883 types.into_iter().filter_map(|(format, container)| {
884 let container = container?;
885 if let serde_reflection::Format::TypeName(n) = format {
886 Some((n, container))
887 } else {
888 None
889 }
890 })
891 })
892 .collect::<serde_reflection::Registry>();
893
894 let config = serde_generate::CodeGeneratorConfig::new(config.namespace.to_owned())
895 .with_comments(comments.unwrap())
896 .with_encodings([serde_generate::Encoding::Bincode]);
897 let installer = serde_generate::cpp::Installer::new(PathBuf::from(out_types));
898 installer.install_module(&config, ®istry).unwrap();
899 installer.install_serde_runtime().unwrap();
900 installer.install_bincode_runtime().unwrap();
901}
902
903fn to_cpp_type_name(f: &serde_reflection::Format) -> String {
904 match f {
905 serde_reflection::Format::Variable(_) => unimplemented!(),
906 serde_reflection::Format::TypeName(_) => to_type_name(f).into_owned(),
907 serde_reflection::Format::Unit => unimplemented!(),
908 serde_reflection::Format::Bool => String::from("bool"),
909 serde_reflection::Format::I8 => String::from("int8_t"),
910 serde_reflection::Format::I16 => String::from("int16_t"),
911 serde_reflection::Format::I32 => String::from("int32_t"),
912 serde_reflection::Format::I64 => String::from("int64_t"),
913 serde_reflection::Format::I128 => unimplemented!(),
914 serde_reflection::Format::U8 => String::from("uint8_t"),
915 serde_reflection::Format::U16 => String::from("uint16_t"),
916 serde_reflection::Format::U32 => String::from("uint32_t"),
917 serde_reflection::Format::U64 => String::from("uint64_t"),
918 serde_reflection::Format::U128 => unimplemented!(),
919 serde_reflection::Format::F32 => String::from("float"),
920 serde_reflection::Format::F64 => String::from("double"),
921 serde_reflection::Format::Char => unimplemented!(),
922 serde_reflection::Format::Str => String::from("std::string"),
923 serde_reflection::Format::Bytes => unimplemented!(),
924 serde_reflection::Format::Option(t) => {
925 format!("std::optional<{}>", to_cpp_type_name(t))
926 }
927 serde_reflection::Format::Seq(p) => {
928 format!("std::vector<{}>", to_cpp_type_name(p))
929 }
930 serde_reflection::Format::Map { .. } => unimplemented!(),
931 serde_reflection::Format::Tuple(d) if d.is_empty() => String::from("void"),
932 serde_reflection::Format::Tuple(_) => unimplemented!(),
933 serde_reflection::Format::TupleArray { .. } => unimplemented!(),
934 }
935}
936
937fn to_type_name(f: &serde_reflection::Format) -> Cow<'_, str> {
938 match f {
939 serde_reflection::Format::Variable(_) => unimplemented!(),
940 serde_reflection::Format::TypeName(n) => Cow::Borrowed(n),
941 serde_reflection::Format::Unit => unimplemented!(),
942 serde_reflection::Format::Bool => Cow::Borrowed("bool"),
943 serde_reflection::Format::I8 => Cow::Borrowed("i8"),
944 serde_reflection::Format::I16 => Cow::Borrowed("i16"),
945 serde_reflection::Format::I32 => Cow::Borrowed("i32"),
946 serde_reflection::Format::I64 => Cow::Borrowed("i64"),
947 serde_reflection::Format::I128 => unimplemented!(),
948 serde_reflection::Format::U8 => Cow::Borrowed("u8"),
949 serde_reflection::Format::U16 => Cow::Borrowed("u16"),
950 serde_reflection::Format::U32 => Cow::Borrowed("u32"),
951 serde_reflection::Format::U64 => Cow::Borrowed("u64"),
952 serde_reflection::Format::U128 => unimplemented!(),
953 serde_reflection::Format::F32 => Cow::Borrowed("f32"),
954 serde_reflection::Format::F64 => Cow::Borrowed("f64"),
955 serde_reflection::Format::Char => unimplemented!(),
956 serde_reflection::Format::Str => Cow::Borrowed("String"),
957 serde_reflection::Format::Bytes => unimplemented!(),
958 serde_reflection::Format::Option(t) => Cow::Owned(format!("Option_{}", to_type_name(t))),
959 serde_reflection::Format::Seq(t) => Cow::Owned(format!("Vec_{}", to_type_name(t))),
960 serde_reflection::Format::Map { .. } => unimplemented!(),
961 serde_reflection::Format::Tuple(d) if d.is_empty() => Cow::Borrowed("void"),
962 serde_reflection::Format::Tuple(d) => {
963 dbg!(d);
964 unimplemented!()
965 }
966 serde_reflection::Format::TupleArray { .. } => unimplemented!(),
967 }
968}
969
970fn to_serde_reflect_type(
971 t: &rustdoc_types::Type,
972 crate_map: &ItemResolver,
973 comment_map: &mut Option<serde_generate::DocComments>,
974 parent_args: Vec<rustdoc_types::GenericArg>,
975 parent_crate: &str,
976 namespace: &str,
977 type_map: &mut HashMap<rustdoc_types::Type, TypeCache>,
978) -> Vec<(
979 serde_reflection::Format,
980 Option<serde_reflection::ContainerFormat>,
981)> {
982 use serde_reflection::{ContainerFormat, Format};
983
984 fn reflect_primitive(p: &rustdoc_types::Type) -> Vec<(Format, Option<ContainerFormat>)> {
986 let rustdoc_types::Type::Primitive(p) = p else {
987 unreachable!("Primitive!")
988 };
989 match p.as_ref() {
990 "i64" => {
991 vec![(Format::I64, None)]
992 }
993 "i32" => {
994 vec![(Format::I32, None)]
995 }
996 "i16" => {
997 vec![(Format::I16, None)]
998 }
999 "i8" => {
1000 vec![(Format::I8, None)]
1001 }
1002 "bool" => {
1003 vec![(Format::Bool, None)]
1004 }
1005 "f64" => {
1006 vec![(Format::F64, None)]
1007 }
1008 "f32" => {
1009 vec![(Format::F32, None)]
1010 }
1011 "u8" => {
1012 vec![(Format::U8, None)]
1013 }
1014 "u16" => {
1015 vec![(Format::U16, None)]
1016 }
1017 "u32" => {
1018 vec![(Format::U32, None)]
1019 }
1020 "u64" => {
1021 vec![(Format::U64, None)]
1022 }
1023 "usize" if size_of::<usize>() == 8 => {
1024 vec![(Format::U64, None)]
1026 }
1027 "usize" if size_of::<usize>() == 4 => {
1028 vec![(Format::U32, None)]
1030 }
1031 "usize" => {
1032 panic!("Invalid size of usize.");
1033 }
1034 _ => {
1035 dbg!(p);
1036 unimplemented!()
1037 }
1038 }
1039 }
1040
1041 let recursive_type = match type_map.get(t) {
1042 Some(TypeCache::Cached(t)) => return t.clone(),
1043 Some(TypeCache::NeedToPopulate) => true,
1044 None => {
1045 type_map.insert(t.clone(), TypeCache::NeedToPopulate);
1046 false
1047 }
1048 };
1049
1050 let r = match t {
1051 rustdoc_types::Type::ResolvedPath(p) if get_name_without_path(&p.path) == "Result" => {
1052 let mut out = Vec::new();
1053 let (ok, error) = if let Some(rustdoc_types::GenericArgs::AngleBracketed {
1054 args, ..
1055 }) = p.args.as_deref()
1056 {
1057 let ok = &args[0];
1058 let ok = if let rustdoc_types::GenericArg::Type(tpe) = ok {
1059 to_serde_reflect_type(
1060 tpe,
1061 crate_map,
1062 comment_map,
1063 Vec::new(),
1064 parent_crate,
1065 namespace,
1066 type_map,
1067 )
1068 } else {
1069 unreachable!()
1070 };
1071 let err = if let Some((id, _)) =
1072 crate_map.doc_types.index.iter().find(|(_, item)| {
1073 item.name.as_deref().map(get_name_without_path) == Some("SerializableError")
1074 }) {
1075 let t = rustdoc_types::Type::ResolvedPath(rustdoc_types::Path {
1076 path: "SerializableError".into(),
1077 id: *id,
1078 args: None,
1079 });
1080 to_serde_reflect_type(
1081 &t,
1082 crate_map,
1083 comment_map,
1084 Vec::new(),
1085 parent_crate,
1086 namespace,
1087 type_map,
1088 )
1089 } else {
1090 unreachable!(
1091 "Could not find docs for `SerializableError`! Maybe the `errors` module or the type itself is still private?"
1092 )
1093 };
1094 (ok, err)
1095 } else {
1096 unreachable!()
1097 };
1098 let mut result_enum = BTreeMap::new();
1099 result_enum.insert(
1100 0,
1101 serde_reflection::Named {
1102 name: "Ok".into(),
1103 value: serde_reflection::VariantFormat::Tuple(vec![
1104 ok.last().unwrap().0.clone(),
1105 ]),
1106 },
1107 );
1108 result_enum.insert(
1109 1,
1110 serde_reflection::Named {
1111 name: "Err".into(),
1112 value: serde_reflection::VariantFormat::Tuple(vec![
1113 error.last().unwrap().0.clone(),
1114 ]),
1115 },
1116 );
1117 let ok_name = to_type_name(&ok.last().unwrap().0);
1118 let err_name = to_type_name(&error.last().unwrap().0);
1119 let name = format!("Result_{ok_name}_{err_name}");
1120 out.extend(ok);
1121 out.extend(error);
1122 out.push((
1123 Format::TypeName(name),
1124 Some(ContainerFormat::Enum(result_enum)),
1125 ));
1126
1127 out
1128 }
1129 rustdoc_types::Type::ResolvedPath(p) if get_name_without_path(&p.path) == "String" => {
1130 vec![(Format::Str, None)]
1131 }
1132 rustdoc_types::Type::ResolvedPath(p) if get_name_without_path(&p.path) == "Vec" => {
1133 if let Some(rustdoc_types::GenericArgs::AngleBracketed { args, .. }) = p.args.as_deref()
1134 {
1135 if let rustdoc_types::GenericArg::Type(tpe) = &args[0] {
1136 let mut inner = to_serde_reflect_type(
1137 tpe,
1138 crate_map,
1139 comment_map,
1140 Vec::new(),
1141 parent_crate,
1142 namespace,
1143 type_map,
1144 );
1145 let last = inner.last().unwrap().0.clone();
1146 inner.push((Format::Seq(Box::new(last)), None));
1147 inner
1148 } else {
1149 unreachable!()
1150 }
1151 } else {
1152 unreachable!()
1153 }
1154 }
1155 rustdoc_types::Type::ResolvedPath(p) if get_name_without_path(&p.path) == "Option" => {
1156 if let Some(rustdoc_types::GenericArgs::AngleBracketed { args, .. }) = p.args.as_deref()
1157 {
1158 if let rustdoc_types::GenericArg::Type(tpe) = &args[0] {
1159 let mut inner = to_serde_reflect_type(
1160 tpe,
1161 crate_map,
1162 comment_map,
1163 Vec::new(),
1164 parent_crate,
1165 namespace,
1166 type_map,
1167 );
1168 let last = inner.last().unwrap().0.clone();
1169 inner.push((Format::Option(Box::new(last)), None));
1170 inner
1171 } else {
1172 unreachable!()
1173 }
1174 } else {
1175 unreachable!()
1176 }
1177 }
1178 rustdoc_types::Type::ResolvedPath(p) if get_name_without_path(&p.path) == "Box" => {
1179 let t = match p.args.as_deref() {
1180 Some(rustdoc_types::GenericArgs::AngleBracketed { args, .. })
1181 if args.len() == 1 =>
1182 {
1183 if let Some(rustdoc_types::GenericArg::Type(t)) = args.first() {
1184 t
1185 } else {
1186 unreachable!()
1187 }
1188 }
1189 Some(_) | None => unreachable!(),
1190 };
1191 if recursive_type {
1192 let name = match t {
1193 rustdoc_types::Type::ResolvedPath(p) => get_name_without_path(&p.path),
1194 _ => unreachable!(),
1195 };
1196 return vec![(Format::TypeName(name.to_owned()), None)];
1199 } else {
1200 to_serde_reflect_type(
1201 t,
1202 crate_map,
1203 comment_map,
1204 parent_args,
1205 parent_crate,
1206 namespace,
1207 type_map,
1208 )
1209 }
1210 }
1211 rustdoc_types::Type::ResolvedPath(p) => {
1212 let t = crate_map.resolve_index(Some(p), &p.id, parent_crate);
1213 let parent_crate = extract_crate_from_span(&t).expect("parent crate is set");
1214 if let Some(comment_map) = comment_map
1215 && let Some(ref doc) = t.docs
1216 {
1217 comment_map.insert(vec![namespace.to_owned(), p.path.clone()], doc.clone());
1218 }
1219
1220 match t.inner {
1221 rustdoc_types::ItemEnum::Struct(rustdoc_types::Struct {
1222 kind: rustdoc_types::StructKind::Plain { ref fields, .. },
1223 ..
1224 }) => generate_exported_struct(
1225 fields,
1226 crate_map,
1227 comment_map,
1228 p,
1229 parent_args,
1230 &parent_crate,
1231 namespace,
1232 type_map,
1233 recursive_type,
1234 ),
1235 rustdoc_types::ItemEnum::Struct(rustdoc_types::Struct {
1236 kind: rustdoc_types::StructKind::Unit,
1237 ..
1238 }) => generate_exported_struct(
1239 &[],
1240 crate_map,
1241 comment_map,
1242 p,
1243 parent_args,
1244 &parent_crate,
1245 namespace,
1246 type_map,
1247 recursive_type,
1248 ),
1249 rustdoc_types::ItemEnum::Enum(ref e) => generate_exported_enum(
1250 e,
1251 crate_map,
1252 comment_map,
1253 p,
1254 &parent_crate,
1255 namespace,
1256 type_map,
1257 recursive_type,
1258 ),
1259 rustdoc_types::ItemEnum::TypeAlias(ref t) => to_serde_reflect_type(
1260 &t.type_,
1261 crate_map,
1262 comment_map,
1263 parent_args,
1264 &parent_crate,
1265 namespace,
1266 type_map,
1267 ),
1268 t => {
1269 dbg!(t);
1270 unimplemented!();
1271 }
1272 }
1273 }
1274 rustdoc_types::Type::DynTrait(_) => unimplemented!(),
1275 rustdoc_types::Type::Generic(p) => {
1276 if parent_args.len() == 1 {
1277 if let rustdoc_types::GenericArg::Type(t) = &parent_args[0] {
1278 to_serde_reflect_type(
1279 t,
1280 crate_map,
1281 comment_map,
1282 Vec::new(),
1283 parent_crate,
1284 namespace,
1285 type_map,
1286 )
1287 } else {
1288 unimplemented!("Only types are accepted here?")
1289 }
1290 } else {
1291 dbg!(parent_args);
1292 dbg!(p);
1293 unimplemented!("Unsure how to resolve multiple args here??")
1294 }
1295 }
1296 rustdoc_types::Type::Primitive(_) => reflect_primitive(t),
1297 rustdoc_types::Type::FunctionPointer(_) => unimplemented!(),
1298 rustdoc_types::Type::Tuple(tup) => {
1299 let mut out = Vec::new();
1300 let mut fields = Vec::with_capacity(tup.len());
1301 for f in tup {
1302 let r = to_serde_reflect_type(
1303 f,
1304 crate_map,
1305 comment_map,
1306 Vec::new(),
1307 parent_crate,
1308 namespace,
1309 type_map,
1310 );
1311 let f = r.last().map(|a| a.0.clone()).unwrap();
1312 out.extend(r);
1313 fields.push(f);
1314 }
1315 out.push((Format::Tuple(fields), None));
1316 out
1317 }
1318 rustdoc_types::Type::Slice(_) => unimplemented!(),
1319 rustdoc_types::Type::Array { type_, len } => {
1320 let size = len.parse::<usize>().expect("Array len should be a number");
1321 let t = reflect_primitive(type_)[0].0.clone();
1322 vec![(
1323 Format::TupleArray {
1324 content: Box::new(t),
1325 size,
1326 },
1327 None,
1328 )]
1329 }
1330 rustdoc_types::Type::ImplTrait(_) => unimplemented!(),
1331 rustdoc_types::Type::Infer => unimplemented!(),
1332 rustdoc_types::Type::RawPointer { .. } => unimplemented!(),
1333 rustdoc_types::Type::Pat { .. } => unimplemented!(),
1334
1335 rustdoc_types::Type::BorrowedRef { type_, .. } => {
1336 if let rustdoc_types::Type::Generic(s) = &**type_
1337 && s == "Self"
1338 {
1339 return Vec::new();
1340 }
1341
1342 dbg!(t);
1343 unimplemented!()
1344 }
1345 rustdoc_types::Type::QualifiedPath { .. } => unimplemented!(),
1346 };
1347
1348 type_map.insert(t.clone(), TypeCache::Cached(r.clone()));
1349 r
1350}
1351
1352fn extract_crate_from_span(t: &rustdoc_types::Item) -> Option<String> {
1353 let p = &t.span.as_ref()?.filename;
1354 let mut components = p.components().peekable();
1355 let crate_name = match components.next() {
1356 Some(Component::Normal(el)) => {
1357 let mut rev_components = components
1366 .rev()
1367 .skip_while(|el| *el != Component::Normal(OsStr::new("src")))
1370 .skip(1); let Component::Normal(next) = rev_components.next().unwrap_or(Component::Normal(el))
1372 else {
1373 panic!("Could not resolve source path");
1374 };
1375 let s = next.to_str().expect("We expect an UTF-8 Path");
1376 s.replace('-', "_")
1378 }
1379 Some(Component::RootDir | Component::Prefix(_)) => {
1380 loop {
1386 match components.next()? {
1387 Component::Normal(e)
1388 if (e == ".cargo" || e == "cargo")
1389 && matches!(components.peek(), Some(Component::Normal(e)) if *e == "registry") =>
1390 {
1391 break;
1392 }
1393 _ => {}
1394 }
1395 }
1396 components.next();
1398 components.next();
1400 components.next();
1402 let Some(Component::Normal(el)) = components.next() else {
1403 panic!("Expect a normal path element")
1404 };
1405 let s = el.to_str().expect("We expect an UTF-8 Path");
1407 let Some((s, _)) = s.rsplit_once('-') else {
1409 panic!("Expect a versioned crate name")
1410 };
1411 s.replace('-', "_")
1413 }
1414 _ => panic!("We expect a relative or absolute path here"),
1415 };
1416 Some(crate_name)
1417}
1418
1419#[allow(clippy::too_many_arguments)]
1423fn generate_exported_enum(
1424 e: &rustdoc_types::Enum,
1425 crate_map: &ItemResolver,
1426 comment_map: &mut Option<BTreeMap<Vec<String>, String>>,
1427 p: &rustdoc_types::Path,
1428 parent_crate: &str,
1429 namespace: &str,
1430 type_map: &mut HashMap<rustdoc_types::Type, TypeCache>,
1431 recursive_type: bool,
1432) -> Vec<(
1433 serde_reflection::Format,
1434 Option<serde_reflection::ContainerFormat>,
1435)> {
1436 use serde_reflection::{ContainerFormat, Format};
1437
1438 let mut out = Vec::new();
1439 let container_format = if recursive_type {
1440 None
1442 } else {
1443 let mut enum_def = BTreeMap::new();
1444 for (id, variant) in e.variants.iter().enumerate() {
1445 let v = crate_map.resolve_index(None, variant, parent_crate);
1446 if let Some(comment_map) = comment_map
1447 && let Some(ref docs) = v.docs
1448 {
1449 comment_map.insert(
1450 vec![
1451 namespace.to_owned(),
1452 p.path.clone(),
1453 v.name.clone().unwrap(),
1454 ],
1455 docs.clone(),
1456 );
1457 }
1458
1459 match v.inner {
1460 rustdoc_types::ItemEnum::Variant(rustdoc_types::Variant {
1461 kind: rustdoc_types::VariantKind::Plain,
1462 ..
1463 }) => {
1464 enum_def.insert(
1465 id as u32,
1466 serde_reflection::Named {
1467 name: v.name.clone().unwrap(),
1468 value: serde_reflection::VariantFormat::Unit,
1469 },
1470 );
1471 }
1472 rustdoc_types::ItemEnum::Variant(rustdoc_types::Variant {
1473 kind: rustdoc_types::VariantKind::Tuple(ref t),
1474 ..
1475 }) => {
1476 let mut variants = Vec::new();
1477 for id in t {
1478 if let Some(t) = id
1479 .as_ref()
1480 .map(|id| crate_map.resolve_index(None, id, parent_crate))
1481 && let rustdoc_types::ItemEnum::StructField(ref tpe) = t.inner
1482 {
1483 let args = HandleDatatypeFieldArgs {
1484 crate_map,
1485 attrs: &t.attrs,
1486 tpe,
1487 parent_crate,
1488 namespace,
1489 parent_args: Vec::new(),
1490 field_name: v.name.as_deref(),
1491 };
1492 for (field_name, tps) in
1493 handle_datatype_field(args, type_map, comment_map)
1494 {
1495 variants.push(serde_reflection::Named {
1496 name: field_name,
1497 value: tps.last().unwrap().0.clone(),
1498 });
1499 out.extend(tps);
1500 }
1501 }
1502 }
1503 if variants.len() == 1 {
1504 let x = Box::new(variants.pop().expect("We have one. See above."));
1505 enum_def.insert(
1506 id as u32,
1507 serde_reflection::Named {
1508 name: x.name,
1509 value: serde_reflection::VariantFormat::NewType(Box::new(x.value)),
1510 },
1511 );
1512 } else {
1513 let variants = variants.into_iter().map(|v| v.value).collect();
1514 enum_def.insert(
1515 id as u32,
1516 serde_reflection::Named {
1517 name: v.name.clone().unwrap(),
1518 value: serde_reflection::VariantFormat::Tuple(variants),
1519 },
1520 );
1521 }
1522 }
1523 rustdoc_types::ItemEnum::Variant(rustdoc_types::Variant {
1524 kind: rustdoc_types::VariantKind::Struct { ref fields, .. },
1525 ..
1526 }) => {
1527 let mut variants = Vec::new();
1528 for id in fields {
1529 let t = crate_map.resolve_index(None, id, parent_crate);
1530 if let rustdoc_types::ItemEnum::StructField(ref tpe) = t.inner {
1531 let args = HandleDatatypeFieldArgs {
1532 crate_map,
1533 attrs: &t.attrs,
1534 tpe,
1535 parent_crate,
1536 namespace,
1537 parent_args: Vec::new(),
1538 field_name: t.name.as_deref(),
1539 };
1540 for (field_name, tps) in
1541 handle_datatype_field(args, type_map, comment_map)
1542 {
1543 variants.push(serde_reflection::Named {
1544 name: field_name,
1545 value: tps.last().unwrap().0.clone(),
1546 });
1547 out.extend(tps);
1548 }
1549 }
1550 }
1551
1552 enum_def.insert(
1553 id as u32,
1554 serde_reflection::Named {
1555 name: v.name.clone().unwrap(),
1556 value: serde_reflection::VariantFormat::Struct(variants),
1557 },
1558 );
1559 }
1560 _ => unimplemented!(),
1561 }
1562 }
1563 Some(ContainerFormat::Enum(enum_def))
1564 };
1565 let name = get_name_without_path(&p.path);
1566 out.push((Format::TypeName(name.to_owned()), container_format));
1567 out
1568}
1569
1570#[derive(Default, Debug)]
1571struct FieldAttributes {
1572 serde_with: Option<String>,
1573 buffi_type: Option<syn::Path>,
1574 buffi_skip: bool,
1575}
1576
1577struct AttributeHelper(syn::Attribute);
1578
1579impl syn::parse::Parse for AttributeHelper {
1580 fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
1581 let mut attrs = syn::Attribute::parse_outer(input)?;
1582 assert_eq!(attrs.len(), 1, "Only expect a single attribute here");
1583 let attr = attrs.pop().unwrap();
1584 Ok(Self(attr))
1585 }
1586}
1587
1588impl FieldAttributes {
1589 fn parse_attributes(attrs: &[rustdoc_types::Attribute]) -> Self {
1590 let mut field_attrs = Self::default();
1591 for attr in attrs {
1592 if let rustdoc_types::Attribute::Other(r) = attr
1593 && let Ok(AttributeHelper(attr)) = syn::parse_str(r)
1594 {
1595 if attr.path().is_ident("serde")
1596 && let syn::Meta::List(args) = &attr.meta
1597 && let Ok(attr) = args.parse_args::<SerdeAttribute>()
1598 {
1599 match attr {
1600 SerdeAttribute::Flatten => {
1601 unimplemented!("`#[serde(flatten)]` is not supported by bincode")
1602 }
1603 SerdeAttribute::With(w) => field_attrs.serde_with = Some(w),
1604 SerdeAttribute::Unknown(_) => {}
1606 }
1607 } else if attr.path().is_ident("buffi")
1608 && let Ok(attr) =
1609 attr.parse_args::<buffi_annotation_attributes::BuffiAnnotation>()
1610 {
1611 match attr {
1612 buffi_annotation_attributes::BuffiAnnotation::Tpe(tpe) => {
1613 field_attrs.buffi_type = Some(tpe)
1614 }
1615 buffi_annotation_attributes::BuffiAnnotation::Skip => {
1616 field_attrs.buffi_skip = true
1617 }
1618 }
1619 }
1620 }
1621 }
1622 field_attrs
1623 }
1624
1625 fn valid_combination(&self) -> bool {
1626 match (
1627 self.buffi_type.is_some(),
1628 self.serde_with.is_some(),
1629 self.buffi_skip,
1630 ) {
1631 (false, false, false)
1632 | (true, false, false)
1633 | (false, true, false)
1634 | (false, false, true) => true,
1635 _ => {
1636 panic!("Invalid attribute configuration: {self:?}")
1637 }
1638 }
1639 }
1640
1641 fn resolve_types(
1642 &self,
1643 args: HandleDatatypeFieldArgs<'_>,
1644 type_map: &mut HashMap<rustdoc_types::Type, TypeCache>,
1645 comment_map: &mut Option<BTreeMap<Vec<String>, String>>,
1646 ) -> FieldList {
1647 assert!(self.valid_combination());
1648 match (
1649 self.serde_with.as_deref(),
1650 self.buffi_type.as_ref(),
1651 self.buffi_skip,
1652 ) {
1653 (None, None, true) => Vec::new(),
1657 (Some(with), None, false) => {
1660 let (parent_crate, item) = args.crate_map.resolve_by_path(
1661 with,
1662 args.parent_crate,
1663 rustdoc_types::ItemKind::Struct,
1664 );
1665 let tpe = rustdoc_types::Type::ResolvedPath(item);
1666 vec![(
1667 args.field_name.unwrap_or_default().to_owned(),
1668 to_serde_reflect_type(
1669 &tpe,
1670 args.crate_map,
1671 comment_map,
1672 args.parent_args,
1673 &parent_crate,
1674 args.namespace,
1675 type_map,
1676 ),
1677 )]
1678 }
1679 (None, Some(syn_path), false) => {
1681 let (parent_crate, item) = syn_path_to_rustdoc_item(&args, syn_path);
1682 let tpe = rustdoc_types::Type::ResolvedPath(item);
1683 vec![(
1684 args.field_name.unwrap_or_default().to_owned(),
1685 to_serde_reflect_type(
1686 &tpe,
1687 args.crate_map,
1688 comment_map,
1689 args.parent_args,
1690 &parent_crate,
1691 args.namespace,
1692 type_map,
1693 ),
1694 )]
1695 }
1696 (None, None, false) => vec![(
1697 args.field_name.unwrap_or_default().to_owned(),
1698 to_serde_reflect_type(
1699 args.tpe,
1700 args.crate_map,
1701 comment_map,
1702 args.parent_args,
1703 args.parent_crate,
1704 args.namespace,
1705 type_map,
1706 ),
1707 )],
1708 _ => unreachable!("We checked for these patterns above, they should not occur"),
1709 }
1710 }
1711}
1712
1713fn syn_path_to_rustdoc_item(
1714 infer_args: &HandleDatatypeFieldArgs<'_>,
1715 syn_path: &syn::Path,
1716) -> (String, rustdoc_types::Path) {
1717 let path = syn_path
1718 .segments
1719 .iter()
1720 .map(|s| {
1721 s.ident.to_string()
1723 })
1724 .collect::<Vec<_>>()
1725 .join("::");
1726 let (parent_crate, mut item) = infer_args.crate_map.resolve_by_path(
1727 &path,
1728 infer_args.parent_crate,
1729 rustdoc_types::ItemKind::Struct,
1730 );
1731 if let syn::PathArguments::AngleBracketed(args) = &syn_path
1732 .segments
1733 .last()
1734 .expect("There is at least one segment")
1735 .arguments
1736 {
1737 let out = args
1738 .args
1739 .iter()
1740 .map(|a| match a {
1741 syn::GenericArgument::Type(syn::Type::Path(path)) => {
1742 let (_parent_crate, item) = syn_path_to_rustdoc_item(infer_args, &path.path);
1743 rustdoc_types::GenericArg::Type(rustdoc_types::Type::ResolvedPath(item))
1744 }
1745 _ => unimplemented!("Only type arguments are supported by buffi"),
1746 })
1747 .collect::<Vec<_>>();
1748 if !out.is_empty() {
1749 item.args = Some(Box::new(rustdoc_types::GenericArgs::AngleBracketed {
1750 args: out,
1751 constraints: Vec::new(),
1752 }));
1753 }
1754 }
1755 (parent_crate, item)
1756}
1757
1758#[derive(Debug)]
1759enum SerdeAttribute {
1760 Flatten,
1761 With(String),
1762 #[expect(dead_code)]
1763 Unknown(String),
1764}
1765
1766impl syn::parse::Parse for SerdeAttribute {
1767 fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
1768 let ident = input.parse::<syn::Ident>()?;
1769 match ident {
1770 i if i == "with" => {
1771 let _ = input.parse::<syn::Token![=]>()?;
1772 let string = input.parse::<syn::LitStr>()?;
1773 Ok(Self::With(string.value()))
1774 }
1775 i if i == "flatten" => Ok(Self::Flatten),
1776 i => Ok(Self::Unknown(i.to_string())),
1777 }
1778 }
1779}
1780
1781struct HandleDatatypeFieldArgs<'a> {
1782 crate_map: &'a ItemResolver,
1783 parent_crate: &'a str,
1784 namespace: &'a str,
1785 attrs: &'a [rustdoc_types::Attribute],
1786 tpe: &'a rustdoc_types::Type,
1787 parent_args: Vec<rustdoc_types::GenericArg>,
1788 field_name: Option<&'a str>,
1789}
1790
1791fn handle_datatype_field(
1792 args: HandleDatatypeFieldArgs,
1793 type_map: &mut HashMap<rustdoc_types::Type, TypeCache>,
1794 comment_map: &mut Option<BTreeMap<Vec<String>, String>>,
1795) -> FieldList {
1796 let attrs = FieldAttributes::parse_attributes(args.attrs);
1801 attrs.resolve_types(args, type_map, comment_map)
1802}
1803
1804#[allow(clippy::too_many_arguments)]
1805fn generate_exported_struct(
1806 fields: &[rustdoc_types::Id],
1807 crate_map: &ItemResolver,
1808 comment_map: &mut Option<BTreeMap<Vec<String>, String>>,
1809 p: &rustdoc_types::Path,
1810 parent_args: Vec<rustdoc_types::GenericArg>,
1811 parent_crate: &str,
1812 namespace: &str,
1813 type_map: &mut HashMap<rustdoc_types::Type, TypeCache>,
1814 recursive_type: bool,
1815) -> Vec<(
1816 serde_reflection::Format,
1817 Option<serde_reflection::ContainerFormat>,
1818)> {
1819 use serde_reflection::{ContainerFormat, Format};
1820
1821 let mut out = Vec::new();
1822 let mut name = get_name_without_path(&p.path).to_owned();
1823 if let Some(rustdoc_types::GenericArgs::AngleBracketed { args, .. }) = p.args.as_deref() {
1824 for arg in args {
1825 if let rustdoc_types::GenericArg::Type(t) = arg {
1826 let tpe = to_serde_reflect_type(
1827 t,
1828 crate_map,
1829 comment_map,
1830 parent_args.clone(),
1831 parent_crate,
1832 namespace,
1833 type_map,
1834 )
1835 .pop()
1836 .unwrap()
1837 .0;
1838 name = format!("{name}_{}", to_type_name(&tpe));
1839 }
1840 }
1841 }
1842 let container_format = if recursive_type {
1843 None
1845 } else {
1846 let fields = generate_struct_fields(
1847 fields,
1848 crate_map,
1849 comment_map,
1850 p.path.clone(),
1851 p,
1852 parent_args,
1853 parent_crate,
1854 namespace,
1855 type_map,
1856 );
1857 let mut struct_fields = Vec::with_capacity(fields.len());
1858 for (name, tpe) in fields {
1859 let format = tpe.last().unwrap().0.clone();
1860 struct_fields.push(serde_reflection::Named {
1861 name,
1862 value: format,
1863 });
1864 out.extend(tpe);
1865 }
1866 Some(ContainerFormat::Struct(struct_fields))
1867 };
1868 out.push((Format::TypeName(name), container_format));
1869 out
1870}
1871
1872#[allow(clippy::too_many_arguments)]
1873fn generate_struct_fields(
1874 fields: &[rustdoc_types::Id],
1875 crate_map: &ItemResolver,
1876 comment_map: &mut Option<BTreeMap<Vec<String>, String>>,
1877 field_path: String,
1878 p: &rustdoc_types::Path,
1879 parent_args: Vec<rustdoc_types::GenericArg>,
1880 parent_crate: &str,
1881 namespace: &str,
1882 type_map: &mut HashMap<rustdoc_types::Type, TypeCache>,
1883) -> FieldList {
1884 fields
1885 .iter()
1886 .map(|id| crate_map.resolve_index(None, id, parent_crate))
1887 .flat_map(|s| {
1888 if let Some(comment_map) = comment_map
1889 && let Some(ref doc) = s.docs
1890 {
1891 comment_map.insert(
1892 vec![
1893 namespace.to_owned(),
1894 field_path.clone(),
1895 s.name.clone().unwrap(),
1896 ],
1897 doc.clone(),
1898 );
1899 }
1900 if let rustdoc_types::ItemEnum::StructField(ref tpe) = s.inner {
1901 let parent_args =
1902 if let Some(rustdoc_types::GenericArgs::AngleBracketed { args, constraints }) =
1903 p.args.as_deref()
1904 {
1905 if args.is_empty() && constraints.is_empty() {
1906 Vec::new()
1907 } else if parent_args.len() == 1
1908 && args.len() == 1
1909 && matches!(
1910 &args[0],
1911 rustdoc_types::GenericArg::Type(rustdoc_types::Type::Generic(_))
1912 )
1913 {
1914 parent_args.clone()
1915 } else {
1916 args.clone()
1917 }
1918 } else {
1919 Vec::new()
1920 };
1921
1922 let args = HandleDatatypeFieldArgs {
1923 crate_map,
1924 parent_crate,
1925 namespace,
1926 attrs: &s.attrs,
1927 tpe,
1928 parent_args,
1929 field_name: s.name.as_deref(),
1930 };
1931
1932 handle_datatype_field(args, type_map, comment_map)
1933 } else {
1934 vec![]
1935 }
1936 })
1937 .collect::<Vec<_>>()
1938}
1939
1940fn is_relevant_impl(item: &&rustdoc_types::Item) -> bool {
1941 let marker_attribute = Attribute::Other(MARKER_TRAIT_STR.into());
1942 if !item.attrs.contains(&marker_attribute) {
1943 return false;
1944 }
1945 matches!(item.inner, rustdoc_types::ItemEnum::Impl(_))
1946}
1947
1948fn is_free_standing_impl(item: &&rustdoc_types::Item) -> bool {
1949 let marker_attribute = Attribute::Other(MARKER_TRAIT_STR.into());
1950 if !item.attrs.contains(&marker_attribute) {
1951 return false;
1952 }
1953 matches!(item.inner, rustdoc_types::ItemEnum::Function(_))
1954}
1955
1956fn to_c_type(tpe: &rustdoc_types::Type) -> String {
1957 match tpe {
1958 rustdoc_types::Type::ResolvedPath(p) => {
1959 let mut ret = get_name_without_path(&p.path).trim().to_string();
1960 if ret == "c_char" {
1961 String::from("char")
1962 } else {
1963 if let Some(rustdoc_types::GenericArgs::AngleBracketed { args, .. }) =
1964 p.args.as_deref()
1965 {
1966 for arg in args {
1967 if let rustdoc_types::GenericArg::Type(t) = arg {
1968 write!(ret, "_{}", to_c_type(t)).unwrap();
1969 }
1970 }
1971 }
1972 ret
1973 }
1974 }
1975 rustdoc_types::Type::DynTrait(_) => unimplemented!(),
1976 rustdoc_types::Type::Generic(_) => unimplemented!(),
1977 rustdoc_types::Type::Primitive(p) if p == "u8" => String::from("std::uint8_t"),
1978 rustdoc_types::Type::Primitive(p) if p == "usize" => String::from("size_t"),
1979 rustdoc_types::Type::Primitive(p) if p == "u16" => String::from("std::uint16_t"),
1980 rustdoc_types::Type::Primitive(p) => p.clone(),
1981 rustdoc_types::Type::FunctionPointer(_) => String::new(),
1982 rustdoc_types::Type::Tuple(_) => unimplemented!(),
1983 rustdoc_types::Type::Slice(_) => unimplemented!(),
1984 rustdoc_types::Type::Array { .. } => unimplemented!(),
1985 rustdoc_types::Type::ImplTrait(_) => unimplemented!(),
1986 rustdoc_types::Type::Infer => unimplemented!(),
1987 rustdoc_types::Type::RawPointer { is_mutable, type_ } => {
1988 let mut out = if *is_mutable {
1989 String::new()
1990 } else {
1991 String::from("const ")
1992 };
1993 write!(out, "{}*", to_c_type(type_)).unwrap();
1994 out
1995 }
1996 rustdoc_types::Type::BorrowedRef { .. } => String::new(),
1997 rustdoc_types::Type::QualifiedPath { .. } => unimplemented!(),
1998 rustdoc_types::Type::Pat { .. } => unimplemented!(),
1999 }
2000}
2001
2002fn generate_extern_c_function_def(name: &str, func: &rustdoc_types::Function) -> String {
2003 let mut out = String::from("extern \"C\" ");
2004 write!(
2005 out,
2006 "{} ",
2007 func.sig
2008 .output
2009 .as_ref()
2010 .map(to_c_type)
2011 .unwrap_or_else(|| "void".into())
2012 )
2013 .unwrap();
2014
2015 let args = func
2016 .sig
2017 .inputs
2018 .iter()
2019 .map(|(name, tpe)| {
2020 let mut out = to_c_type(tpe);
2021 write!(out, " {name}").unwrap();
2022 out
2023 })
2024 .collect::<Vec<_>>()
2025 .join(", ");
2026 write!(out, "{name}({args});").unwrap();
2027 out
2028}
2029
2030fn get_name_without_path(name: &str) -> &str {
2031 name.rsplit_once("::").map(|(_, e)| e).unwrap_or(name)
2033}