1 | use super::*; |
2 | use crate::tokens::{quote, to_ident, TokenStream}; |
3 | use crate::{rdl, Error, Result, Tree}; |
4 | use metadata::*; |
5 | |
6 | pub 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 | |
31 | fn 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 | |
48 | fn 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)] |
55 | enum Dialect { |
56 | Win32, |
57 | WinRT, |
58 | } |
59 | |
60 | struct Writer { |
61 | reader: &'static metadata::Reader, |
62 | namespace: &'static str, |
63 | dialect: Dialect, |
64 | split: bool, |
65 | output: String, |
66 | } |
67 | |
68 | impl 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(¶m.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 | |