playdate_bindgen/gen/docs/
gen.rs1use std::cell::Cell;
2use std::collections::HashMap;
3use syn::Item;
4use syn::Type;
5use syn::ItemStruct;
6
7use crate::Result;
8use super::DocsMap;
9
10
11pub fn engage(bindings: &mut syn::File, root: &str, docs: &DocsMap) -> Result<()> {
12 let items = Cell::from_mut(&mut bindings.items[..]);
14 let items_cells = items.as_slice_of_cells();
15 if let Some(root) = find_struct(items_cells, root) {
16 walk_struct(items_cells, None, root, docs);
17 }
18
19 Ok(())
20}
21
22
23fn find_struct<'t>(items: &'t [Cell<Item>], name: &str) -> Option<&'t mut ItemStruct> {
24 items.iter().find_map(|item| {
25 match unsafe { item.as_ptr().as_mut() }.expect("cell is null, impossible") {
26 syn::Item::Struct(entry) if entry.ident == name => Some(entry),
27 _ => None,
28 }
29 })
30}
31
32
33fn walk_struct(items: &[Cell<Item>],
34 this: Option<&str>,
35 structure: &mut ItemStruct,
36 docs: &HashMap<String, String>) {
37 let prefix = this.map(|s| format!("{s}.")).unwrap_or("".to_owned());
38 for field in structure.fields.iter_mut() {
39 let field_name = field.ident.as_ref().expect("field name");
40
41 match &mut field.ty {
42 syn::Type::Ptr(entry) => {
43 match entry.elem.as_mut() {
44 syn::Type::Path(path) => {
45 if let Some(ident) = path.path.get_ident() {
46 if let Some(ty) = find_struct(items, &ident.to_string()) {
47 let key = format!("{prefix}{field_name}");
48 walk_struct(items, Some(&key), ty, docs);
49 }
50 }
51 },
52 _ => unimplemented!("unknown ty: {}", quote::quote!(#{{field.ty}})),
53 }
54 },
55
56 syn::Type::Path(path) => {
57 if let Some(ident) = path.path.get_ident() {
58 unimplemented!("unexpected struct: '{}'", quote::quote!(#ident))
59 } else if let Some(ty) = extract_type_from_option(&field.ty) {
60 match ty {
61 Type::BareFn(_) => {
62 let key = format!("{prefix}{field_name}");
63 if let Some(doc) = docs.get(&key) {
64 let attr: syn::Attribute = syn::parse_quote! { #[doc = #doc] };
65 field.attrs.push(attr);
66 } else {
67 #[cfg(feature = "log")]
68 println!("cargo::warning=Doc not found for '{key}'");
69 }
70 },
71 _ => unimplemented!("unexpected ty: '{}'", quote::quote!(#ty)),
72 }
73 } else {
74 unimplemented!("unexpected ty: '{}'", quote::quote!(#&path))
75 }
76 },
77
78 ty => {
79 println!(
80 "unknown: {prefix}{}: {}",
81 field.ident.as_ref().expect("field.ident"),
82 quote::quote!(#ty)
83 );
84 },
85 }
86 }
87}
88
89
90fn extract_type_from_option(ty: &syn::Type) -> Option<&syn::Type> {
92 use syn::{GenericArgument, Path, PathArguments, PathSegment};
93
94 fn extract_type_path(ty: &syn::Type) -> Option<&Path> {
95 match *ty {
96 syn::Type::Path(ref tp) if tp.qself.is_none() => Some(&tp.path),
97 _ => None,
98 }
99 }
100
101 fn extract_option_segment(path: &Path) -> Option<&PathSegment> {
102 let idents_of_path = path.segments.iter().fold(String::new(), |mut acc, v| {
103 acc.push_str(&v.ident.to_string());
104 acc.push('|');
105 acc
106 });
107 vec!["Option|", "std|option|Option|", "core|option|Option|"].into_iter()
108 .find(|s| idents_of_path == *s)
109 .and_then(|_| path.segments.last())
110 }
111
112 extract_type_path(ty).and_then(|path| extract_option_segment(path))
113 .and_then(|path_seg| {
114 let type_params = &path_seg.arguments;
115 match *type_params {
116 PathArguments::AngleBracketed(ref params) => params.args.first(),
117 _ => None,
118 }
119 })
120 .and_then(|generic_arg| {
121 match *generic_arg {
122 GenericArgument::Type(ref ty) => Some(ty),
123 _ => None,
124 }
125 })
126}