1use super::*;
2use crate::tokens::{quote, to_ident, TokenStream};
3use crate::{rdl, Error, Result, Tree};
4use metadata::*;
5
6pub fn from_reader(reader: &'static metadata::Reader, mut config: std::collections::BTreeMap<&str, &str>, output: &str) -> Result<()> {
7 let dialect: Dialect = match config.remove(key:"type") {
8 Some("winrt") => Dialect::WinRT,
9 Some("win32") => Dialect::Win32,
10 _ => return Err(Error::new(message:"configuration value `type` must be `win32` or `winrt`")),
11 };
12
13 let mut writer: Writer = Writer::new(reader, output, dialect);
14
15 // TODO: be sure to use the same "split" key for winmd splitting.
16 // May also want to support split=N similar to the way MIDLRT supports winmd splitting
17 // at different nesting levels.
18 writer.split = config.remove(key:"split").is_some();
19
20 if let Some((key: &&str, _)) = config.first_key_value() {
21 return Err(Error::new(&format!("invalid configuration value `{key}`")));
22 }
23
24 if writer.split {
25 gen_split(&writer)
26 } else {
27 gen_file(&writer)
28 }
29}
30
31fn gen_split(writer: &Writer) -> Result<()> {
32 let tree: Tree = Tree::new(writer.reader);
33 let directory: &str = crate::directory(&writer.output);
34
35 // TODO: parallelize
36 for tree: &Tree in tree.flatten() {
37 let tokens: TokenStream = writer.tree(tree);
38
39 if !tokens.is_empty() {
40 let output: String = format!("{directory}/{}.rdl", tree.namespace);
41 writer.write_to_file(&output, tokens)?;
42 }
43 }
44
45 Ok(())
46}
47
48fn gen_file(writer: &Writer) -> Result<()> {
49 let tree: Tree = Tree::new(writer.reader);
50 let tokens: TokenStream = writer.tree(&tree);
51 writer.write_to_file(&writer.output, tokens)
52}
53
54#[derive(Debug, Copy, Clone, PartialEq)]
55enum Dialect {
56 Win32,
57 WinRT,
58}
59
60struct Writer {
61 reader: &'static metadata::Reader,
62 namespace: &'static str,
63 dialect: Dialect,
64 split: bool,
65 output: String,
66}
67
68impl Writer {
69 fn new(reader: &'static metadata::Reader, output: &str, dialect: Dialect) -> Self {
70 Self { reader, namespace: "", output: output.to_string(), dialect, split: false }
71 }
72
73 fn with_namespace(&self, namespace: &'static str) -> Self {
74 Self { reader: self.reader, namespace, dialect: self.dialect, output: self.output.clone(), split: self.split }
75 }
76
77 fn write_to_file(&self, output: &str, tokens: TokenStream) -> Result<()> {
78 let dialect = match self.dialect {
79 Dialect::Win32 => quote! { #![win32] },
80 Dialect::WinRT => quote! { #![winrt] },
81 };
82
83 let tokens = quote! {
84 #dialect
85 #tokens
86 };
87
88 let file = rdl::File::parse_str(&tokens.into_string())?;
89 crate::write_to_file(output, file.fmt())
90 //crate::write_to_file(output, tokens.into_string())
91 }
92
93 fn tree(&self, tree: &Tree) -> TokenStream {
94 let items = self.items(tree);
95
96 if self.split {
97 let mut tokens = items;
98
99 if !tokens.is_empty() {
100 for name in tree.namespace.rsplit('.').map(to_ident) {
101 tokens = quote! {
102 mod #name {
103 #tokens
104 }
105 };
106 }
107 }
108
109 tokens
110 } else {
111 let name = to_ident(tree.namespace.rsplit_once('.').map_or(tree.namespace, |(_, name)| name));
112
113 let modules = tree.nested.values().map(|tree| self.with_namespace(tree.namespace).tree(tree));
114
115 if tree.namespace.is_empty() {
116 quote! {
117 #(#modules)*
118 #items
119 }
120 } else {
121 quote! {
122 mod #name {
123 #(#modules)*
124 #items
125 }
126 }
127 }
128 }
129 }
130
131 fn items(&self, tree: &Tree) -> TokenStream {
132 let mut functions = vec![];
133 let mut constants = vec![];
134 let mut types = vec![];
135
136 if !tree.namespace.is_empty() {
137 for item in self.reader.namespace_items(tree.namespace).filter(|item| match item {
138 metadata::Item::Type(def) => {
139 let winrt = def.flags().contains(metadata::TypeAttributes::WindowsRuntime);
140 match self.dialect {
141 Dialect::Win32 => !winrt,
142 Dialect::WinRT => winrt,
143 }
144 }
145 metadata::Item::Fn(_, _) | metadata::Item::Const(_) => self.dialect == Dialect::Win32,
146 }) {
147 match item {
148 metadata::Item::Type(def) => types.push(self.type_def(def)),
149 metadata::Item::Const(field) => constants.push(self.constant(field)),
150 metadata::Item::Fn(method, namespace) => functions.push(self.function(method, namespace)),
151 }
152 }
153 }
154
155 quote! {
156 #(#functions)*
157 #(#constants)*
158 #(#types)*
159 }
160 }
161
162 fn function(&self, def: metadata::MethodDef, _namespace: &str) -> TokenStream {
163 let name = to_ident(def.name());
164 quote! { fn #name(); }
165 }
166
167 fn constant(&self, def: metadata::Field) -> TokenStream {
168 let name = to_ident(def.name());
169 quote! { const #name: i32 = 0; }
170 }
171
172 fn type_def(&self, def: metadata::TypeDef) -> TokenStream {
173 if let Some(extends) = def.extends() {
174 if extends.namespace == "System" {
175 if extends.name == "Enum" {
176 self.enum_def(def)
177 } else if extends.name == "ValueType" {
178 self.struct_def(def)
179 } else if extends.name == "MulticastDelegate" {
180 self.delegate_def(def)
181 } else {
182 self.class_def(def)
183 }
184 } else {
185 self.class_def(def)
186 }
187 } else {
188 self.interface_def(def)
189 }
190 }
191
192 fn enum_def(&self, def: metadata::TypeDef) -> TokenStream {
193 let name = to_ident(def.name());
194
195 quote! {
196 struct #name {
197
198 }
199 }
200 }
201
202 fn struct_def(&self, def: metadata::TypeDef) -> TokenStream {
203 let name = to_ident(def.name());
204
205 let fields = def.fields().map(|field| {
206 let name = to_ident(field.name());
207 let ty = self.ty(&field.ty(Some(def)));
208 quote! {
209 #name: #ty
210 }
211 });
212
213 quote! {
214 struct #name {
215 #(#fields),*
216 }
217 }
218 }
219
220 fn delegate_def(&self, def: metadata::TypeDef) -> TokenStream {
221 let name = to_ident(def.name());
222
223 quote! {
224 struct #name {
225
226 }
227 }
228 }
229
230 fn class_def(&self, def: metadata::TypeDef) -> TokenStream {
231 let name = to_ident(def.name());
232 let implements = self.implements(def, &[]);
233
234 quote! {
235 class #name #implements;
236 }
237 }
238
239 fn interface_def(&self, def: metadata::TypeDef) -> TokenStream {
240 let name = to_ident(def.name());
241 let generics = &metadata::type_def_generics(def);
242 let implements = self.implements(def, generics);
243
244 let methods = def.methods().map(|method| {
245 let name = to_ident(method.name());
246
247 // TODO: use reader.method_def_signature instead
248 let signature = metadata::method_def_signature(def.namespace(), method, generics);
249
250 let return_type = self.return_type(&signature.return_type);
251
252 let params = signature.params.iter().map(|param| {
253 let name = to_ident(param.def.name());
254 let ty = self.ty(&param.ty);
255 quote! { #name: #ty }
256 });
257
258 quote! {
259 fn #name(#(#params),*) #return_type;
260 }
261 });
262
263 let generics = self.generics(generics);
264
265 quote! {
266 interface #name #generics #implements {
267 #(#methods)*
268 }
269 }
270 }
271
272 fn generics(&self, generics: &[metadata::Type]) -> TokenStream {
273 if generics.is_empty() {
274 quote! {}
275 } else {
276 let generics = generics.iter().map(|generic| self.ty(generic));
277
278 quote! { <#(#generics),*>}
279 }
280 }
281
282 fn implements(&self, def: metadata::TypeDef, generics: &[metadata::Type]) -> TokenStream {
283 let mut types = Vec::<TokenStream>::new();
284
285 // TODO: if a winrt composable class then start with base
286 // TODO: then list default interface first
287 // Then everything else
288
289 for interface in type_def_interfaces(def, generics) {
290 if interface.kind == InterfaceKind::Default {
291 types.insert(0, self.ty(&interface.ty));
292 } else {
293 types.push(self.ty(&interface.ty));
294 }
295 }
296
297 if let Some(type_name) = def.extends() {
298 if type_name != metadata::TypeName::Object {
299 let namespace = self.namespace(type_name.namespace);
300 let name = to_ident(type_name.name);
301 // TODO: ideally the "class" contextual keyword wouldn't be needed here
302 // but currently there's no way to tell the base class apart from a required interface.
303 types.insert(0, quote! { class #namespace #name });
304 }
305 }
306
307 if types.is_empty() {
308 quote! {}
309 } else {
310 quote! { : #(#types),* }
311 }
312 }
313
314 fn return_type(&self, ty: &metadata::Type) -> TokenStream {
315 match ty {
316 metadata::Type::Void => quote! {},
317 _ => {
318 let ty = self.ty(ty);
319 quote! { -> #ty }
320 }
321 }
322 }
323
324 fn ty(&self, ty: &metadata::Type) -> TokenStream {
325 match ty {
326 metadata::Type::Void => quote! { ::core::ffi::c_void },
327 metadata::Type::Bool => quote! { bool },
328 metadata::Type::Char => quote! { u16 },
329 metadata::Type::I8 => quote! { i8 },
330 metadata::Type::U8 => quote! { u8 },
331 metadata::Type::I16 => quote! { i16 },
332 metadata::Type::U16 => quote! { u16 },
333 metadata::Type::I32 => quote! { i32 },
334 metadata::Type::U32 => quote! { u32 },
335 metadata::Type::I64 => quote! { i64 },
336 metadata::Type::U64 => quote! { u64 },
337 metadata::Type::F32 => quote! { f32 },
338 metadata::Type::F64 => quote! { f64 },
339 metadata::Type::ISize => quote! { isize },
340 metadata::Type::USize => quote! { usize },
341
342 // TODO: dialect-specific keywords for "well-known types" that don't map to metadata in all cases.
343 metadata::Type::String => quote! { HSTRING },
344 metadata::Type::HRESULT => quote! { HRESULT },
345 metadata::Type::GUID => quote! { GUID },
346 metadata::Type::IInspectable => quote! { IInspectable },
347 metadata::Type::IUnknown => quote! { IUnknown },
348
349 metadata::Type::TypeDef(def, generics) => {
350 let namespace = self.namespace(def.namespace());
351 let name = to_ident(def.name());
352 if generics.is_empty() {
353 quote! { #namespace #name }
354 } else {
355 let generics = generics.iter().map(|ty| self.ty(ty));
356 quote! { #namespace #name<#(#generics,)*> }
357 }
358 }
359
360 metadata::Type::TypeRef(type_name) => {
361 let namespace = self.namespace(type_name.namespace);
362 let name = to_ident(type_name.name);
363 quote! { #namespace #name }
364 }
365
366 metadata::Type::GenericParam(generic) => generic.name().into(),
367 metadata::Type::WinrtArray(ty) => self.ty(ty),
368 metadata::Type::WinrtArrayRef(ty) => self.ty(ty),
369 metadata::Type::ConstRef(ty) => self.ty(ty),
370 metadata::Type::MutPtr(ty, _pointers) => self.ty(ty),
371 metadata::Type::ConstPtr(ty, _pointers) => self.ty(ty),
372 metadata::Type::Win32Array(ty, _len) => self.ty(ty),
373 // TODO: these types should just be regular metadata type defs
374 metadata::Type::PSTR => quote! { PSTR },
375 metadata::Type::PWSTR => quote! { PWSTR },
376 metadata::Type::PCSTR => quote! { PCSTR },
377 metadata::Type::PCWSTR => quote! { PCWSTR },
378 metadata::Type::BSTR => quote! { BSTR },
379 metadata::Type::PrimitiveOrEnum(_, ty) => self.ty(ty),
380 rest => unimplemented!("{rest:?}"),
381 }
382 }
383
384 fn namespace(&self, namespace: &str) -> TokenStream {
385 // TODO: handle nested structs?
386 if namespace.is_empty() || self.namespace == namespace {
387 quote! {}
388 } else {
389 // TODO: problem with making relative paths here is that we don't have the context to disambiguate
390
391 // let mut relative = self.namespace.split('.').peekable();
392 // let mut namespace = namespace.split('.').peekable();
393 // let mut related = false;
394
395 // while relative.peek() == namespace.peek() {
396 // related = true;
397
398 // if relative.next().is_none() {
399 // break;
400 // }
401
402 // namespace.next();
403 // }
404
405 // let mut tokens = TokenStream::new();
406
407 // if related {
408 // for _ in 0..relative.count() {
409 // tokens.push_str("super::");
410 // }
411 // }
412
413 // for namespace in namespace {
414 // tokens.push_str(namespace);
415 // tokens.push_str("::");
416 // }
417
418 // tokens
419
420 // TODO: so instead we just gen it out in full
421
422 let mut tokens = TokenStream::new();
423
424 for namespace in namespace.split('.') {
425 tokens.push_str(namespace);
426 tokens.push_str("::");
427 }
428
429 tokens
430 }
431 }
432}
433