Skip to main content

windows_bindgen/
lib.rs

1#![doc = include_str!("../readme.md")]
2
3mod cli;
4mod config;
5mod derive;
6mod derive_writer;
7mod filter;
8mod format;
9mod guid;
10mod implements;
11mod io;
12mod package_writer;
13mod param;
14mod paths;
15mod references;
16mod signature;
17mod tables;
18mod tokens;
19mod type_map;
20mod type_name;
21mod type_tree;
22mod types;
23mod value;
24mod winmd;
25
26pub use cli::bindgen;
27use config::*;
28use derive::*;
29use derive_writer::*;
30use filter::*;
31use guid::*;
32use implements::*;
33use io::*;
34use package_writer::*;
35use param::*;
36use references::*;
37use signature::*;
38use std::cmp::Ordering;
39use std::collections::*;
40use std::fmt::Write;
41use std::path::{Path, PathBuf};
42use tables::*;
43use tokens::*;
44use type_map::*;
45use type_name::*;
46use type_tree::*;
47use types::*;
48use value::*;
49use winmd::*;
50mod filter_parser;
51mod method_names;
52mod type_closure;
53use method_names::*;
54use type_closure::*;
55
56fn report_timing(output: &Path, phase: &str, elapsed: std::time::Duration) {
57    if std::env::var_os("WINDOWS_BINDGEN_TIMINGS").is_some() {
58        eprintln!(
59            "windows-bindgen timing `{}` {phase}: {:.3} ms",
60            output.display(),
61            elapsed.as_secs_f64() * 1_000.0
62        );
63    }
64}
65
66/// Creates a new [`Bindgen`] builder for generating Windows API bindings.
67pub fn builder() -> Bindgen {
68    Bindgen::new()
69}
70
71/// Builder for generating Windows API bindings.
72///
73/// This is the fluent alternative to [`bindgen`].
74///
75/// # Example
76///
77/// ```rust,no_run
78/// windows_bindgen::Bindgen::new()
79///     .output("src/bindings.rs")
80///     .filter("GetTickCount")
81///     .write();
82/// ```
83#[derive(Default)]
84pub struct Bindgen {
85    input: Vec<Input>,
86    input_default: bool,
87    filter: Vec<String>,
88    output: PathBuf,
89    derive: Vec<String>,
90    implement: Option<Vec<String>>,
91    compose: Vec<String>,
92    rustfmt: Option<String>,
93    layout: Layout,
94    style: Style,
95    dead_code: bool,
96}
97
98enum Input {
99    Path(PathBuf),
100    Bytes(Vec<u8>),
101}
102
103/// Output layout for the generated bindings.
104#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
105enum Layout {
106    /// One Rust module per metadata namespace (the default).
107    #[default]
108    Modules,
109    /// A single flat list of items (no namespace modules).
110    Flat,
111    /// One file per namespace + `Cargo.toml` features.
112    Package,
113}
114
115impl Layout {
116    fn is_flat(self) -> bool {
117        matches!(self, Self::Flat)
118    }
119    fn is_package(self) -> bool {
120        matches!(self, Self::Package)
121    }
122}
123
124/// Code-style mode for the generated bindings.
125#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
126enum Style {
127    /// Full-fidelity bindings (the default).
128    #[default]
129    Default,
130    /// Raw / sys-style bindings.
131    Sys {
132        /// When `true`, emit `extern { fn ... }` instead of `link!` macros.
133        extern_fns: bool,
134    },
135    /// Minimal-mode bindings (drop class wrappers, inherited forwarders,
136    /// handle ergonomics; auto-revoke events).
137    Minimal,
138}
139
140impl Style {
141    fn is_sys(self) -> bool {
142        matches!(self, Self::Sys { .. })
143    }
144    fn is_minimal(self) -> bool {
145        matches!(self, Self::Minimal)
146    }
147    fn sys_fn_extern(self) -> bool {
148        matches!(self, Self::Sys { extern_fns: true })
149    }
150
151    /// Minimal bindings use the class's default interface directly.
152    fn emit_class_methods(self) -> bool {
153        !self.is_minimal()
154    }
155
156    /// Minimal bindings require casting to the interface that owns an inherited method.
157    fn emit_inherited_forwarders(self) -> bool {
158        !self.is_minimal()
159    }
160
161    /// Minimal bindings require casting inherited iterables to `IIterable<T>`.
162    fn emit_iterable_into_iterator(self) -> bool {
163        !self.is_minimal()
164    }
165
166    /// Minimal bindings expose input strings as `&str`.
167    fn minimal_string_input(self, param: &Param) -> bool {
168        self.is_minimal() && param.is_input_only() && matches!(param.ty, Type::String)
169    }
170
171    /// Minimal bindings return strings as `String`.
172    fn minimal_string_return(self, ty: &Type) -> bool {
173        self.is_minimal() && matches!(ty, Type::String)
174    }
175
176    /// Sys bindings omit standard derives beyond `Copy` and `Clone`.
177    fn derive_std_traits(self) -> bool {
178        !self.is_sys()
179    }
180
181    /// Sys bindings omit traits that require `windows-core`.
182    fn emit_core_traits(self) -> bool {
183        !self.is_sys()
184    }
185
186    /// Whether handle structs are emitted as bare aliases rather than newtypes.
187    fn emit_bare_typedef(self) -> bool {
188        self.is_sys() || self.is_minimal()
189    }
190}
191
192impl Bindgen {
193    /// Creates a new builder with default options.
194    pub fn new() -> Self {
195        Self::default()
196    }
197
198    /// Adds a `.winmd` file or directory.
199    pub fn input(&mut self, input: impl AsRef<Path>) -> &mut Self {
200        self.input.push(Input::Path(input.as_ref().to_path_buf()));
201        self
202    }
203
204    /// Adds the default Windows metadata.
205    pub fn input_default(&mut self) -> &mut Self {
206        self.input_default = true;
207        self
208    }
209
210    /// Adds a `.winmd` file from memory.
211    pub fn input_bytes(&mut self, input: &[u8]) -> &mut Self {
212        self.input.push(Input::Bytes(input.to_vec()));
213        self
214    }
215
216    /// Adds `.winmd` files from memory.
217    pub fn input_byte_sets<I, B>(&mut self, inputs: I) -> &mut Self
218    where
219        I: IntoIterator<Item = B>,
220        B: AsRef<[u8]>,
221    {
222        for input in inputs {
223            self.input_bytes(input.as_ref());
224        }
225        self
226    }
227
228    /// Adds `.winmd` files or directories.
229    pub fn inputs<I, S>(&mut self, inputs: I) -> &mut Self
230    where
231        I: IntoIterator<Item = S>,
232        S: AsRef<Path>,
233    {
234        for input in inputs {
235            self.input(input);
236        }
237        self
238    }
239
240    /// Sets the generated Rust file.
241    pub fn output(&mut self, output: impl AsRef<Path>) -> &mut Self {
242        self.output = output.as_ref().to_path_buf();
243        self
244    }
245
246    /// Add a filter rule to include or exclude APIs.
247    ///
248    /// Filter rules may be a function or type name, a namespace prefix, a fully-qualified name,
249    /// or a method-level entry of the form `Namespace.Type::Method` (with optional `Property` /
250    /// `Event` sugar). Prefix with `!` to exclude rather than include. See the crate-level
251    /// docs for the full grammar.
252    pub fn filter(&mut self, filter: &str) -> &mut Self {
253        self.filters(std::iter::once(filter))
254    }
255
256    /// Adds filter rules from a text file.
257    #[track_caller]
258    pub fn filter_file(&mut self, input: impl AsRef<Path>) -> &mut Self {
259        self.filters(cli::read_tokens(input))
260    }
261
262    /// Adds filter rules from text files.
263    #[track_caller]
264    pub fn filter_files<I, S>(&mut self, inputs: I) -> &mut Self
265    where
266        I: IntoIterator<Item = S>,
267        S: AsRef<Path>,
268    {
269        for input in inputs {
270            self.filter_file(input);
271        }
272        self
273    }
274
275    /// Add multiple filter rules to include or exclude APIs.
276    ///
277    /// Filter rules may be a function or type name, a namespace prefix, a fully-qualified name,
278    /// or a method-level entry of the form `Namespace.Type::Method` (with optional `Property` /
279    /// `Event` sugar). Prefix with `!` to exclude rather than include. See the crate-level
280    /// docs for the full grammar.
281    pub fn filters<I, S>(&mut self, filters: I) -> &mut Self
282    where
283        I: IntoIterator<Item = S>,
284        S: AsRef<str>,
285    {
286        for filter in filters {
287            self.filter.push(filter.as_ref().to_string());
288        }
289        self
290    }
291
292    /// Add an extra trait for types to derive.
293    pub fn derive(&mut self, derive: &str) -> &mut Self {
294        self.derives(std::iter::once(derive))
295    }
296
297    /// Add multiple extra traits for types to derive.
298    pub fn derives<I, S>(&mut self, derives: I) -> &mut Self
299    where
300        I: IntoIterator<Item = S>,
301        S: AsRef<str>,
302    {
303        for derive in derives {
304            self.derive.push(derive.as_ref().to_string());
305        }
306        self
307    }
308
309    /// Override the default Rust formatter path.
310    pub fn rustfmt(&mut self, rustfmt: &str) -> &mut Self {
311        self.rustfmt = Some(rustfmt.to_string());
312        self
313    }
314
315    /// Avoid the default namespace-to-module conversion.
316    #[track_caller]
317    pub fn flat(&mut self) -> &mut Self {
318        if matches!(self.layout, Layout::Package) {
319            panic!("cannot combine `--package` and `--flat`");
320        }
321        self.layout = Layout::Flat;
322        self
323    }
324
325    fn uses_inline_core_types(&self) -> bool {
326        self.style.is_sys() && !self.layout.is_package()
327    }
328
329    /// Generate bindings as a package with one file per namespace.
330    #[track_caller]
331    pub fn package(&mut self) -> &mut Self {
332        if matches!(self.layout, Layout::Flat) {
333            panic!("cannot combine `--package` and `--flat`");
334        }
335        self.layout = Layout::Package;
336        self
337    }
338
339    /// Includes implementation traits for every WinRT interface in scope.
340    #[track_caller]
341    pub fn implement_all(&mut self) -> &mut Self {
342        match &self.implement {
343            None => self.implement = Some(vec![]),
344            Some(names) if names.is_empty() => {}
345            Some(_) => panic!("cannot combine `implement_all` with selected implementations"),
346        }
347        self
348    }
349
350    /// Includes implementation traits for a WinRT interface or namespace prefix.
351    ///
352    /// The name may be a fully-qualified type name (`Namespace.Name`) or a namespace prefix that
353    /// matches every type defined under it.
354    #[track_caller]
355    pub fn implement(&mut self, name: &str) -> &mut Self {
356        assert!(
357            !self.implement.as_ref().is_some_and(Vec::is_empty),
358            "cannot combine selected implementations with `implement_all`"
359        );
360        self.implement
361            .get_or_insert_with(Vec::new)
362            .push(name.to_string());
363        self
364    }
365
366    /// Includes implementation traits for multiple WinRT interfaces or namespace prefixes.
367    pub fn implements<I, S>(&mut self, names: I) -> &mut Self
368    where
369        I: IntoIterator<Item = S>,
370        S: AsRef<str>,
371    {
372        for name in names {
373            self.implement(name.as_ref());
374        }
375        self
376    }
377
378    /// Selects a composable WinRT class as a minimal-mode composition target.
379    ///
380    /// The class and its composable factory methods must also be selected by a filter.
381    #[track_caller]
382    pub fn compose(&mut self, name: &str) -> &mut Self {
383        assert!(
384            name.rsplit_once('.')
385                .is_some_and(|(namespace, name)| !namespace.is_empty() && !name.is_empty()),
386            "`compose` requires a fully qualified class name"
387        );
388        self.compose.push(name.to_string());
389        self
390    }
391
392    /// Selects multiple composable WinRT classes as minimal-mode composition targets.
393    pub fn composes<I, S>(&mut self, names: I) -> &mut Self
394    where
395        I: IntoIterator<Item = S>,
396        S: AsRef<str>,
397    {
398        for name in names {
399            self.compose(name.as_ref());
400        }
401        self
402    }
403
404    /// Generate raw or sys-style Rust bindings.
405    ///
406    /// Mutually exclusive with [`Bindgen::minimal`]; panics if `minimal` was
407    /// already selected.
408    #[track_caller]
409    pub fn sys(&mut self) -> &mut Self {
410        let extern_fns = matches!(self.style, Style::Sys { extern_fns: true });
411        if matches!(self.style, Style::Minimal) {
412            panic!("cannot combine `--sys` and `--minimal`");
413        }
414        self.style = Style::Sys { extern_fns };
415        self
416    }
417
418    /// Generate minimal-mode Rust bindings.
419    ///
420    /// Drops per-class wrapper methods, inherited interface forwarders, handle
421    /// ergonomics, and free-function wrappers.
422    ///
423    /// Mutually exclusive with `--sys`.
424    #[track_caller]
425    pub fn minimal(&mut self) -> &mut Self {
426        if matches!(self.style, Style::Sys { .. }) {
427            panic!("cannot combine `--sys` and `--minimal`");
428        }
429        self.style = Style::Minimal;
430        self
431    }
432
433    /// Generate `extern` declarations rather than `link!` macros for sys-style Rust bindings.
434    ///
435    /// Only valid in combination with [`Bindgen::sys`]; panics otherwise.
436    #[track_caller]
437    pub fn extern_fns(&mut self) -> &mut Self {
438        match &mut self.style {
439            Style::Sys { extern_fns } => *extern_fns = true,
440            _ => panic!("`--extern` requires `--sys`"),
441        }
442        self
443    }
444
445    /// Emit `pub(crate)` instead of `pub` on generated items to surface unused
446    /// bindings as dead-code warnings.
447    pub fn dead_code(&mut self) -> &mut Self {
448        self.dead_code = true;
449        self
450    }
451
452    /// Generate the bindings.
453    #[track_caller]
454    pub fn write(&self) {
455        let total = std::time::Instant::now();
456
457        // Validate before setting up reader and reference state.
458        assert!(
459            !self.output.as_os_str().is_empty(),
460            "output is required (call `.output()` or pass `--out`)"
461        );
462        assert!(
463            self.compose.is_empty() || self.style.is_minimal(),
464            "`compose` requires `minimal`"
465        );
466
467        let mut include: Vec<&str> = vec![];
468        let mut exclude: Vec<&str> = vec![];
469
470        for f in &self.filter {
471            if let Some(rest) = f.strip_prefix('!') {
472                exclude.push(rest);
473            } else {
474                include.push(f.as_str());
475            }
476        }
477
478        assert!(!include.is_empty(), "at least one `--filter` required");
479
480        let sys = self.style.is_sys();
481        let link = if sys { "windows_link" } else { "windows_core" };
482
483        let phase = std::time::Instant::now();
484        let reader_storage;
485        let reader = if self.input.is_empty() {
486            default_reader()
487        } else {
488            reader_storage = Reader::new(expand_input(&self.input, self.input_default));
489            &reader_storage
490        };
491        report_timing(&self.output, "metadata", phase.elapsed());
492
493        let phase = std::time::Instant::now();
494        let mut references: Vec<ReferenceStage> = Vec::new();
495
496        if !sys {
497            // Register implicit references to sibling windows-* crates present in metadata.
498            for (probe_namespace, crate_name, paths) in [
499                (
500                    "Windows.Foundation",
501                    "windows_future",
502                    &[
503                        "Windows.Foundation.AsyncActionCompletedHandler",
504                        "Windows.Foundation.AsyncActionProgressHandler",
505                        "Windows.Foundation.AsyncActionWithProgressCompletedHandler",
506                        "Windows.Foundation.AsyncOperationCompletedHandler",
507                        "Windows.Foundation.AsyncOperationProgressHandler",
508                        "Windows.Foundation.AsyncOperationWithProgressCompletedHandler",
509                        "Windows.Foundation.AsyncStatus",
510                        "Windows.Foundation.IAsyncAction",
511                        "Windows.Foundation.IAsyncActionWithProgress",
512                        "Windows.Foundation.IAsyncInfo",
513                        "Windows.Foundation.IAsyncOperation",
514                        "Windows.Foundation.IAsyncOperationWithProgress",
515                    ][..],
516                ),
517                (
518                    "Windows.Foundation.Collections",
519                    "windows_collections",
520                    &[
521                        "Windows.Foundation.Collections.CollectionChange",
522                        "Windows.Foundation.Collections.IIterable",
523                        "Windows.Foundation.Collections.IIterator",
524                        "Windows.Foundation.Collections.IKeyValuePair",
525                        "Windows.Foundation.Collections.IMap",
526                        "Windows.Foundation.Collections.IMapChangedEventArgs",
527                        "Windows.Foundation.Collections.IMapView",
528                        "Windows.Foundation.Collections.IObservableMap",
529                        "Windows.Foundation.Collections.IObservableVector",
530                        "Windows.Foundation.Collections.IVector",
531                        "Windows.Foundation.Collections.IVectorChangedEventArgs",
532                        "Windows.Foundation.Collections.IVectorView",
533                        "Windows.Foundation.Collections.MapChangedEventHandler",
534                        "Windows.Foundation.Collections.VectorChangedEventHandler",
535                    ][..],
536                ),
537                (
538                    "Windows.Foundation",
539                    "windows_reference",
540                    &["Windows.Foundation.IReference"][..],
541                ),
542                (
543                    "Windows.Foundation",
544                    "windows_time",
545                    &["Windows.Foundation.DateTime", "Windows.Foundation.TimeSpan"][..],
546                ),
547                (
548                    "Windows.Foundation.Numerics",
549                    "windows_numerics",
550                    &[
551                        "Windows.Foundation.Numerics.Matrix3x2",
552                        "Windows.Foundation.Numerics.Matrix4x4",
553                        "Windows.Foundation.Numerics.Vector2",
554                        "Windows.Foundation.Numerics.Vector3",
555                        "Windows.Foundation.Numerics.Vector4",
556                    ][..],
557                ),
558            ] {
559                if reader.contains_key(probe_namespace) {
560                    let filtered: Vec<&str> = paths
561                        .iter()
562                        .copied()
563                        .filter(|path| {
564                            if let Some((namespace, name)) = path.rsplit_once('.')
565                                && let Some(ns_map) = reader.get(namespace)
566                            {
567                                return ns_map.contains_key(name);
568                            }
569                            false
570                        })
571                        .collect();
572                    if !filtered.is_empty() {
573                        prepend_default_refs(&mut references, crate_name, &filtered);
574                    }
575                }
576            }
577        }
578
579        let derive_str: Vec<&str> = self.derive.iter().map(|s| s.as_str()).collect();
580        let implements = self.implement.as_ref().map(|names| {
581            let names_str: Vec<&str> = names.iter().map(|s| s.as_str()).collect();
582            Implements::new(&names_str)
583        });
584
585        let references = References::new(reader, references);
586        report_timing(&self.output, "references", phase.elapsed());
587
588        let phase = std::time::Instant::now();
589        let (filter, types) = {
590            let mut all_parsed = Vec::new();
591            for entry in &include {
592                all_parsed.extend(filter_parser::parse_filter_entry(entry));
593            }
594            for entry in &exclude {
595                let mut entries = filter_parser::parse_filter_entry(entry);
596                for e in &mut entries {
597                    e.exclude = true;
598                }
599                all_parsed.extend(entries);
600            }
601            let resolved = filter_parser::resolve_entries(reader, &all_parsed);
602
603            let mut filter = Filter::from_resolved(reader, &resolved);
604
605            // Precise filters use bottom-up closure; broad filters and packages scan top-down.
606            let types = if !filter.has_broad_filter && !self.layout.is_package() {
607                filter.uses_closure = true;
608                TypeClosure::build(reader, &mut filter, &references, implements.as_ref())
609            } else {
610                TypeMap::filter(reader, &filter, &references, self.style.is_sys())
611            };
612
613            (filter, types)
614        };
615        report_timing(&self.output, "selection", phase.elapsed());
616
617        let phase = std::time::Instant::now();
618        let derive = Derive::new(reader, &types, &derive_str);
619        if let Some(implements) = &implements {
620            filter.validate_implements(implements);
621        }
622
623        let event_only_delegates = compute_event_only_delegates(&types, reader);
624
625        let config = Config {
626            bindgen: self,
627            reader,
628            types: &types,
629            references: &references,
630            filter: &filter,
631            derive: &derive,
632            implement: implements.as_ref(),
633            link,
634            namespace: "",
635            event_only_delegates: &event_only_delegates,
636            self_ty: None,
637            self_generics: Vec::new(),
638            prunable: std::sync::Arc::new(BTreeSet::new()),
639        };
640        let filter_config = Config {
641            implement: None,
642            ..config.clone()
643        };
644
645        for target in &self.compose {
646            let class = composition_target(reader, target);
647            assert!(
648                types.contains_key(&class.type_name()),
649                "composition target `{target}` is not selected by the filter"
650            );
651            assert!(
652                class
653                    .required_interfaces(reader)
654                    .iter()
655                    .filter(|interface| interface.kind == InterfaceKind::Composable)
656                    .filter(|interface| types.contains_key(&interface.def.type_name()))
657                    .any(|interface| interface
658                        .get_methods(&filter_config)
659                        .iter()
660                        .any(|method| matches!(method, MethodOrName::Method(_)))),
661                "composition target `{target}` has no composable factory method selected by the \
662                 filter"
663            );
664        }
665
666        let tree = TypeTree::new(&types);
667        report_timing(&self.output, "planning", phase.elapsed());
668        config.write(tree);
669        report_timing(&self.output, "total", total.elapsed());
670    }
671}
672
673#[track_caller]
674fn composition_target(reader: &Reader, target: &str) -> Class {
675    let (namespace, name) = target.rsplit_once('.').unwrap();
676    let class = reader
677        .with_full_name(namespace, name)
678        .find_map(|ty| match ty {
679            Type::Class(class) => Some(class),
680            _ => None,
681        })
682        .unwrap_or_else(|| panic!("composition target `{target}` is not a WinRT class"));
683    assert!(
684        class
685            .required_interfaces(reader)
686            .iter()
687            .any(|interface| interface.kind == InterfaceKind::Composable),
688        "composition target `{target}` is not composable"
689    );
690    class
691}
692
693fn default_reader() -> &'static Reader {
694    static READER: std::sync::OnceLock<Reader> = std::sync::OnceLock::new();
695    READER.get_or_init(|| Reader::new(default_input()))
696}
697
698#[track_caller]
699fn default_input() -> Vec<File> {
700    [windows_default::WINRT, windows_default::WIN32]
701        .into_iter()
702        .map(|bytes| File::new(bytes.to_vec()).unwrap())
703        .collect()
704}
705
706fn expand_input(input: &[Input], input_default: bool) -> Vec<File> {
707    #[track_caller]
708    fn expand_path(result: &mut Vec<File>, path: &Path) {
709        if path.is_dir() {
710            let mut paths = vec![];
711
712            for path in path
713                .read_dir()
714                .unwrap_or_else(|_| panic!("failed to read directory `{}`", path.display()))
715                .flatten()
716                .map(|entry| entry.path())
717            {
718                if path.is_file()
719                    && path
720                        .extension()
721                        .is_some_and(|extension| extension.eq_ignore_ascii_case("winmd"))
722                {
723                    paths.push(path);
724                }
725            }
726
727            assert!(
728                !paths.is_empty(),
729                "failed to find .winmd files in directory `{}`",
730                path.display()
731            );
732
733            for path in paths {
734                let bytes = std::fs::read(&path)
735                    .unwrap_or_else(|_| panic!("failed to read binary file `{}`", path.display()));
736                let file = File::new(bytes)
737                    .unwrap_or_else(|| panic!("failed to read .winmd format `{}`", path.display()));
738                result.push(file);
739            }
740        } else {
741            let Ok(bytes) = std::fs::read(path) else {
742                panic!("failed to read binary file `{}`", path.display());
743            };
744            let Some(file) = File::new(bytes) else {
745                panic!("failed to read .winmd format `{}`", path.display());
746            };
747            result.push(file);
748        }
749    }
750
751    let mut result = if input_default {
752        default_input()
753    } else {
754        vec![]
755    };
756
757    for input in input {
758        match input {
759            Input::Path(path) => expand_path(&mut result, path),
760            Input::Bytes(bytes) => result.push(
761                File::new(bytes.clone())
762                    .unwrap_or_else(|| panic!("failed to read .winmd format from memory")),
763            ),
764        }
765    }
766
767    result
768}
769
770/// Finds delegates used only as event-handler parameters.
771fn compute_event_only_delegates(types: &TypeMap, reader: &Reader) -> HashSet<TypeName> {
772    let mut event_delegates: HashSet<TypeName> = HashSet::new();
773    let mut non_event_delegates: HashSet<TypeName> = HashSet::new();
774
775    for type_set in types.values() {
776        for ty in type_set {
777            let (methods, generics): (Box<dyn Iterator<Item = MethodDef>>, &[Type]) = match ty {
778                Type::Interface(i) => (Box::new(i.def.methods()), &i.generics),
779                _ => continue,
780            };
781
782            for method in methods {
783                let is_event_add = method.flags().contains(MethodAttributes::SpecialName)
784                    && method.name().starts_with("add_");
785
786                let sig = method.method_signature(generics, reader);
787                for param in &sig.params {
788                    if let Type::Delegate(d) = &param.ty {
789                        if is_event_add {
790                            event_delegates.insert(d.type_name());
791                        } else {
792                            non_event_delegates.insert(d.type_name());
793                        }
794                    }
795                }
796            }
797        }
798    }
799
800    event_delegates
801        .difference(&non_event_delegates)
802        .copied()
803        .collect()
804}
805
806fn namespace_starts_with(namespace: &str, starts_with: &str) -> bool {
807    namespace.starts_with(starts_with)
808        && (namespace.len() == starts_with.len()
809            || namespace.as_bytes().get(starts_with.len()) == Some(&b'.'))
810}
811
812/// Collapses private per-header Win32 package namespaces to the public umbrella.
813fn flat_module_namespace(namespace: &str) -> &str {
814    const UMBRELLA: &str = "Windows.Win32";
815    if namespace.len() > UMBRELLA.len()
816        && namespace.starts_with(UMBRELLA)
817        && namespace.as_bytes()[UMBRELLA.len()] == b'.'
818    {
819        return UMBRELLA;
820    }
821    namespace
822}
823
824/// Derives the cargo-feature name for a `--package` namespace.
825fn namespace_feature(namespace: &str) -> String {
826    if let Some(stem) = namespace.strip_prefix("Windows.Win32.") {
827        stem.replace('.', "_")
828    } else if let Some((_, rest)) = namespace.split_once('.') {
829        rest.replace('.', "_")
830    } else {
831        namespace.to_string()
832    }
833}
834
835/// Prepend reference entries so they take precedence.
836fn prepend_default_refs(refs: &mut Vec<ReferenceStage>, crate_name: &str, paths: &[&str]) {
837    refs.splice(
838        0..0,
839        paths
840            .iter()
841            .rev()
842            .map(|path| ReferenceStage::new(crate_name, path)),
843    );
844}
845
846#[cfg(test)]
847mod tests {
848    use super::*;
849
850    #[test]
851    fn test_starts_with() {
852        assert!(namespace_starts_with(
853            "Windows.Win32.Graphics.Direct3D11on12",
854            "Windows.Win32.Graphics.Direct3D11on12"
855        ));
856        assert!(namespace_starts_with(
857            "Windows.Win32.Graphics.Direct3D11on12",
858            "Windows.Win32.Graphics"
859        ));
860        assert!(!namespace_starts_with(
861            "Windows.Win32.Graphics.Direct3D11on12",
862            "Windows.Win32.Graphics.Direct3D11"
863        ));
864        assert!(!namespace_starts_with(
865            "Windows.Win32.Graphics.Direct3D",
866            "Windows.Win32.Graphics.Direct3D11"
867        ));
868    }
869
870    #[test]
871    fn default_metadata_reader_is_reused() {
872        assert!(std::ptr::eq(default_reader(), default_reader()));
873    }
874
875    #[test]
876    fn implementation_selection() {
877        let mut builder = Bindgen::new();
878        builder
879            .implement("Test.IFirst")
880            .implements(["Test.ISecond", "Other"]);
881        assert_eq!(
882            builder.implement,
883            Some(vec![
884                "Test.IFirst".to_string(),
885                "Test.ISecond".to_string(),
886                "Other".to_string()
887            ])
888        );
889
890        let mut builder = Bindgen::new();
891        builder.implement_all().implement_all();
892        assert_eq!(builder.implement, Some(vec![]));
893
894        let mut builder = Bindgen::new();
895        builder.implements(std::iter::empty::<&str>());
896        assert_eq!(builder.implement, None);
897    }
898
899    #[test]
900    fn composition_selection() {
901        let mut builder = Bindgen::new();
902        builder
903            .compose("Test.First")
904            .composes(["Test.Second", "Other.Third"]);
905        assert_eq!(
906            builder.compose,
907            vec![
908                "Test.First".to_string(),
909                "Test.Second".to_string(),
910                "Other.Third".to_string()
911            ]
912        );
913    }
914
915    #[test]
916    #[should_panic(expected = "cannot combine selected implementations with `implement_all`")]
917    fn implementation_after_all_panics() {
918        Bindgen::new().implement_all().implement("Test.IFirst");
919    }
920
921    #[test]
922    #[should_panic(expected = "cannot combine `implement_all` with selected implementations")]
923    fn implementation_all_after_selection_panics() {
924        Bindgen::new().implement("Test.IFirst").implement_all();
925    }
926}