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