1 | use super::*; |
2 | mod fmt; |
3 | mod from_reader; |
4 | mod to_winmd; |
5 | use crate::Result; |
6 | pub use from_reader::from_reader; |
7 | use syn::spanned::Spanned; |
8 | |
9 | // TODO: may want to finally get rid of `syn` as it also doesn't support preserving code comments |
10 | |
11 | impl File { |
12 | pub fn parse_str(input: &str) -> Result<Self> { |
13 | Ok(syn::parse_str::<Self>(input)?) |
14 | } |
15 | |
16 | // Note: this isn't called automatically by `parse_str` to avoid canonicalizing when we're merely formatting IDL. |
17 | pub fn canonicalize(&mut self) -> Result<()> { |
18 | // TODO maybe we rewrite the `File` here to resolve any `super` references and use declarations so that |
19 | // subsequently the rdl-to-winmd conversion can just assume everything's fully qualified? |
20 | // * super can't refer to something outside of the IDL file |
21 | // * use declarations are only used for unqualified names that aren't defined in the IDL file |
22 | // * use declarations don't support globs and must name all externally defined types |
23 | // This way we can quickly kick out common invalid IDL files before we lost file/span context info |
24 | |
25 | Ok(()) |
26 | } |
27 | |
28 | pub fn fmt(&self) -> String { |
29 | fmt::Writer::new(self).into_string() |
30 | } |
31 | |
32 | pub fn into_winmd(mut self) -> Result<Vec<u8>> { |
33 | self.canonicalize()?; |
34 | to_winmd::rdl_to_winmd(&self) |
35 | } |
36 | } |
37 | |
38 | // The value of the IDL-specific memory representation is that it allows for constructs that are not modeled in the abstract Module |
39 | // tree such as the use declarations and if we get rid of it we'd always "format" IDL by stripping out any of that into a single |
40 | // canonical form which would not be very friendly to developers. |
41 | #[derive (Debug)] |
42 | pub struct File { |
43 | pub winrt: bool, |
44 | pub references: Vec<syn::ItemUse>, |
45 | pub modules: Vec<Module>, |
46 | } |
47 | |
48 | // TODO: need to change these to unpack the syn types and store strings we can reference for efficiency along with spans since the syn |
49 | // is made for value semantics. |
50 | |
51 | #[derive (Clone, Debug)] |
52 | pub struct Module { |
53 | pub namespace: String, |
54 | pub members: Vec<ModuleMember>, |
55 | } |
56 | |
57 | #[derive (Clone, Debug)] |
58 | pub enum ModuleMember { |
59 | Module(Module), |
60 | Interface(Interface), |
61 | Struct(Struct), |
62 | Enum(Enum), |
63 | Class(Class), |
64 | Function(Function), |
65 | Constant(Constant), |
66 | } |
67 | |
68 | impl ModuleMember { |
69 | pub fn name(&self) -> &str { |
70 | match self { |
71 | Self::Module(module: &Module) => crate::extension(&module.namespace), |
72 | Self::Interface(member: &Interface) => &member.name, |
73 | Self::Struct(member: &Struct) => &member.name, |
74 | Self::Enum(member: &Enum) => &member.name, |
75 | Self::Class(member: &Class) => &member.name, |
76 | Self::Function(member: &Function) => &member.name, |
77 | Self::Constant(member: &Constant) => &member.name, |
78 | } |
79 | } |
80 | } |
81 | |
82 | #[derive (Clone, Debug)] |
83 | pub struct Enum { |
84 | pub winrt: bool, |
85 | pub name: String, |
86 | pub item: syn::ItemEnum, |
87 | } |
88 | |
89 | #[derive (Clone, Debug)] |
90 | pub struct Constant { |
91 | pub name: String, |
92 | pub item: syn::ItemConst, |
93 | } |
94 | |
95 | #[derive (Clone, Debug)] |
96 | pub struct Struct { |
97 | pub winrt: bool, |
98 | pub name: String, |
99 | pub attributes: Vec<syn::Attribute>, |
100 | pub span: proc_macro2::Span, |
101 | pub fields: Vec<Field>, |
102 | } |
103 | |
104 | #[derive (Clone, Debug)] |
105 | pub struct Field { |
106 | pub name: String, |
107 | pub attributes: Vec<syn::Attribute>, |
108 | pub span: proc_macro2::Span, |
109 | pub ty: syn::Type, |
110 | } |
111 | |
112 | #[derive (Clone, Debug)] |
113 | pub struct Class { |
114 | pub name: String, |
115 | pub attributes: Vec<syn::Attribute>, |
116 | pub base: Option<syn::TypePath>, |
117 | pub extends: Vec<syn::TypePath>, |
118 | } |
119 | |
120 | #[derive (Clone, Debug)] |
121 | pub struct Function { |
122 | pub name: String, |
123 | pub item: syn::TraitItemFn, |
124 | } |
125 | |
126 | #[derive (Clone, Debug)] |
127 | pub struct Interface { |
128 | pub winrt: bool, |
129 | pub name: String, |
130 | pub generics: Vec<String>, |
131 | pub attributes: Vec<syn::Attribute>, |
132 | pub extends: Vec<syn::TypePath>, |
133 | pub methods: Vec<syn::TraitItemFn>, |
134 | } |
135 | |
136 | syn::custom_keyword!(interface); |
137 | syn::custom_keyword!(class); |
138 | |
139 | fn winrt(input: syn::parse::ParseStream) -> syn::Result<bool> { |
140 | let attributes: Vec = input.call(function:syn::Attribute::parse_inner)?; |
141 | if attributes.len() == 1 { |
142 | if let syn::Meta::Path(path: &Path) = &attributes[0].meta { |
143 | if path.is_ident("winrt" ) { |
144 | return Ok(true); |
145 | } |
146 | |
147 | if path.is_ident("win32" ) { |
148 | return Ok(false); |
149 | } |
150 | } |
151 | } |
152 | |
153 | Err(syn::Error::new(input.span(), message:"A single `#![win32]` or `#![winrt]` attribute required" )) |
154 | } |
155 | |
156 | impl syn::parse::Parse for File { |
157 | fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> { |
158 | let mut references: Vec = vec![]; |
159 | let mut modules: Vec = vec![]; |
160 | let winrt: bool = winrt(input)?; |
161 | |
162 | while !input.is_empty() { |
163 | let lookahead: Lookahead1<'_> = input.lookahead1(); |
164 | if lookahead.peek(syn::Token![mod]) { |
165 | modules.push(Module::parse(namespace:"" , winrt, input)?); |
166 | } else if lookahead.peek(syn::Token![use]) { |
167 | references.push(input.parse()?); |
168 | } else { |
169 | return Err(lookahead.error()); |
170 | } |
171 | } |
172 | Ok(Self { winrt, references, modules }) |
173 | } |
174 | } |
175 | |
176 | impl Module { |
177 | fn name(&self) -> &str { |
178 | self.namespace.rsplit_once('.' ).map_or(&self.namespace, |(_, name: &str)| name) |
179 | } |
180 | |
181 | fn parse(namespace: &str, winrt: bool, input: syn::parse::ParseStream) -> syn::Result<Self> { |
182 | input.parse::<syn::Token![mod]>()?; |
183 | let name: String = input.parse::<syn::Ident>()?.to_string(); |
184 | |
185 | let namespace: String = if namespace.is_empty() { name.to_string() } else { format!(" {namespace}. {name}" ) }; |
186 | |
187 | let content: ParseBuffer<'_>; |
188 | syn::braced!(content in input); |
189 | let mut members: Vec = vec![]; |
190 | while !content.is_empty() { |
191 | members.push(ModuleMember::parse(&namespace, winrt, &content)?); |
192 | } |
193 | Ok(Self { namespace, members }) |
194 | } |
195 | } |
196 | |
197 | impl ModuleMember { |
198 | fn parse(namespace: &str, winrt: bool, input: syn::parse::ParseStream) -> syn::Result<Self> { |
199 | let attributes: Vec<syn::Attribute> = input.call(syn::Attribute::parse_outer)?; |
200 | let lookahead = input.lookahead1(); |
201 | if lookahead.peek(syn::Token![mod]) { |
202 | if let Some(attribute) = attributes.first() { |
203 | return Err(syn::Error::new(attribute.span(), "`use` attributes not supported" )); |
204 | } |
205 | Ok(ModuleMember::Module(Module::parse(namespace, winrt, input)?)) |
206 | } else if lookahead.peek(interface) { |
207 | Ok(ModuleMember::Interface(Interface::parse(namespace, winrt, attributes, input)?)) |
208 | } else if lookahead.peek(syn::Token![struct]) { |
209 | Ok(ModuleMember::Struct(Struct::parse(namespace, winrt, attributes, input)?)) |
210 | } else if lookahead.peek(syn::Token![enum]) { |
211 | Ok(ModuleMember::Enum(Enum::parse(namespace, winrt, attributes, input)?)) |
212 | } else if lookahead.peek(class) { |
213 | Ok(ModuleMember::Class(Class::parse(attributes, input)?)) |
214 | } else if lookahead.peek(syn::Token![fn]) { |
215 | Ok(ModuleMember::Function(Function::parse(namespace, attributes, input)?)) |
216 | } else if lookahead.peek(syn::Token![const]) { |
217 | Ok(ModuleMember::Constant(Constant::parse(namespace, attributes, input)?)) |
218 | } else { |
219 | Err(lookahead.error()) |
220 | } |
221 | } |
222 | } |
223 | |
224 | impl Class { |
225 | fn parse(attributes: Vec<syn::Attribute>, input: syn::parse::ParseStream) -> syn::Result<Self> { |
226 | input.parse::<class>()?; |
227 | let name: String = input.parse::<syn::Ident>()?.to_string(); |
228 | let mut extends: Vec = Vec::new(); |
229 | let mut base: Option = None; |
230 | |
231 | if input.peek(syn::Token![:]) { |
232 | input.parse::<syn::Token![:]>()?; |
233 | while !input.peek(syn::Token![;]) { |
234 | if input.peek(token:class) { |
235 | input.parse::<class>()?; |
236 | base = Some(input.parse()?); |
237 | } else { |
238 | extends.push(input.parse()?); |
239 | } |
240 | _ = input.parse::<syn::Token![,]>(); |
241 | } |
242 | } |
243 | |
244 | input.parse::<syn::Token![;]>()?; |
245 | Ok(Self { attributes, name, base, extends }) |
246 | } |
247 | } |
248 | |
249 | impl Interface { |
250 | fn parse(_namespace: &str, winrt: bool, attributes: Vec<syn::Attribute>, input: syn::parse::ParseStream) -> syn::Result<Self> { |
251 | input.parse::<interface>()?; |
252 | let name = input.parse::<syn::Ident>()?.to_string(); |
253 | |
254 | let mut generics = Vec::new(); |
255 | |
256 | if input.peek(syn::Token![<]) { |
257 | input.parse::<syn::Token![<]>()?; |
258 | while input.peek(syn::Ident) { |
259 | generics.push(input.parse::<syn::Ident>()?.to_string()); |
260 | _ = input.parse::<syn::Token![,]>(); |
261 | } |
262 | |
263 | input.parse::<syn::Token![>]>()?; |
264 | } |
265 | |
266 | let mut extends = Vec::new(); |
267 | |
268 | if input.peek(syn::Token![:]) { |
269 | input.parse::<syn::Token![:]>()?; |
270 | while !input.peek(syn::token::Brace) { |
271 | extends.push(input.parse()?); |
272 | _ = input.parse::<syn::Token![,]>(); |
273 | } |
274 | } |
275 | |
276 | let content; |
277 | syn::braced!(content in input); |
278 | let mut methods = vec![]; |
279 | while !content.is_empty() { |
280 | methods.push(content.parse()?); |
281 | } |
282 | Ok(Self { winrt, attributes, generics, extends, name, methods }) |
283 | } |
284 | } |
285 | |
286 | impl Struct { |
287 | fn parse(_namespace: &str, winrt: bool, attributes: Vec<syn::Attribute>, input: syn::parse::ParseStream) -> syn::Result<Self> { |
288 | // TODO: need to validate that the struct is valid according to the constraints of the winmd type system. |
289 | // Same for the other types. That way we can spit out errors quickly for things like unnamed fields. |
290 | let span = input.span(); |
291 | let item: syn::ItemStruct = input.parse()?; |
292 | let name = item.ident.to_string(); |
293 | let mut fields = vec![]; |
294 | |
295 | let syn::Fields::Named(named) = item.fields else { |
296 | return Err(syn::Error::new(item.span(), "unnamed fields not supported" )); |
297 | }; |
298 | |
299 | for field in named.named { |
300 | fields.push(Field { |
301 | span: field.span(), |
302 | attributes: field.attrs, |
303 | // Simply unwrapping since we already know that it is a named field. |
304 | name: field.ident.unwrap().to_string(), |
305 | ty: field.ty, |
306 | }); |
307 | } |
308 | |
309 | Ok(Self { winrt, name, attributes, span, fields }) |
310 | } |
311 | } |
312 | |
313 | impl Enum { |
314 | fn parse(_namespace: &str, winrt: bool, attributes: Vec<syn::Attribute>, input: syn::parse::ParseStream) -> syn::Result<Self> { |
315 | let mut item: syn::ItemEnum = input.parse()?; |
316 | item.attrs = attributes; |
317 | let name: String = item.ident.to_string(); |
318 | Ok(Self { winrt, name, item }) |
319 | } |
320 | } |
321 | |
322 | impl Constant { |
323 | fn parse(_namespace: &str, attributes: Vec<syn::Attribute>, input: syn::parse::ParseStream) -> syn::Result<Self> { |
324 | let mut item: syn::ItemConst = input.parse()?; |
325 | item.attrs = attributes; |
326 | let name: String = item.ident.to_string(); |
327 | Ok(Self { name, item }) |
328 | } |
329 | } |
330 | |
331 | impl Function { |
332 | fn parse(_namespace: &str, attributes: Vec<syn::Attribute>, input: syn::parse::ParseStream) -> syn::Result<Self> { |
333 | let mut item: syn::TraitItemFn = input.parse()?; |
334 | item.attrs = attributes; |
335 | let name: String = item.sig.ident.to_string(); |
336 | Ok(Self { name, item }) |
337 | } |
338 | } |
339 | |