Skip to main content

windows_metadata/merge/
remap.rs

1use super::*;
2
3/// Rewrites a flat winmd into header-based namespaces for package generation.
4#[derive(Default)]
5pub struct Remapper {
6    input: Vec<PathBuf>,
7    output: PathBuf,
8    routes: HashMap<String, String>,
9    sources: Vec<String>,
10    fallback: String,
11}
12
13impl Remapper {
14    pub fn new() -> Self {
15        Self::default()
16    }
17
18    pub fn input(&mut self, input: impl AsRef<Path>) -> &mut Self {
19        self.input.push(input.as_ref().to_path_buf());
20        self
21    }
22
23    pub fn inputs<I, S>(&mut self, inputs: I) -> &mut Self
24    where
25        I: IntoIterator<Item = S>,
26        S: AsRef<Path>,
27    {
28        for input in inputs {
29            self.input(input);
30        }
31        self
32    }
33
34    /// Registers a namespace whose members are remapped.
35    pub fn source(&mut self, namespace: &str) -> &mut Self {
36        self.sources.push(namespace.to_string());
37        self
38    }
39
40    /// Registers namespaces whose members are remapped.
41    pub fn sources<I, S>(&mut self, namespaces: I) -> &mut Self
42    where
43        I: IntoIterator<Item = S>,
44        S: AsRef<str>,
45    {
46        for namespace in namespaces {
47            self.source(namespace.as_ref());
48        }
49        self
50    }
51
52    pub fn fallback(&mut self, namespace: &str) -> &mut Self {
53        self.fallback = namespace.to_string();
54        self
55    }
56
57    pub fn route(&mut self, name: impl Into<String>, namespace: impl Into<String>) -> &mut Self {
58        self.routes.insert(name.into(), namespace.into());
59        self
60    }
61
62    pub fn routes<I, K, V>(&mut self, routes: I) -> &mut Self
63    where
64        I: IntoIterator<Item = (K, V)>,
65        K: Into<String>,
66        V: Into<String>,
67    {
68        for (name, namespace) in routes {
69            self.route(name, namespace);
70        }
71        self
72    }
73
74    pub fn output(&mut self, output: impl AsRef<Path>) -> &mut Self {
75        self.output = output.as_ref().to_path_buf();
76        self
77    }
78
79    pub fn remap(&self) -> Result<(), Error> {
80        if self.output.as_os_str().is_empty() {
81            return Err(Error::new("output is required"));
82        }
83
84        let name = self
85            .output
86            .file_stem()
87            .and_then(|s| s.to_str())
88            .ok_or_else(|| {
89                Error::new(format!("invalid output path `{}`", self.output.display()))
90            })?;
91
92        let files = read_inputs(&self.input)?;
93        let index = reader::Index::new(files);
94        let mut file = writer::File::new(name);
95
96        // Split source `Apis` containers after all regular types have been written.
97        let mut apis: Vec<reader::TypeDef> = Vec::new();
98        let mut types: Vec<reader::TypeDef> = index.types().collect();
99        types.sort_by(|a, b| (a.namespace(), a.name()).cmp(&(b.namespace(), b.name())));
100
101        for ty in types {
102            if self.is_source_apis(ty) {
103                apis.push(ty);
104            } else {
105                self.write_type(&mut file, &index, ty, None);
106            }
107        }
108
109        self.split_apis(&mut file, &apis);
110
111        let bytes = file.into_stream();
112        std::fs::write(&self.output, bytes)
113            .map_err(|e| Error::new(format!("failed to write `{}`: {e}", self.output.display())))
114    }
115
116    fn is_source_apis(&self, ty: reader::TypeDef) -> bool {
117        !ty.flags().contains(TypeAttributes::WindowsRuntime)
118            && ty.category() == reader::TypeCategory::Class
119            && ty.name() == "Apis"
120            && self.sources.iter().any(|s| s == ty.namespace())
121    }
122
123    fn target(&self, namespace: &str, name: &str) -> String {
124        if self.sources.iter().any(|s| s == namespace) {
125            self.routes
126                .get(trim_tick(name))
127                .cloned()
128                .unwrap_or_else(|| self.fallback.clone())
129        } else {
130            namespace.to_string()
131        }
132    }
133
134    fn remap_type(&self, ty: &Type) -> Type {
135        match ty {
136            Type::ClassName(tn) => Type::ClassName(self.remap_type_name(tn)),
137            Type::ValueName(tn) => Type::ValueName(self.remap_type_name(tn)),
138            Type::Array(inner) => Type::Array(Box::new(self.remap_type(inner))),
139            Type::RefMut(inner) => Type::RefMut(Box::new(self.remap_type(inner))),
140            Type::RefConst(inner) => Type::RefConst(Box::new(self.remap_type(inner))),
141            Type::PtrMut(inner, n) => Type::PtrMut(Box::new(self.remap_type(inner)), *n),
142            Type::PtrConst(inner, n) => Type::PtrConst(Box::new(self.remap_type(inner)), *n),
143            Type::ArrayFixed(inner, n) => Type::ArrayFixed(Box::new(self.remap_type(inner)), *n),
144            other => other.clone(),
145        }
146    }
147
148    fn remap_type_name(&self, tn: &TypeName) -> TypeName {
149        TypeName {
150            namespace: self.target(&tn.namespace, &tn.name),
151            name: tn.name.clone(),
152            generics: tn.generics.iter().map(|g| self.remap_type(g)).collect(),
153        }
154    }
155
156    fn remap_signature(&self, sig: &Signature) -> Signature {
157        Signature {
158            flags: sig.flags,
159            return_type: self.remap_type(&sig.return_type),
160            types: sig.types.iter().map(|t| self.remap_type(t)).collect(),
161        }
162    }
163
164    fn remap_extends(&self, file: &mut writer::File, def: reader::TypeDef) -> writer::TypeDefOrRef {
165        def.extends()
166            .map(|extends| {
167                let namespace = self.target(extends.namespace(), extends.name());
168                writer::TypeDefOrRef::TypeRef(file.TypeRef(&namespace, extends.name()))
169            })
170            .unwrap_or_default()
171    }
172
173    /// Writes one non-`Apis` type while preserving ECMA-335 TypeDef field/method ranges.
174    fn write_type(
175        &self,
176        file: &mut writer::File,
177        index: &reader::Index,
178        def: reader::TypeDef,
179        outer: Option<writer::TypeDef>,
180    ) {
181        let extends = self.remap_extends(file, def);
182        let namespace = if def.flags().is_nested() {
183            String::new()
184        } else {
185            self.target(def.namespace(), def.name())
186        };
187
188        let type_def = file.TypeDef(&namespace, def.name(), extends, def.flags());
189
190        if let Some(outer) = outer {
191            file.NestedClass(type_def, outer);
192        }
193
194        for field in def.fields() {
195            let field_def = file.Field(field.name(), &self.remap_type(&field.ty()), field.flags());
196            if let Some(constant) = field.constant() {
197                file.Constant(writer::HasConstant::Field(field_def), &constant.value());
198            }
199            write_attributes(file, writer::HasAttribute::Field(field_def), field);
200        }
201
202        let generics: Vec<_> = def
203            .generic_params()
204            .map(|param| Type::Generic(param.name().to_string(), param.sequence()))
205            .collect();
206
207        write_attributes(file, writer::HasAttribute::TypeDef(type_def), def);
208
209        for map in def.interface_impls() {
210            let interface = self.remap_type(&map.interface(&generics));
211            let interface_impl = file.InterfaceImpl(type_def, &interface);
212            write_attributes(
213                file,
214                writer::HasAttribute::InterfaceImpl(interface_impl),
215                map,
216            );
217        }
218
219        for generic in def.generic_params() {
220            file.GenericParam(
221                generic.name(),
222                writer::TypeOrMethodDef::TypeDef(type_def),
223                generic.sequence(),
224                generic.flags(),
225            );
226        }
227
228        let is_winrt_class = def.category() == reader::TypeCategory::Class
229            && def.flags().contains(TypeAttributes::WindowsRuntime);
230
231        if !is_winrt_class {
232            for method in def.methods() {
233                self.write_method(file, method, &generics);
234            }
235        }
236
237        if let Some(class_layout) = def.class_layout() {
238            file.ClassLayout(
239                type_def,
240                class_layout.packing_size(),
241                class_layout.class_size(),
242            );
243        }
244
245        for inner_def in index.nested(def) {
246            self.write_type(file, index, inner_def, Some(type_def));
247        }
248    }
249
250    fn write_method(&self, file: &mut writer::File, method: reader::MethodDef, generics: &[Type]) {
251        let signature = self.remap_signature(&method.signature(generics));
252        let method_def = file.MethodDef(
253            method.name(),
254            &signature,
255            method.flags(),
256            method.impl_flags(),
257        );
258        for param_def in method.params() {
259            let param = file.Param(param_def.name(), param_def.sequence(), param_def.flags());
260            write_attributes(file, writer::HasAttribute::Param(param), param_def);
261        }
262        write_attributes(file, writer::HasAttribute::MethodDef(method_def), method);
263        if let Some(impl_map) = method.impl_map() {
264            file.ImplMap(
265                method_def,
266                impl_map.flags(),
267                impl_map.import_name(),
268                impl_map.import_scope().name(),
269            );
270        }
271    }
272
273    /// Splits flat `Apis` containers while preserving each TypeDef's contiguous member range.
274    fn split_apis(&self, file: &mut writer::File, apis: &[reader::TypeDef]) {
275        let mut namespaces: Vec<String> = Vec::new();
276        let mut fields: HashMap<String, Vec<reader::Field>> = HashMap::new();
277        let mut methods: HashMap<String, Vec<reader::MethodDef>> = HashMap::new();
278
279        let record = |namespaces: &mut Vec<String>, namespace: String| {
280            if !namespaces.contains(&namespace) {
281                namespaces.push(namespace);
282            }
283        };
284
285        let template = apis.first().copied();
286
287        for &container in apis {
288            for field in container.fields() {
289                let namespace = self.target(container.namespace(), field.name());
290                record(&mut namespaces, namespace.clone());
291                fields.entry(namespace).or_default().push(field);
292            }
293            for method in container.methods() {
294                let namespace = self.target(container.namespace(), method.name());
295                record(&mut namespaces, namespace.clone());
296                methods.entry(namespace).or_default().push(method);
297            }
298        }
299
300        namespaces.sort();
301
302        let Some(template) = template else { return };
303
304        for namespace in &namespaces {
305            let extends = self.remap_extends(file, template);
306            let type_def = file.TypeDef(namespace, "Apis", extends, template.flags());
307
308            for field in fields.get(namespace).into_iter().flatten() {
309                let field_def =
310                    file.Field(field.name(), &self.remap_type(&field.ty()), field.flags());
311                if let Some(constant) = field.constant() {
312                    file.Constant(writer::HasConstant::Field(field_def), &constant.value());
313                }
314                write_attributes(file, writer::HasAttribute::Field(field_def), *field);
315            }
316
317            for method in methods.get(namespace).into_iter().flatten() {
318                self.write_method(file, *method, &[]);
319            }
320
321            write_attributes(file, writer::HasAttribute::TypeDef(type_def), template);
322        }
323    }
324}