1mod attribute;
2mod callback;
3mod class;
4mod delegate;
5mod r#enum;
6mod r#fn;
7mod interface;
8mod layout;
9mod r#struct;
10
11use super::*;
12use attribute::*;
13use callback::*;
14use class::*;
15use delegate::*;
16use r#enum::*;
17use r#fn::*;
18use interface::*;
19use layout::*;
20use metadata::AsRow;
21use metadata::HasAttributes;
22use r#struct::*;
23
24#[derive(Default)]
25pub struct Writer {
27 input: Vec<PathBuf>,
28 input_default: bool,
29 input_bytes: Vec<Vec<u8>>,
30 filter: Vec<String>,
31 output: PathBuf,
32 split: bool,
33 partition: Option<HashMap<String, String>>,
34}
35
36impl Writer {
37 pub fn new() -> Self {
39 Self::default()
40 }
41
42 pub fn input(&mut self, input: impl AsRef<Path>) -> &mut Self {
44 self.input.push(input.as_ref().to_path_buf());
45 self
46 }
47
48 pub fn input_bytes(&mut self, input: &[u8]) -> &mut Self {
50 self.input_bytes.push(input.to_vec());
51 self
52 }
53
54 pub fn input_byte_sets<I, B>(&mut self, inputs: I) -> &mut Self
56 where
57 I: IntoIterator<Item = B>,
58 B: AsRef<[u8]>,
59 {
60 for input in inputs {
61 self.input_bytes(input.as_ref());
62 }
63 self
64 }
65
66 pub fn input_default(&mut self) -> &mut Self {
68 self.input_default = true;
69 self
70 }
71
72 pub fn output(&mut self, output: impl AsRef<Path>) -> &mut Self {
74 self.output = output.as_ref().to_path_buf();
75 self
76 }
77
78 pub fn inputs<I, S>(&mut self, inputs: I) -> &mut Self
80 where
81 I: IntoIterator<Item = S>,
82 S: AsRef<Path>,
83 {
84 for input in inputs {
85 self.input(input);
86 }
87 self
88 }
89
90 pub fn filters<I, S>(&mut self, filters: I) -> &mut Self
92 where
93 I: IntoIterator<Item = S>,
94 S: AsRef<str>,
95 {
96 for filter in filters {
97 self.filter.push(filter.as_ref().to_string());
98 }
99
100 self
101 }
102
103 pub fn filter(&mut self, filter: &str) -> &mut Self {
105 self.filter.push(filter.to_string());
106 self
107 }
108
109 pub fn split(&mut self) -> &mut Self {
111 self.split = true;
112 self
113 }
114
115 pub fn partition(&mut self, map: HashMap<String, String>) -> &mut Self {
117 self.partition = Some(map);
118 self
119 }
120
121 pub fn write(&self) -> Result<(), Error> {
123 if self.output.as_os_str().is_empty() {
124 return Err(Error::new("output is required", "", 0, 0));
125 }
126
127 let mut files = vec![];
128
129 for file_name in &expand_input_files(&self.input, "winmd")? {
130 let source = file_name.to_string_lossy();
131 files.push(
132 metadata::reader::File::read(file_name)
133 .ok_or_else(|| Error::new("invalid input", &source, 0, 0))?,
134 );
135 }
136
137 if self.input_default {
138 files.extend(
139 [windows_default::WINRT, windows_default::WIN32]
140 .into_iter()
141 .map(|bytes| metadata::reader::File::new(bytes.to_vec()).unwrap()),
142 );
143 }
144
145 for bytes in &self.input_bytes {
146 files.push(
147 metadata::reader::File::new(bytes.clone())
148 .ok_or_else(|| Error::new("invalid input", "<memory>", 0, 0))?,
149 );
150 }
151
152 let index = metadata::reader::Index::new(files);
153 let rules = resolve_filter(&self.filter, &index);
154
155 if let Some(map) = &self.partition {
156 if let Ok(entries) = std::fs::read_dir(&self.output) {
157 for entry in entries.flatten() {
158 let path = entry.path();
159 if path
160 .extension()
161 .is_some_and(|ext| ext.eq_ignore_ascii_case("rdl"))
162 {
163 let _ = std::fs::remove_file(path);
164 }
165 }
166 }
167
168 let mut layouts: BTreeMap<String, Layout> = BTreeMap::new();
169 for namespace in index.namespaces() {
170 if namespace.is_empty() {
171 continue;
172 }
173 for (name, item) in index.namespace_items(namespace) {
174 if !item_included(&rules, namespace, name) {
175 continue;
176 }
177 let Some(stem) = map.get(name) else {
178 continue;
179 };
180 let layout = layouts.entry(stem.clone()).or_default();
181 for (item_name, tokens) in write_items(namespace, item)? {
182 layout.insert(namespace, &item_name, item_winrt(item), tokens.to_string());
183 }
184 }
185 }
186
187 for (stem, layout) in &layouts {
188 let output = layout.to_string();
189 if output.is_empty() {
190 continue;
191 }
192
193 let mut path = PathBuf::new();
194 path.push(&self.output);
195 path.push(format!("{stem}.rdl"));
196
197 write_to_file(path, formatter::format(&output))?;
198 }
199
200 return Ok(());
201 }
202
203 if self.split {
204 if let Ok(entries) = std::fs::read_dir(&self.output) {
205 for entry in entries.flatten() {
206 let path = entry.path();
207 if path
208 .extension()
209 .is_some_and(|ext| ext.eq_ignore_ascii_case("rdl"))
210 {
211 let _ = std::fs::remove_file(path);
212 }
213 }
214 }
215
216 for namespace in index.namespaces() {
217 if namespace.is_empty() {
218 continue;
219 }
220
221 let mut layout = Layout::new();
222
223 for (name, item) in index.namespace_items(namespace) {
224 if !item_included(&rules, namespace, name) {
225 continue;
226 }
227 for (item_name, tokens) in write_items(namespace, item)? {
228 layout.insert(namespace, &item_name, item_winrt(item), tokens.to_string());
229 }
230 }
231
232 let output = layout.to_string();
233
234 if output.is_empty() {
235 continue;
236 }
237
238 let mut path = PathBuf::new();
239 path.push(&self.output);
240 path.push(format!("{namespace}.rdl"));
241
242 write_to_file(path, formatter::format(&output))?;
243 }
244 } else {
245 let mut layout = Layout::new();
246
247 for namespace in index.namespaces() {
248 for (name, item) in index.namespace_items(namespace) {
249 if !item_included(&rules, namespace, name) {
250 continue;
251 }
252 for (item_name, tokens) in write_items(namespace, item)? {
253 layout.insert(namespace, &item_name, item_winrt(item), tokens.to_string());
254 }
255 }
256 }
257
258 let output = layout.to_string();
259 write_to_file(&self.output, formatter::format(&output))?;
260 }
261
262 Ok(())
263 }
264}
265
266fn namespace_starts_with(namespace: &str, starts_with: &str) -> bool {
267 namespace.starts_with(starts_with)
268 && (namespace.len() == starts_with.len()
269 || namespace.as_bytes().get(starts_with.len()) == Some(&b'.'))
270}
271
272enum FilterRule {
273 Namespace(String),
274 Type(String, String),
275}
276
277fn resolve_filter(filter: &[String], index: &metadata::reader::Index) -> Vec<(FilterRule, bool)> {
279 let mut rules = vec![];
280
281 for f in filter {
282 let (rule_str, include) = if let Some(r) = f.strip_prefix('!') {
283 (r, false)
284 } else {
285 (f.as_str(), true)
286 };
287
288 if index
289 .namespaces()
290 .any(|ns| namespace_starts_with(ns, rule_str))
291 {
292 rules.push((FilterRule::Namespace(rule_str.to_string()), include));
293 continue;
294 }
295
296 if let Some((namespace, name)) = rule_str.rsplit_once('.')
297 && index.get_item(namespace, name).next().is_some()
298 {
299 rules.push((
300 FilterRule::Type(namespace.to_string(), name.to_string()),
301 include,
302 ));
303 continue;
304 }
305
306 let mut found = false;
307 for ns in index.namespaces() {
308 if index.get_item(ns, rule_str).next().is_some() {
309 rules.push((
310 FilterRule::Type(ns.to_string(), rule_str.to_string()),
311 include,
312 ));
313 found = true;
314 }
315 }
316 if found {
317 continue;
318 }
319
320 rules.push((FilterRule::Namespace(rule_str.to_string()), include));
321 }
322
323 rules
324}
325
326fn item_included(rules: &[(FilterRule, bool)], namespace: &str, name: &str) -> bool {
327 if rules.is_empty() {
328 return true;
329 }
330
331 let mut matched_include = false;
332
333 for (rule, include) in rules {
334 let matches = match rule {
335 FilterRule::Namespace(prefix) => namespace_starts_with(namespace, prefix),
336 FilterRule::Type(ns, n) => ns == namespace && n == name,
337 };
338
339 if matches {
340 if !include {
341 return false;
342 }
343 matched_include = true;
344 }
345 }
346
347 matched_include
348}
349
350fn item_winrt(item: metadata::reader::Item) -> bool {
351 match item {
352 metadata::reader::Item::Type(item) => item
353 .flags()
354 .contains(metadata::TypeAttributes::WindowsRuntime),
355 _ => false,
356 }
357}
358
359fn write_items(
360 namespace: &str,
361 item: metadata::reader::Item,
362) -> Result<Vec<(String, TokenStream)>, Error> {
363 match item {
364 metadata::reader::Item::Type(ty) => write_type_def_items(namespace, &ty),
365 metadata::reader::Item::Fn(ty) => {
366 Ok(vec![(ty.name().to_string(), write_fn(namespace, &ty)?)])
367 }
368 metadata::reader::Item::Const(ty) => {
369 Ok(vec![(ty.name().to_string(), write_const(namespace, &ty)?)])
370 }
371 }
372}
373
374fn write_type_def_items(
375 namespace: &str,
376 item: &metadata::reader::TypeDef,
377) -> Result<Vec<(String, TokenStream)>, Error> {
378 if item.category() == metadata::reader::TypeCategory::Struct {
379 if item.attributes().any(|attr| {
381 attr.namespace() == METADATA_NAMESPACE && attr.name() == "NativeTypedefAttribute"
382 }) {
383 let name = write_ident(item.name());
384 let field = item
385 .fields()
386 .next()
387 .ok_or_else(|| writer_err!("typedef `{}` has no field", item.name()))?;
388 let ty = write_type(namespace, &field.ty());
389 let arch_attr = write_arch_attr(item.arches());
390 let tokens = quote! { #arch_attr type #name = #ty; };
391 return Ok(vec![(item.name().to_string(), tokens)]);
392 }
393 write_struct_items(item)
394 } else {
395 let tokens = write_type_def(item)?;
396 if tokens.is_empty() {
397 Ok(vec![])
398 } else {
399 Ok(vec![(item.name().to_string(), tokens)])
400 }
401 }
402}
403
404fn write_const(namespace: &str, item: &metadata::reader::Field) -> Result<TokenStream, Error> {
405 let is_guid = match item.ty() {
407 metadata::Type::ValueName(tn) => &tn == ("System", "Guid") || tn.name == "GUID",
408 _ => false,
409 };
410 if is_guid && item.find_attribute("GuidAttribute").is_some() {
411 write_const_guid(namespace, item)
412 } else if item.find_attribute("GuidAttribute").is_some() {
413 write_const_property_key(namespace, item)
415 } else {
416 write_const_value(namespace, item)
417 }
418}
419
420fn write_const_value(
421 namespace: &str,
422 item: &metadata::reader::Field,
423) -> Result<TokenStream, Error> {
424 let name = write_ident(item.name());
425 let constant = item.constant();
426 let ty = write_type(namespace, &item.ty());
427 let arch_attr = write_arch_attr(item.arches());
428 let custom_attrs = write_custom_attributes_except(
429 item.attributes(),
430 namespace,
431 item.index(),
432 &["SupportedArchitectureAttribute"],
433 )?;
434
435 Ok(if let Some(constant) = constant {
436 let value = write_typed_value(namespace, &item.ty(), &constant.value());
437 quote! {
438 #arch_attr
439 #(#custom_attrs)*
440 const #name: #ty = #value;
441 }
442 } else {
443 quote! {
444 #arch_attr
445 #(#custom_attrs)*
446 const #name: #ty;
447 }
448 })
449}
450
451fn write_const_guid(
452 _namespace: &str,
453 item: &metadata::reader::Field,
454) -> Result<TokenStream, Error> {
455 let name = write_ident(item.name());
456 let arch_attr = write_arch_attr(item.arches());
457 let literal = guid_attribute_literal(item)?;
458 Ok(quote! { #arch_attr const #name: GUID = #literal; })
459}
460
461fn write_const_property_key(
463 namespace: &str,
464 item: &metadata::reader::Field,
465) -> Result<TokenStream, Error> {
466 let name = write_ident(item.name());
467 let ty = write_type(namespace, &item.ty());
468 let arch_attr = write_arch_attr(item.arches());
469 let guid = guid_attribute_literal(item)?;
470 let constant = item
471 .constant()
472 .ok_or_else(|| writer_err!("property key constant `{}` has no `pid` value", item.name()))?;
473 let pid = write_value(namespace, &constant.value());
474 Ok(quote! { #arch_attr #[guid(#guid)] const #name: #ty = #pid; })
475}
476
477fn guid_attribute_literal(item: &metadata::reader::Field) -> Result<syn::LitInt, Error> {
479 let attribute = item
480 .find_attribute("GuidAttribute")
481 .ok_or_else(|| writer_err!("GUID constant `{}` has no `GuidAttribute`", item.name()))?;
482
483 let value: u128 = attribute
484 .value()
485 .into_iter()
486 .try_fold(0u128, |acc, (_, val)| match val {
487 metadata::Value::U8(x) => Ok((acc << 8) | x as u128),
488 metadata::Value::U16(x) => Ok((acc << 16) | x as u128),
489 metadata::Value::U32(x) => Ok((acc << 32) | x as u128),
490 metadata::Value::U64(x) => Ok((acc << 64) | x as u128),
491 _ => Err(writer_err!(
492 "unexpected value type in `GuidAttribute` for `{}`",
493 item.name()
494 )),
495 })?;
496
497 let value = format!(
498 "0x{:08x}_{:04x}_{:04x}_{:04x}_{:012x}",
499 (value >> 96) as u32,
500 (value >> 80) as u16,
501 (value >> 64) as u16,
502 (value >> 48) as u16,
503 value as u64 & 0xffffffffffff,
504 );
505
506 Ok(syn::LitInt::new(&value, Span::call_site()))
507}
508
509fn write_params(
510 namespace: &str,
511 method: &metadata::reader::MethodDef,
512 signature_types: Vec<metadata::Type>,
513) -> Result<Vec<TokenStream>, Error> {
514 let params = method
515 .params_by_sequence(signature_types.len())
516 .map_err(|error| {
517 writer_err!(
518 "method `{}` has invalid parameter metadata: {error}",
519 method.name()
520 )
521 })?;
522
523 signature_types
524 .into_iter()
525 .enumerate()
526 .map(|(position, ty)| {
527 let param = params.params()[position];
528 let is_mutable = matches!(ty, metadata::Type::RefMut(_) | metadata::Type::PtrMut(..));
529 let direction = param.map_or_else(
530 || {
531 if is_mutable {
532 metadata::reader::ParamDirection::Output
533 } else {
534 metadata::reader::ParamDirection::Input
535 }
536 },
537 |param| param.direction(),
538 );
539 let (effective_in, has_out) = match direction {
540 metadata::reader::ParamDirection::Unspecified
541 | metadata::reader::ParamDirection::Input => (true, false),
542 metadata::reader::ParamDirection::Output => (false, true),
543 metadata::reader::ParamDirection::InputOutput => (true, true),
544 };
545 let in_attr = if effective_in && (has_out || is_mutable) {
546 quote! { #[r#in] }
547 } else {
548 quote! {}
549 };
550 let out_attr = if has_out && (effective_in || !is_mutable) {
551 quote! { #[out] }
552 } else {
553 quote! {}
554 };
555 let opt_attr = if param.is_some_and(|param| param.is_optional()) {
556 quote! { #[opt] }
557 } else {
558 quote! {}
559 };
560 let name = param.map_or_else(
561 || write_ident(&format!("p{position}")),
562 |param| write_ident(param.name()),
563 );
564 let param_attrs = match param {
565 Some(param) => write_custom_attributes_except(
566 param.attributes(),
567 namespace,
568 method.index(),
569 &[],
570 )?,
571 None => Vec::new(),
572 };
573 let ty = write_type(namespace, &ty);
574 Ok(quote! { #(#param_attrs)* #in_attr #out_attr #opt_attr #name: #ty })
575 })
576 .collect()
577}
578
579fn write_return_type(
580 namespace: &str,
581 method: &metadata::reader::MethodDef,
582 signature: &metadata::Signature,
583) -> Result<TokenStream, Error> {
584 let params = method
585 .params_by_sequence(signature.types.len())
586 .map_err(|error| {
587 writer_err!(
588 "method `{}` has invalid parameter metadata: {error}",
589 method.name()
590 )
591 })?;
592 let return_attrs: Vec<TokenStream> = params
593 .return_param()
594 .map(|p| write_custom_attributes(p.attributes(), namespace, method.index()))
595 .transpose()?
596 .unwrap_or_default();
597
598 Ok(match &signature.return_type {
599 metadata::Type::Void => quote! {},
600 ty => {
601 let ty = write_type(namespace, ty);
602 quote! { -> #(#return_attrs)* #ty }
603 }
604 })
605}
606
607fn write_custom_attributes<'a>(
608 attributes: impl Iterator<Item = windows_metadata::reader::Attribute<'a>>,
609 item_namespace: &str,
610 index: &windows_metadata::reader::Index,
611) -> Result<Vec<TokenStream>, Error> {
612 write_custom_attributes_except(attributes, item_namespace, index, &[])
613}
614
615fn write_custom_attributes_except<'a>(
616 attributes: impl Iterator<Item = windows_metadata::reader::Attribute<'a>>,
617 item_namespace: &str,
618 index: &windows_metadata::reader::Index,
619 exclude: &[&str],
620) -> Result<Vec<TokenStream>, Error> {
621 let mut rendered = attributes
622 .filter(|attr| {
623 !(namespace_starts_with(attr.namespace(), "System")
624 || exclude.contains(&attr.name())
625 || (attr.namespace() == METADATA_NAMESPACE
626 && attr.name() == "NativeTypedefAttribute"))
627 })
628 .map(|attr| {
629 let attr_ns = attr.namespace();
630 let values = attr.value();
631
632 let pseudo = if attr_ns == METADATA_NAMESPACE {
634 let arg_names: Vec<String> = values.iter().map(|(n, _)| n.clone()).collect();
635 pseudo_for_metadata(attr.name(), &arg_names)
636 } else {
637 None
638 };
639
640 let name_ts = if let Some(pseudo) = pseudo {
641 write_ident(pseudo.short)
642 } else {
643 let attr_short = attr
644 .name()
645 .strip_suffix("Attribute")
646 .unwrap_or_else(|| attr.name());
647
648 if attr_ns.is_empty() || attr_ns == item_namespace {
649 write_ident(attr_short)
650 } else {
651 let mut tokens = TokenStream::new();
652 for part in attr_ns.split('.') {
653 let ident = write_ident(part);
654 tokens = quote! { #tokens #ident :: };
655 }
656 let short = write_ident(attr_short);
657 quote! { #tokens #short }
658 }
659 };
660
661 let drop_names = pseudo.and_then(|p| p.prop).is_some();
663 let args: Vec<TokenStream> = values
664 .into_iter()
665 .map(|(name, v)| {
666 let value_ts = match &v {
667 metadata::Value::EnumValue(tn, inner) => {
668 write_enum_value(item_namespace, tn, inner, index)?
669 }
670 _ => write_value(item_namespace, &v),
671 };
672 let ts = if name.is_empty() || drop_names {
673 value_ts
674 } else {
675 let name_ident = write_ident(&name);
676 quote! { #name_ident = #value_ts }
677 };
678 Ok(ts)
679 })
680 .collect::<Result<Vec<_>, Error>>()?;
681
682 Ok(if args.is_empty() {
683 quote! { #[#name_ts] }
684 } else {
685 quote! { #[#name_ts(#(#args),*)] }
686 })
687 })
688 .collect::<Result<Vec<TokenStream>, Error>>()?;
689
690 rendered.sort_by_key(|ts| ts.to_string());
692 Ok(rendered)
693}
694
695fn write_enum_value(
697 namespace: &str,
698 tn: &metadata::TypeName,
699 inner: &metadata::Value,
700 index: &metadata::reader::Index,
701) -> Result<TokenStream, Error> {
702 let inner_i32 = match inner {
703 metadata::Value::I32(n) => *n,
704 _ => return Ok(write_value(namespace, inner)),
705 };
706
707 let mut found_in_index = false;
708 for typedef in index.get(&tn.namespace, &tn.name) {
709 found_in_index = true;
710 if typedef.category() == metadata::reader::TypeCategory::Enum {
711 for field in typedef.fields() {
712 if field.flags().contains(metadata::FieldAttributes::Literal)
713 && let Some(constant) = field.constant()
714 {
715 let matches = match constant.value() {
716 metadata::Value::I32(v) => v == inner_i32,
717 metadata::Value::U32(v) => v == inner_i32 as u32,
719 _ => false,
720 };
721 if matches {
722 let variant = write_ident(field.name());
723 return Ok(quote! { #variant });
724 }
725 }
726 }
727
728 let has_flags = typedef.attributes().any(|attr| {
729 attr.name() == "FlagsAttribute" && attr.ctor().parent().namespace() == "System"
730 });
731
732 if has_flags
733 && let Some(flags_ts) = write_flags_combination(namespace, &typedef, inner_i32)
734 {
735 return Ok(flags_ts);
736 }
737 }
738 }
739
740 if !found_in_index {
741 return Err(writer_err!(
742 "enum type `{}::{}` not found in the metadata index; ensure the winmd file that defines it is included",
743 tn.namespace,
744 tn.name
745 ));
746 }
747
748 Ok(write_value(namespace, inner))
749}
750
751fn write_flags_combination(
752 _namespace: &str,
753 typedef: &metadata::reader::TypeDef,
754 value: i32,
755) -> Option<TokenStream> {
756 let mut fields: Vec<(String, i32)> = typedef
757 .fields()
758 .filter_map(|field| {
759 if !field.flags().contains(metadata::FieldAttributes::Literal) {
760 return None;
761 }
762 let constant = field.constant()?;
763 let v = match constant.value() {
764 metadata::Value::I32(v) => v,
765 metadata::Value::U32(v) => v as i32,
766 _ => return None,
767 };
768 if v == 0 {
769 None
770 } else {
771 Some((field.name().to_string(), v))
772 }
773 })
774 .collect();
775
776 fields.sort_by_key(|b| std::cmp::Reverse(b.1 as u32));
778
779 let mut remaining = value;
780 let mut components: Vec<String> = Vec::new();
781
782 for (name, v) in &fields {
783 if remaining == 0 {
784 break;
785 }
786 if (remaining & v) == *v {
787 remaining &= !v;
788 components.push(name.clone());
789 }
790 }
791
792 if remaining != 0 || components.is_empty() {
793 return None;
794 }
795
796 let mut iter = components.iter();
797 let first = write_ident(iter.next().unwrap());
798 let result = iter.fold(first, |acc, name| {
799 let variant = write_ident(name);
800 quote! { #acc | #variant }
801 });
802
803 Some(result)
804}
805
806pub(super) fn write_arch_attr(arches: i32) -> TokenStream {
808 if arches == 0 {
809 return quote! {};
810 }
811
812 let mut parts: Vec<TokenStream> = vec![];
813 if arches & 1 != 0 {
814 parts.push(quote! { X86 });
815 }
816 if arches & 2 != 0 {
817 parts.push(quote! { X64 });
818 }
819 if arches & 4 != 0 {
820 parts.push(quote! { Arm64 });
821 }
822
823 if parts.is_empty() {
824 return quote! {};
825 }
826
827 let value = parts
828 .iter()
829 .skip(1)
830 .fold(parts[0].clone(), |acc, p| quote! { #acc | #p });
831
832 quote! { #[arch(#value)] }
833}
834
835fn write_type_def(item: &metadata::reader::TypeDef) -> Result<TokenStream, Error> {
836 match item.category() {
837 metadata::reader::TypeCategory::Struct => Ok(quote! {}),
838 metadata::reader::TypeCategory::Enum => write_enum(item),
839 metadata::reader::TypeCategory::Interface => write_interface(item),
840 metadata::reader::TypeCategory::Class => write_class(item),
841 metadata::reader::TypeCategory::Delegate => {
842 if item
843 .flags()
844 .contains(metadata::TypeAttributes::WindowsRuntime)
845 {
846 write_delegate(item)
847 } else {
848 write_callback(item)
849 }
850 }
851 metadata::reader::TypeCategory::Attribute => write_attribute(item),
852 }
853}
854
855fn write_type_ref(namespace: &str, item: &metadata::reader::TypeDefOrRef) -> TokenStream {
856 write_type(
857 namespace,
858 &metadata::Type::class_named(item.namespace(), item.name()),
859 )
860}
861
862fn extract_guid_from_attribute(
863 attr: metadata::reader::Attribute,
864) -> Result<(u32, u16, u16, [u8; 8]), Error> {
865 let values: Vec<_> = attr.value().into_iter().map(|(_, v)| v).collect();
866 if values.len() != 11 {
867 return Err(writer_err!(
868 "GuidAttribute must have exactly 11 arguments, got {}",
869 values.len()
870 ));
871 }
872 let d1 = match values[0] {
873 metadata::Value::U32(v) => v,
874 ref v => return Err(writer_err!("GuidAttribute d1: expected U32, got {v:?}")),
875 };
876 let d2 = match values[1] {
877 metadata::Value::U16(v) => v,
878 ref v => return Err(writer_err!("GuidAttribute d2: expected U16, got {v:?}")),
879 };
880 let d3 = match values[2] {
881 metadata::Value::U16(v) => v,
882 ref v => return Err(writer_err!("GuidAttribute d3: expected U16, got {v:?}")),
883 };
884 let mut d4 = [0u8; 8];
885 for i in 0..8 {
886 d4[i] = match values[3 + i] {
887 metadata::Value::U8(v) => v,
888 ref v => return Err(writer_err!("GuidAttribute d4[{i}]: expected U8, got {v:?}")),
889 };
890 }
891 Ok((d1, d2, d3, d4))
892}
893
894enum GuidOutput {
895 Omit,
896 Explicit(u32, u16, u16, [u8; 8]),
897 None,
898}
899
900fn guid_output(
902 item: &metadata::reader::TypeDef,
903 methods: &[(&str, &[metadata::Type], &metadata::Type)],
904) -> Result<GuidOutput, Error> {
905 let Some(attr) = item.find_attribute("GuidAttribute") else {
906 return Ok(GuidOutput::None);
907 };
908 let stored = extract_guid_from_attribute(attr)?;
909 let s = reader::guid::build_interface_string(
910 item.namespace(),
911 metadata::trim_tick(item.name()),
912 methods,
913 );
914 let derived = reader::guid::guid_from_interface_string(&s);
915 if stored == derived {
916 Ok(GuidOutput::Omit)
917 } else {
918 Ok(GuidOutput::Explicit(stored.0, stored.1, stored.2, stored.3))
919 }
920}
921
922fn interface_guid_output(
923 item: &metadata::reader::TypeDef,
924 generics: &[metadata::Type],
925) -> Result<GuidOutput, Error> {
926 let sigs: Vec<(String, Vec<metadata::Type>, metadata::Type)> = item
927 .methods()
928 .map(|m| {
929 let sig = m.signature(generics);
930 (m.name().to_string(), sig.types, sig.return_type)
931 })
932 .collect();
933 let methods: Vec<(&str, &[metadata::Type], &metadata::Type)> = sigs
934 .iter()
935 .map(|(n, t, r)| (n.as_str(), t.as_slice(), r))
936 .collect();
937 guid_output(item, &methods)
938}
939
940fn delegate_guid_output(
941 item: &metadata::reader::TypeDef,
942 generics: &[metadata::Type],
943) -> Result<GuidOutput, Error> {
944 let (types, return_type) = item.methods().find(|m| m.name() == "Invoke").map_or_else(
945 || (vec![], metadata::Type::Void),
946 |invoke| {
947 let sig = invoke.signature(generics);
948 (sig.types, sig.return_type)
949 },
950 );
951 guid_output(item, &[("Invoke", types.as_slice(), &return_type)])
952}
953
954fn read_unmanaged_abi(item: &metadata::reader::TypeDef) -> Option<i32> {
956 item.find_attribute("UnmanagedFunctionPointerAttribute")
957 .and_then(|attribute| attribute.value().into_iter().next())
958 .and_then(|(_, v)| {
959 if let metadata::Value::EnumValue(_, value) = v
960 && let metadata::Value::I32(n) = *value
961 {
962 return Some(n);
963 }
964 None
965 })
966}
967
968fn write_generic_params(item: &metadata::reader::TypeDef) -> (Vec<metadata::Type>, TokenStream) {
969 let types: Vec<_> = item
970 .generic_params()
971 .map(|param| metadata::Type::Generic(param.name().to_string(), param.sequence()))
972 .collect();
973 let tokens = if types.is_empty() {
974 quote! {}
975 } else {
976 let names = item.generic_params().map(|param| write_ident(param.name()));
977 quote! { <#(#names),*> }
978 };
979 (types, tokens)
980}