1use std::collections::{BTreeMap, HashSet};
2use std::fmt::Write as _;
3
4use quote::ToTokens;
5use serde::Serialize;
6use syn::{ImplItem, Item, ItemMod, UseTree, Visibility};
7
8use super::{
9 AttrInheritContext, Attrs, CustomType, Enum, Ident, Method, ModSymbol, Mutability,
10 OpaqueStruct, Path, PathType, RustLink, Struct,
11};
12use crate::environment::*;
13
14#[derive(Debug)]
16enum DiplomatStructAttribute {
17 Out,
20 Opaque,
24 OpaqueMut,
30}
31
32impl DiplomatStructAttribute {
33 fn parse(attrs: &[syn::Attribute]) -> Result<Option<Self>, Vec<Self>> {
37 let mut buf = String::with_capacity(32);
38 let mut res = Ok(None);
39 for attr in attrs {
40 buf.clear();
41 write!(&mut buf, "{}", attr.path().to_token_stream()).unwrap();
42 let parsed = match buf.as_str() {
43 "diplomat :: out" => Some(Self::Out),
44 "diplomat :: opaque" => Some(Self::Opaque),
45 "diplomat :: opaque_mut" => Some(Self::OpaqueMut),
46 _ => None,
47 };
48
49 if let Some(parsed) = parsed {
50 match res {
51 Ok(None) => res = Ok(Some(parsed)),
52 Ok(Some(first)) => res = Err(vec![first, parsed]),
53 Err(ref mut errors) => errors.push(parsed),
54 }
55 }
56 }
57
58 res
59 }
60}
61
62#[derive(Clone, Serialize, Debug)]
63#[non_exhaustive]
64pub struct Module {
65 pub name: Ident,
66 pub imports: Vec<(Path, Ident)>,
67 pub declared_types: BTreeMap<Ident, CustomType>,
68 pub sub_modules: Vec<Module>,
69 pub attrs: Attrs,
70}
71
72impl Module {
73 pub fn all_rust_links(&self) -> HashSet<&RustLink> {
74 let mut rust_links = self
75 .declared_types
76 .values()
77 .flat_map(|t| t.all_rust_links())
78 .collect::<HashSet<_>>();
79
80 self.sub_modules.iter().for_each(|m| {
81 rust_links.extend(m.all_rust_links().iter());
82 });
83 rust_links
84 }
85
86 pub fn insert_all_types(&self, in_path: Path, out: &mut Env) {
87 let mut mod_symbols = ModuleEnv::new(self.attrs.clone());
88
89 self.imports.iter().for_each(|(path, name)| {
90 mod_symbols.insert(name.clone(), ModSymbol::Alias(path.clone()));
91 });
92
93 self.declared_types.iter().for_each(|(k, v)| {
94 if mod_symbols
95 .insert(k.clone(), ModSymbol::CustomType(v.clone()))
96 .is_some()
97 {
98 panic!("Two types were declared with the same name, this needs to be implemented");
99 }
100 });
101
102 let path_to_self = in_path.sub_path(self.name.clone());
103 self.sub_modules.iter().for_each(|m| {
104 m.insert_all_types(path_to_self.clone(), out);
105 mod_symbols.insert(m.name.clone(), ModSymbol::SubModule(m.name.clone()));
106 });
107
108 out.insert(path_to_self, mod_symbols);
109 }
110
111 pub fn from_syn(input: &ItemMod, force_analyze: bool) -> Module {
112 let mut custom_types_by_name = BTreeMap::new();
113 let mut sub_modules = Vec::new();
114 let mut imports = Vec::new();
115
116 let analyze_types = force_analyze
117 || input
118 .attrs
119 .iter()
120 .any(|a| a.path().to_token_stream().to_string() == "diplomat :: bridge");
121
122 let mod_attrs: Attrs = (&*input.attrs).into();
123
124 let impl_parent_attrs: Attrs =
125 mod_attrs.attrs_for_inheritance(AttrInheritContext::MethodOrImplFromModule);
126 let type_parent_attrs: Attrs = mod_attrs.attrs_for_inheritance(AttrInheritContext::Type);
127
128 input
129 .content
130 .as_ref()
131 .map(|t| &t.1[..])
132 .unwrap_or_default()
133 .iter()
134 .for_each(|a| match a {
135 Item::Use(u) => {
136 if analyze_types {
137 extract_imports(&Path::empty(), &u.tree, &mut imports);
138 }
139 }
140 Item::Struct(strct) => {
141 if analyze_types {
142 let custom_type = match DiplomatStructAttribute::parse(&strct.attrs[..]) {
143 Ok(None) => CustomType::Struct(Struct::new(strct, false, &type_parent_attrs)),
144 Ok(Some(DiplomatStructAttribute::Out)) => {
145 CustomType::Struct(Struct::new(strct, true, &type_parent_attrs))
146 }
147 Ok(Some(DiplomatStructAttribute::Opaque)) => {
148 CustomType::Opaque(OpaqueStruct::new(strct, Mutability::Immutable, &type_parent_attrs))
149 }
150 Ok(Some(DiplomatStructAttribute::OpaqueMut)) => {
151 CustomType::Opaque(OpaqueStruct::new(strct, Mutability::Mutable, &type_parent_attrs))
152 }
153 Err(errors) => {
154 panic!("Multiple conflicting Diplomat struct attributes, there can be at most one: {errors:?}");
155 }
156 };
157
158 custom_types_by_name.insert(Ident::from(&strct.ident), custom_type);
159 }
160 }
161
162 Item::Enum(enm) => {
163 if analyze_types {
164 let ident = (&enm.ident).into();
165 let enm = Enum::new(enm, &type_parent_attrs);
166 custom_types_by_name
167 .insert(ident, CustomType::Enum(enm));
168 }
169 }
170
171 Item::Impl(imp) => {
172 if analyze_types {
173 assert!(imp.trait_.is_none());
174
175 let self_path = match imp.self_ty.as_ref() {
176 syn::Type::Path(s) => PathType::from(s),
177 _ => panic!("Self type not found"),
178 };
179 let mut impl_attrs = impl_parent_attrs.clone();
180 impl_attrs.add_attrs(&imp.attrs);
181 let method_parent_attrs = impl_attrs.attrs_for_inheritance(AttrInheritContext::MethodFromImpl);
182 let mut new_methods = imp
183 .items
184 .iter()
185 .filter_map(|i| match i {
186 ImplItem::Fn(m) => Some(m),
187 _ => None,
188 })
189 .filter(|m| matches!(m.vis, Visibility::Public(_)))
190 .map(|m| Method::from_syn(m, self_path.clone(), Some(&imp.generics), &method_parent_attrs))
191 .collect();
192
193 let self_ident = self_path.path.elements.last().unwrap();
194
195 match custom_types_by_name.get_mut(self_ident).unwrap() {
196 CustomType::Struct(strct) => {
197 strct.methods.append(&mut new_methods);
198 }
199 CustomType::Opaque(strct) => {
200 strct.methods.append(&mut new_methods);
201 }
202 CustomType::Enum(enm) => {
203 enm.methods.append(&mut new_methods);
204 }
205 }
206 }
207 }
208 Item::Mod(item_mod) => {
209 sub_modules.push(Module::from_syn(item_mod, false));
210 }
211 _ => {}
212 });
213
214 Module {
215 name: (&input.ident).into(),
216 imports,
217 declared_types: custom_types_by_name,
218 sub_modules,
219 attrs: mod_attrs,
220 }
221 }
222}
223
224fn extract_imports(base_path: &Path, use_tree: &UseTree, out: &mut Vec<(Path, Ident)>) {
225 match use_tree {
226 UseTree::Name(name) => out.push((
227 base_path.sub_path((&name.ident).into()),
228 (&name.ident).into(),
229 )),
230 UseTree::Path(path) => {
231 extract_imports(&base_path.sub_path((&path.ident).into()), &path.tree, out)
232 }
233 UseTree::Glob(_) => todo!("Glob imports are not yet supported"),
234 UseTree::Group(group) => {
235 group
236 .items
237 .iter()
238 .for_each(|i| extract_imports(base_path, i, out));
239 }
240 UseTree::Rename(rename) => out.push((
241 base_path.sub_path((&rename.ident).into()),
242 (&rename.rename).into(),
243 )),
244 }
245}
246
247#[derive(Serialize, Clone, Debug)]
248#[non_exhaustive]
249pub struct File {
250 pub modules: BTreeMap<String, Module>,
251}
252
253impl File {
254 pub fn all_types(&self) -> Env {
256 let mut out = Env::default();
257 let mut top_symbols = ModuleEnv::new(Default::default());
258
259 self.modules.values().for_each(|m| {
260 m.insert_all_types(Path::empty(), &mut out);
261 top_symbols.insert(m.name.clone(), ModSymbol::SubModule(m.name.clone()));
262 });
263
264 out.insert(Path::empty(), top_symbols);
265
266 out
267 }
268
269 pub fn all_rust_links(&self) -> HashSet<&RustLink> {
270 self.modules
271 .values()
272 .flat_map(|m| m.all_rust_links().into_iter())
273 .collect()
274 }
275}
276
277impl From<&syn::File> for File {
278 fn from(file: &syn::File) -> File {
280 let mut out = BTreeMap::new();
281 file.items.iter().for_each(|i| {
282 if let Item::Mod(item_mod) = i {
283 out.insert(
284 item_mod.ident.to_string(),
285 Module::from_syn(item_mod, false),
286 );
287 }
288 });
289
290 File { modules: out }
291 }
292}
293
294#[cfg(test)]
295mod tests {
296 use insta::{self, Settings};
297
298 use syn;
299
300 use crate::ast::{File, Module};
301
302 #[test]
303 fn simple_mod() {
304 let mut settings = Settings::new();
305 settings.set_sort_maps(true);
306
307 settings.bind(|| {
308 insta::assert_yaml_snapshot!(Module::from_syn(
309 &syn::parse_quote! {
310 mod ffi {
311 struct NonOpaqueStruct {
312 a: i32,
313 b: Box<NonOpaqueStruct>
314 }
315
316 impl NonOpaqueStruct {
317 pub fn new(x: i32) -> NonOpaqueStruct {
318 unimplemented!();
319 }
320
321 pub fn set_a(&mut self, new_a: i32) {
322 self.a = new_a;
323 }
324 }
325
326 #[diplomat::opaque]
327 struct OpaqueStruct {
328 a: SomeExternalType
329 }
330
331 impl OpaqueStruct {
332 pub fn new() -> Box<OpaqueStruct> {
333 unimplemented!();
334 }
335
336 pub fn get_string(&self) -> String {
337 unimplemented!()
338 }
339 }
340 }
341 },
342 true
343 ));
344 });
345 }
346
347 #[test]
348 fn method_visibility() {
349 let mut settings = Settings::new();
350 settings.set_sort_maps(true);
351
352 settings.bind(|| {
353 insta::assert_yaml_snapshot!(Module::from_syn(
354 &syn::parse_quote! {
355 #[diplomat::bridge]
356 mod ffi {
357 struct Foo {}
358
359 impl Foo {
360 pub fn pub_fn() {
361 unimplemented!()
362 }
363 pub(crate) fn pub_crate_fn() {
364 unimplemented!()
365 }
366 pub(super) fn pub_super_fn() {
367 unimplemented!()
368 }
369 fn priv_fn() {
370 unimplemented!()
371 }
372 }
373 }
374 },
375 true
376 ));
377 });
378 }
379
380 #[test]
381 fn import_in_non_diplomat_not_analyzed() {
382 let mut settings = Settings::new();
383 settings.set_sort_maps(true);
384
385 settings.bind(|| {
386 insta::assert_yaml_snapshot!(File::from(&syn::parse_quote! {
387 #[diplomat::bridge]
388 mod ffi {
389 struct Foo {}
390 }
391
392 mod other {
393 use something::*;
394 }
395 }));
396 });
397 }
398}