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