1 /* This Source Code Form is subject to the terms of the Mozilla Public
2  * License, v. 2.0. If a copy of the MPL was not distributed with this
3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4 
5 use std::collections::{BTreeSet, HashMap};
6 
7 use crate::*;
8 use anyhow::{bail, Result};
9 
10 type MetadataGroupMap = HashMap<String, MetadataGroup>;
11 
12 // Create empty metadata groups based on the metadata items.
create_metadata_groups(items: &[Metadata]) -> MetadataGroupMap13 pub fn create_metadata_groups(items: &[Metadata]) -> MetadataGroupMap {
14     // Map crate names to MetadataGroup instances
15     items
16         .iter()
17         .filter_map(|i| match i {
18             Metadata::Namespace(namespace) => {
19                 let group = MetadataGroup {
20                     namespace: namespace.clone(),
21                     namespace_docstring: None,
22                     items: BTreeSet::new(),
23                 };
24                 Some((namespace.crate_name.clone(), group))
25             }
26             Metadata::UdlFile(udl) => {
27                 let namespace = NamespaceMetadata {
28                     crate_name: udl.module_path.clone(),
29                     name: udl.namespace.clone(),
30                 };
31                 let group = MetadataGroup {
32                     namespace,
33                     namespace_docstring: None,
34                     items: BTreeSet::new(),
35                 };
36                 Some((udl.module_path.clone(), group))
37             }
38             _ => None,
39         })
40         .collect::<HashMap<_, _>>()
41 }
42 
43 /// Consume the items into the previously created metadata groups.
group_metadata(group_map: &mut MetadataGroupMap, items: Vec<Metadata>) -> Result<()>44 pub fn group_metadata(group_map: &mut MetadataGroupMap, items: Vec<Metadata>) -> Result<()> {
45     for item in items {
46         if matches!(&item, Metadata::Namespace(_)) {
47             continue;
48         }
49 
50         let crate_name = calc_crate_name(item.module_path()).to_owned(); // XXX - kill clone?
51 
52         let item = fixup_external_type(item, group_map);
53         let group = match group_map.get_mut(&crate_name) {
54             Some(ns) => ns,
55             None => bail!("Unknown namespace for {item:?} ({crate_name})"),
56         };
57         if group.items.contains(&item) {
58             bail!("Duplicate metadata item: {item:?}");
59         }
60         group.add_item(item);
61     }
62     Ok(())
63 }
64 
65 #[derive(Debug)]
66 pub struct MetadataGroup {
67     pub namespace: NamespaceMetadata,
68     pub namespace_docstring: Option<String>,
69     pub items: BTreeSet<Metadata>,
70 }
71 
72 impl MetadataGroup {
add_item(&mut self, item: Metadata)73     pub fn add_item(&mut self, item: Metadata) {
74         self.items.insert(item);
75     }
76 }
77 
fixup_external_type(item: Metadata, group_map: &MetadataGroupMap) -> Metadata78 pub fn fixup_external_type(item: Metadata, group_map: &MetadataGroupMap) -> Metadata {
79     let crate_name = calc_crate_name(item.module_path()).to_owned();
80     let converter = ExternalTypeConverter {
81         crate_name: &crate_name,
82         crate_to_namespace: group_map,
83     };
84     converter.convert_item(item)
85 }
86 
87 /// Convert metadata items by replacing types from external crates with Type::External
88 struct ExternalTypeConverter<'a> {
89     crate_name: &'a str,
90     crate_to_namespace: &'a MetadataGroupMap,
91 }
92 
93 impl<'a> ExternalTypeConverter<'a> {
crate_to_namespace(&self, crate_name: &str) -> String94     fn crate_to_namespace(&self, crate_name: &str) -> String {
95         self.crate_to_namespace
96             .get(crate_name)
97             .unwrap_or_else(|| panic!("Can't find namespace for module {crate_name}"))
98             .namespace
99             .name
100             .clone()
101     }
102 
convert_item(&self, item: Metadata) -> Metadata103     fn convert_item(&self, item: Metadata) -> Metadata {
104         match item {
105             Metadata::Func(meta) => Metadata::Func(FnMetadata {
106                 inputs: self.convert_params(meta.inputs),
107                 return_type: self.convert_optional(meta.return_type),
108                 throws: self.convert_optional(meta.throws),
109                 ..meta
110             }),
111             Metadata::Method(meta) => Metadata::Method(MethodMetadata {
112                 inputs: self.convert_params(meta.inputs),
113                 return_type: self.convert_optional(meta.return_type),
114                 throws: self.convert_optional(meta.throws),
115                 ..meta
116             }),
117             Metadata::TraitMethod(meta) => Metadata::TraitMethod(TraitMethodMetadata {
118                 inputs: self.convert_params(meta.inputs),
119                 return_type: self.convert_optional(meta.return_type),
120                 throws: self.convert_optional(meta.throws),
121                 ..meta
122             }),
123             Metadata::Constructor(meta) => Metadata::Constructor(ConstructorMetadata {
124                 inputs: self.convert_params(meta.inputs),
125                 throws: self.convert_optional(meta.throws),
126                 ..meta
127             }),
128             Metadata::Record(meta) => Metadata::Record(RecordMetadata {
129                 fields: self.convert_fields(meta.fields),
130                 ..meta
131             }),
132             Metadata::Enum(meta) => Metadata::Enum(self.convert_enum(meta)),
133             _ => item,
134         }
135     }
136 
convert_params(&self, params: Vec<FnParamMetadata>) -> Vec<FnParamMetadata>137     fn convert_params(&self, params: Vec<FnParamMetadata>) -> Vec<FnParamMetadata> {
138         params
139             .into_iter()
140             .map(|param| FnParamMetadata {
141                 ty: self.convert_type(param.ty),
142                 ..param
143             })
144             .collect()
145     }
146 
convert_fields(&self, fields: Vec<FieldMetadata>) -> Vec<FieldMetadata>147     fn convert_fields(&self, fields: Vec<FieldMetadata>) -> Vec<FieldMetadata> {
148         fields
149             .into_iter()
150             .map(|field| FieldMetadata {
151                 ty: self.convert_type(field.ty),
152                 ..field
153             })
154             .collect()
155     }
156 
convert_enum(&self, enum_: EnumMetadata) -> EnumMetadata157     fn convert_enum(&self, enum_: EnumMetadata) -> EnumMetadata {
158         EnumMetadata {
159             variants: enum_
160                 .variants
161                 .into_iter()
162                 .map(|variant| VariantMetadata {
163                     fields: self.convert_fields(variant.fields),
164                     ..variant
165                 })
166                 .collect(),
167             ..enum_
168         }
169     }
170 
convert_optional(&self, ty: Option<Type>) -> Option<Type>171     fn convert_optional(&self, ty: Option<Type>) -> Option<Type> {
172         ty.map(|ty| self.convert_type(ty))
173     }
174 
convert_type(&self, ty: Type) -> Type175     fn convert_type(&self, ty: Type) -> Type {
176         match ty {
177             // Convert `ty` if it's external
178             Type::Enum { module_path, name } | Type::Record { module_path, name }
179                 if self.is_module_path_external(&module_path) =>
180             {
181                 Type::External {
182                     namespace: self.crate_to_namespace(&module_path),
183                     module_path,
184                     name,
185                     kind: ExternalKind::DataClass,
186                     tagged: false,
187                 }
188             }
189             Type::Custom {
190                 module_path, name, ..
191             } if self.is_module_path_external(&module_path) => {
192                 // For now, it's safe to assume that all custom types are data classes.
193                 // There's no reason to use a custom type with an interface.
194                 Type::External {
195                     namespace: self.crate_to_namespace(&module_path),
196                     module_path,
197                     name,
198                     kind: ExternalKind::DataClass,
199                     tagged: false,
200                 }
201             }
202             Type::Object {
203                 module_path, name, ..
204             } if self.is_module_path_external(&module_path) => Type::External {
205                 namespace: self.crate_to_namespace(&module_path),
206                 module_path,
207                 name,
208                 kind: ExternalKind::Interface,
209                 tagged: false,
210             },
211             Type::CallbackInterface { module_path, name }
212                 if self.is_module_path_external(&module_path) =>
213             {
214                 panic!("External callback interfaces not supported ({name})")
215             }
216             // Convert child types
217             Type::Custom {
218                 module_path,
219                 name,
220                 builtin,
221                 ..
222             } => Type::Custom {
223                 module_path,
224                 name,
225                 builtin: Box::new(self.convert_type(*builtin)),
226             },
227             Type::Optional { inner_type } => Type::Optional {
228                 inner_type: Box::new(self.convert_type(*inner_type)),
229             },
230             Type::Sequence { inner_type } => Type::Sequence {
231                 inner_type: Box::new(self.convert_type(*inner_type)),
232             },
233             Type::Map {
234                 key_type,
235                 value_type,
236             } => Type::Map {
237                 key_type: Box::new(self.convert_type(*key_type)),
238                 value_type: Box::new(self.convert_type(*value_type)),
239             },
240             // Existing External types probably need namespace fixed.
241             Type::External {
242                 namespace,
243                 module_path,
244                 name,
245                 kind,
246                 tagged,
247             } => {
248                 assert!(namespace.is_empty());
249                 Type::External {
250                     namespace: self.crate_to_namespace(&module_path),
251                     module_path,
252                     name,
253                     kind,
254                     tagged,
255                 }
256             }
257 
258             // Otherwise, just return the type unchanged
259             _ => ty,
260         }
261     }
262 
is_module_path_external(&self, module_path: &str) -> bool263     fn is_module_path_external(&self, module_path: &str) -> bool {
264         calc_crate_name(module_path) != self.crate_name
265     }
266 }
267 
calc_crate_name(module_path: &str) -> &str268 fn calc_crate_name(module_path: &str) -> &str {
269     module_path.split("::").next().unwrap()
270 }
271