1 | use super::*; |
2 | use crate::winmd::{self, writer}; |
3 | |
4 | pub 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, ¶m_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 |
97 | fn 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 | |