1#![doc = include_str!("../readme.md")]
2#![allow(
3 non_upper_case_globals,
4 clippy::enum_variant_names,
5 clippy::upper_case_acronyms,
6 clippy::needless_doctest_main
7)]
8
9mod derive;
10mod derive_writer;
11mod filter;
12mod guid;
13mod index;
14mod io;
15mod libraries;
16mod param;
17mod references;
18mod signature;
19mod tables;
20mod tokens;
21mod type_map;
22mod type_name;
23mod type_tree;
24mod types;
25mod value;
26mod warnings;
27mod winmd;
28mod writer;
29
30use derive::*;
31use derive_writer::*;
32use filter::*;
33use guid::*;
34use io::*;
35pub use libraries::*;
36use param::*;
37use references::*;
38use signature::*;
39use std::cmp::Ordering;
40use std::collections::*;
41use std::fmt::Write;
42use tables::*;
43use tokens::*;
44use type_map::*;
45use type_name::*;
46use type_tree::*;
47use types::*;
48use value::*;
49pub use warnings::*;
50use winmd::*;
51use writer::*;
52mod method_names;
53use method_names::*;
54
55struct Config {
56 pub types: TypeMap,
57 pub references: References,
58 pub output: String,
59 pub flat: bool,
60 pub no_allow: bool,
61 pub no_comment: bool,
62 pub no_deps: bool,
63 pub no_toml: bool,
64 pub package: bool,
65 pub rustfmt: String,
66 pub sys: bool,
67 pub implement: bool,
68 pub derive: Derive,
69 pub link: String,
70 pub warnings: WarningBuilder,
71}
72
73#[track_caller]
75pub fn bindgen<I, S>(args: I) -> Warnings
76where
77 I: IntoIterator<Item = S>,
78 S: AsRef<str>,
79{
80 let args = expand_args(args);
81 let mut kind = ArgKind::None;
82 let mut input = Vec::new();
83 let mut include = Vec::new();
84 let mut exclude = Vec::new();
85 let mut references = Vec::new();
86 let mut derive = Vec::new();
87
88 let mut flat = false;
89 let mut no_allow = false;
90 let mut no_comment = false;
91 let mut no_deps = false;
92 let mut no_toml = false;
93 let mut package = false;
94 let mut implement = false;
95 let mut rustfmt = String::new();
96 let mut output = String::new();
97 let mut sys = false;
98 let mut link = "windows_link".to_string();
99 let mut index = false;
100
101 for arg in &args {
102 if arg.starts_with('-') {
103 kind = ArgKind::None;
104 }
105
106 match kind {
107 ArgKind::None => match arg.as_str() {
108 "--in" => kind = ArgKind::Input,
109 "--out" => kind = ArgKind::Output,
110 "--filter" => kind = ArgKind::Filter,
111 "--rustfmt" => kind = ArgKind::Rustfmt,
112 "--reference" => kind = ArgKind::Reference,
113 "--derive" => kind = ArgKind::Derive,
114 "--flat" => flat = true,
115 "--no-allow" => no_allow = true,
116 "--no-comment" => no_comment = true,
117 "--no-deps" => no_deps = true,
118 "--no-toml" => no_toml = true,
119 "--package" => package = true,
120 "--sys" => sys = true,
121 "--implement" => implement = true,
122 "--link" => kind = ArgKind::Link,
123 "--index" => index = true,
124 _ => panic!("invalid option `{arg}`"),
125 },
126 ArgKind::Output => {
127 if output.is_empty() {
128 output = arg.to_string();
129 } else {
130 panic!("exactly one `--out` is required");
131 }
132 }
133 ArgKind::Input => input.push(arg.as_str()),
134 ArgKind::Filter => {
135 if let Some(rest) = arg.strip_prefix('!') {
136 exclude.push(rest);
137 } else {
138 include.push(arg.as_str());
139 }
140 }
141 ArgKind::Reference => {
142 references.push(ReferenceStage::parse(arg));
143 }
144 ArgKind::Derive => {
145 derive.push(arg.as_str());
146 }
147 ArgKind::Rustfmt => rustfmt = arg.to_string(),
148 ArgKind::Link => link = arg.to_string(),
149 }
150 }
151
152 if package && flat {
153 panic!("cannot combine `--package` and `--flat`");
154 }
155
156 if input.is_empty() {
157 input.push("default");
158 };
159
160 if output.is_empty() {
161 panic!("exactly one `--out` is required");
162 };
163
164 if !sys && !no_deps {
165 references.insert(
166 0,
167 ReferenceStage::parse("windows_collections,flat,Windows.Foundation.Collections"),
168 );
169 references.insert(
170 0,
171 ReferenceStage::parse("windows_numerics,flat,Windows.Foundation.Numerics"),
172 );
173 references.insert(
174 0,
175 ReferenceStage::parse("windows_future,flat,Windows.Foundation.Async*"),
176 );
177 references.insert(
178 0,
179 ReferenceStage::parse("windows_future,flat,Windows.Foundation.IAsync*"),
180 );
181 }
182
183 if include.is_empty() {
186 panic!("at least one `--filter` required");
187 }
188
189 let reader = Reader::new(expand_input(&input));
190 let filter = Filter::new(&reader, &include, &exclude);
191 let references = References::new(&reader, references);
192 let types = TypeMap::filter(&reader, &filter, &references);
193 let derive = Derive::new(&reader, &types, &derive);
194
195 let config = Config {
196 types,
197 flat,
198 references,
199 derive,
200 no_allow,
201 no_comment,
202 no_deps,
203 no_toml,
204 package,
205 rustfmt,
206 output,
207 sys,
208 implement,
209 link,
210 warnings: WarningBuilder::default(),
211 };
212
213 let tree = TypeTree::new(&config.types);
214
215 let writer = Writer {
216 config: &config,
217 namespace: "",
218 };
219
220 writer.write(tree);
221
222 if index {
223 index::write(&config.types, &format!("{}/features.json", config.output));
224 }
225
226 config.warnings.build()
227}
228
229enum ArgKind {
230 None,
231 Input,
232 Output,
233 Filter,
234 Rustfmt,
235 Reference,
236 Derive,
237 Link,
238}
239
240#[track_caller]
241fn expand_args<I, S>(args: I) -> Vec<String>
242where
243 I: IntoIterator<Item = S>,
244 S: AsRef<str>,
245{
246 #[track_caller]
248 fn from_string(result: &mut Vec<String>, value: &str) {
249 expand_args(result, value.split_whitespace().map(|arg| arg.to_string()))
250 }
251
252 #[track_caller]
253 fn expand_args<I, S>(result: &mut Vec<String>, args: I)
254 where
255 I: IntoIterator<Item = S>,
256 S: AsRef<str>,
257 {
258 let mut expand = false;
259
260 for arg in args.into_iter().map(|arg| arg.as_ref().to_string()) {
261 if arg.starts_with('-') {
262 expand = false;
263 }
264 if expand {
265 for args in io::read_file_lines(&arg) {
266 if !args.starts_with("//") {
267 from_string(result, &args);
268 }
269 }
270 } else if arg == "--etc" {
271 expand = true;
272 } else {
273 result.push(arg);
274 }
275 }
276 }
277
278 let mut result = vec![];
279 expand_args(&mut result, args);
280 result
281}
282
283#[track_caller]
284fn expand_input(input: &[&str]) -> Vec<File> {
285 #[track_caller]
286 fn expand_input(result: &mut Vec<String>, input: &str) {
287 let path = std::path::Path::new(input);
288
289 if path.is_dir() {
290 let prev_len = result.len();
291
292 for path in path
293 .read_dir()
294 .unwrap_or_else(|_| panic!("failed to read directory `{input}`"))
295 .flatten()
296 .map(|entry| entry.path())
297 {
298 if path.is_file()
299 && path
300 .extension()
301 .is_some_and(|extension| extension.eq_ignore_ascii_case("winmd"))
302 {
303 result.push(path.to_string_lossy().to_string());
304 }
305 }
306
307 if result.len() == prev_len {
308 panic!("failed to find .winmd files in directory `{input}`");
309 }
310 } else {
311 result.push(input.to_string());
312 }
313 }
314
315 let mut paths = vec![];
316 let mut use_default = false;
317
318 for input in input {
319 if *input == "default" {
320 use_default = true;
321 } else {
322 expand_input(&mut paths, input);
323 }
324 }
325
326 let mut input = vec![];
327
328 if use_default {
329 input = [
330 std::include_bytes!("../default/Windows.winmd").to_vec(),
331 std::include_bytes!("../default/Windows.Win32.winmd").to_vec(),
332 std::include_bytes!("../default/Windows.Wdk.winmd").to_vec(),
333 ]
334 .into_iter()
335 .map(|bytes| File::new(bytes).unwrap())
336 .collect();
337 }
338
339 for path in &paths {
340 let Ok(bytes) = std::fs::read(path) else {
341 panic!("failed to read binary file `{path}`");
342 };
343
344 let Some(file) = File::new(bytes) else {
345 panic!("failed to read .winmd format `{path}`");
346 };
347
348 input.push(file);
349 }
350
351 input
352}
353
354fn namespace_starts_with(namespace: &str, starts_with: &str) -> bool {
355 namespace.starts_with(starts_with)
356 && (namespace.len() == starts_with.len()
357 || namespace.as_bytes().get(starts_with.len()) == Some(&b'.'))
358}
359
360#[cfg(test)]
361mod tests {
362 use super::*;
363
364 #[test]
365 fn test_starts_with() {
366 assert!(namespace_starts_with(
367 "Windows.Win32.Graphics.Direct3D11on12",
368 "Windows.Win32.Graphics.Direct3D11on12"
369 ));
370 assert!(namespace_starts_with(
371 "Windows.Win32.Graphics.Direct3D11on12",
372 "Windows.Win32.Graphics"
373 ));
374 assert!(!namespace_starts_with(
375 "Windows.Win32.Graphics.Direct3D11on12",
376 "Windows.Win32.Graphics.Direct3D11"
377 ));
378 assert!(!namespace_starts_with(
379 "Windows.Win32.Graphics.Direct3D",
380 "Windows.Win32.Graphics.Direct3D11"
381 ));
382 }
383}