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