1use super::*;
2use crate::winmd::{self, writer};
3
4pub fn from_reader(reader: &metadata::Reader, config: std::collections::BTreeMap<&str, &str>, output: &str) -> crate::Result<()> {
5 let mut writer = winmd::Writer::new(output);
6
7 // TODO: do we need any configuration values for winmd generation?
8 // Maybe per-namespace winmd files for namespace-splitting - be sure to use
9 // the same key as for winmd generation.
10
11 if let Some((key, _)) = config.first_key_value() {
12 return Err(crate::Error::new(&format!("invalid configuration value `{key}`")));
13 }
14
15 // TODO: just use the reader directly since we now have everything in the reader, there's no need to abstract
16 // away the source format. Few reprs is always better.
17
18 for item in reader.items() {
19 // TODO: cover all variants
20 let metadata::Item::Type(def) = item else {
21 continue;
22 };
23
24 let generics = &metadata::type_def_generics(def);
25
26 let extends = if let Some(extends) = def.extends() { writer.insert_type_ref(extends.namespace, extends.name) } else { 0 };
27
28 writer.tables.TypeDef.push(writer::TypeDef {
29 Extends: extends,
30 FieldList: writer.tables.Field.len() as u32,
31 Flags: def.flags().0,
32 MethodList: writer.tables.MethodDef.len() as u32,
33 TypeName: writer.strings.insert(def.name()),
34 TypeNamespace: writer.strings.insert(def.namespace()),
35 });
36
37 for generic in def.generics() {
38 writer.tables.GenericParam.push(writer::GenericParam {
39 Number: generic.number(), // TODO: isn't this just going to be incremental?
40 Flags: 0,
41 Owner: writer::TypeOrMethodDef::TypeDef(writer.tables.TypeDef.len() as u32 - 1).encode(),
42 Name: writer.strings.insert(generic.name()),
43 });
44 }
45
46 for interface in metadata::type_def_interfaces(def, generics) {
47 let ty = winmd_type(&interface.ty);
48
49 let reference = match &ty {
50 winmd::Type::TypeRef(type_name) if type_name.generics.is_empty() => writer.insert_type_ref(&type_name.namespace, &type_name.name),
51 winmd::Type::TypeRef(_) => writer.insert_type_spec(ty),
52 winmd::Type::IUnknown => writer.insert_type_ref("Windows.Win32.System.Com", "IUnknown"),
53 winmd::Type::IInspectable => writer.insert_type_ref("Windows.Win32.System.WinRT", "IInspectable"),
54 rest => unimplemented!("{rest:?}"),
55 };
56
57 writer.tables.InterfaceImpl.push(writer::InterfaceImpl { Class: writer.tables.TypeDef.len() as u32 - 1, Interface: reference });
58 }
59
60 // TODO: if the class is "Apis" then should we sort the fields (constants) and methods (functions) for stability
61
62 for field in def.fields() {
63 let ty = winmd_type(&field.ty(Some(def)));
64 let signature = writer.insert_field_sig(&ty);
65
66 writer.tables.Field.push(writer::Field { Flags: field.flags().0, Name: writer.strings.insert(field.name()), Signature: signature });
67 }
68
69 for method in def.methods() {
70 let signature = method.signature(generics);
71 let return_type = winmd_type(&signature.return_type);
72 let param_types: Vec<Type> = signature.params.iter().map(winmd_type).collect();
73
74 let signature = writer.insert_method_sig(signature.call_flags, &return_type, &param_types);
75
76 writer.tables.MethodDef.push(winmd::MethodDef {
77 RVA: 0,
78 ImplFlags: method.impl_flags().0,
79 Flags: method.flags().0,
80 Name: writer.strings.insert(method.name()),
81 Signature: signature,
82 ParamList: writer.tables.Param.len() as u32,
83 });
84
85 for param in method.params() {
86 writer.tables.Param.push(writer::Param { Flags: param.flags().0, Sequence: param.sequence(), Name: writer.strings.insert(param.name()) });
87 }
88 }
89 }
90
91 // TODO: In theory, `config` could instruct this function to balance the types across a number of winmd files
92 // like mdmerge supports for namespace-splitting.
93 crate::write_to_file(output, writer.into_stream()).map_err(|err| err.with_path(output))
94}
95
96// TODO: keep the basic type conversion
97fn winmd_type(ty: &metadata::Type) -> winmd::Type {
98 match ty {
99 metadata::Type::Void => winmd::Type::Void,
100 metadata::Type::Bool => winmd::Type::Bool,
101 metadata::Type::Char => winmd::Type::Char,
102 metadata::Type::I8 => winmd::Type::I8,
103 metadata::Type::U8 => winmd::Type::U8,
104 metadata::Type::I16 => winmd::Type::I16,
105 metadata::Type::U16 => winmd::Type::U16,
106 metadata::Type::I32 => winmd::Type::I32,
107 metadata::Type::U32 => winmd::Type::U32,
108 metadata::Type::I64 => winmd::Type::I64,
109 metadata::Type::U64 => winmd::Type::U64,
110 metadata::Type::F32 => winmd::Type::F32,
111 metadata::Type::F64 => winmd::Type::F64,
112 metadata::Type::ISize => winmd::Type::ISize,
113 metadata::Type::USize => winmd::Type::USize,
114 metadata::Type::String => winmd::Type::String,
115 metadata::Type::GUID => winmd::Type::GUID,
116 metadata::Type::IUnknown => winmd::Type::IUnknown,
117 metadata::Type::IInspectable => winmd::Type::IInspectable,
118 metadata::Type::HRESULT => winmd::Type::HRESULT,
119 metadata::Type::PSTR => winmd::Type::PSTR,
120 metadata::Type::PWSTR => winmd::Type::PWSTR,
121 metadata::Type::PCSTR => winmd::Type::PCSTR,
122 metadata::Type::PCWSTR => winmd::Type::PCWSTR,
123 metadata::Type::BSTR => winmd::Type::BSTR,
124 metadata::Type::Type => winmd::Type::Type,
125 metadata::Type::TypeDef(def, generics) => winmd::Type::TypeRef(winmd::TypeName { namespace: def.namespace().to_string(), name: def.name().to_string(), generics: generics.iter().map(winmd_type).collect() }),
126 metadata::Type::GenericParam(generic) => winmd::Type::GenericParam(generic.number()),
127 metadata::Type::ConstRef(ty) => winmd::Type::ConstRef(Box::new(winmd_type(ty))),
128 metadata::Type::WinrtArrayRef(ty) => winmd::Type::WinrtArrayRef(Box::new(winmd_type(ty))),
129 metadata::Type::WinrtArray(ty) => winmd::Type::WinrtArray(Box::new(winmd_type(ty))),
130 metadata::Type::MutPtr(ty, pointers) => winmd::Type::MutPtr(Box::new(winmd_type(ty)), *pointers),
131 metadata::Type::ConstPtr(ty, pointers) => winmd::Type::ConstPtr(Box::new(winmd_type(ty)), *pointers),
132 metadata::Type::Win32Array(ty, len) => winmd::Type::Win32Array(Box::new(winmd_type(ty)), *len),
133 rest => unimplemented!("{rest:?}"),
134 }
135}
136