1use std::borrow::Cow;
2use std::collections::HashSet;
3use std::env;
4use std::error::Error;
5use std::io::Write;
6use std::path::{Path, PathBuf};
7
8use crate::clang::*;
9use crate::parse::*;
10use crate::print::*;
11
12const HOST_TARGET: &'static str = include_str!(concat!(env!("OUT_DIR"), "/host-target.txt"));
13
14fn rust_to_clang_target(rust_target: &str) -> String {
17 if rust_target.starts_with("aarch64-apple-") {
18 return "arm64-apple-".to_owned() + &rust_target["aarch64-apple-".len()..];
19 }
20
21 rust_target.to_owned()
22}
23
24pub struct Generator {
26 pub(crate) include_paths: Vec<PathBuf>,
27 pub(crate) target: Option<String>,
28 pub(crate) skip_types: HashSet<String>,
29 pub(crate) skip_interface_traits: HashSet<String>,
30 pub(crate) constant_parser: Option<Box<dyn Fn(&[String]) -> Option<String>>>,
31 pub(crate) iid_generator: Option<Box<dyn Fn(&str) -> String>>,
32 pub(crate) query_interface_fn: Option<String>,
33 pub(crate) add_ref_fn: Option<String>,
34 pub(crate) release_fn: Option<String>,
35}
36
37impl Default for Generator {
38 fn default() -> Generator {
39 Generator {
40 include_paths: Vec::new(),
41 target: None,
42 skip_types: HashSet::new(),
43 skip_interface_traits: HashSet::new(),
44 constant_parser: None,
45 iid_generator: None,
46 query_interface_fn: None,
47 add_ref_fn: None,
48 release_fn: None,
49 }
50 }
51}
52
53impl Generator {
54 pub fn include_path<T: AsRef<Path>>(mut self, path: T) -> Self {
56 self.include_paths.push(path.as_ref().to_path_buf());
57 self
58 }
59
60 pub fn target<T: AsRef<str>>(mut self, target: T) -> Self {
62 self.target = Some(target.as_ref().to_string());
63 self
64 }
65
66 pub fn skip_type<T: AsRef<str>>(mut self, type_: T) -> Self {
68 self.skip_types.insert(type_.as_ref().to_string());
69 self
70 }
71
72 pub fn skip_types<S: AsRef<str>, T: AsRef<[S]>>(mut self, types: T) -> Self {
74 self.skip_types
75 .extend(types.as_ref().iter().map(|s| s.as_ref().to_string()));
76 self
77 }
78
79 pub fn skip_interface_trait<T: AsRef<str>>(mut self, interface: T) -> Self {
81 self.skip_interface_traits
82 .insert(interface.as_ref().to_string());
83 self
84 }
85
86 pub fn skip_interface_traits<'a, T: AsRef<[&'a str]>>(mut self, interfaces: T) -> Self {
88 self.skip_interface_traits
89 .extend(interfaces.as_ref().iter().map(|s| s.to_string()));
90 self
91 }
92
93 pub fn constant_parser<F>(mut self, f: F) -> Self
99 where
100 F: Fn(&[String]) -> Option<String> + 'static,
101 {
102 self.constant_parser = Some(Box::new(f));
103 self
104 }
105
106 pub fn iid_generator<F>(mut self, f: F) -> Self
109 where
110 F: Fn(&str) -> String + 'static,
111 {
112 self.iid_generator = Some(Box::new(f));
113 self
114 }
115
116 pub fn query_interface_fn<T: AsRef<str>>(mut self, f: T) -> Self {
122 self.query_interface_fn = Some(f.as_ref().to_string());
123 self
124 }
125
126 pub fn add_ref_fn<T: AsRef<str>>(mut self, f: T) -> Self {
132 self.add_ref_fn = Some(f.as_ref().to_string());
133 self
134 }
135
136 pub fn release_fn<T: AsRef<str>>(mut self, f: T) -> Self {
142 self.release_fn = Some(f.as_ref().to_string());
143 self
144 }
145
146 pub fn generate<T: AsRef<str>, W: Write>(
148 &self,
149 source: T,
150 sink: W,
151 ) -> Result<(), Box<dyn Error>> {
152 if !clang_sys::is_loaded() {
153 clang_sys::load()?;
154 }
155
156 let rust_target = if let Some(target) = &self.target {
157 Cow::from(target)
158 } else if let Ok(target) = env::var("TARGET") {
159 Cow::from(target)
160 } else {
161 Cow::from(HOST_TARGET)
162 };
163 let clang_target = rust_to_clang_target(&rust_target);
164
165 let unit = TranslationUnit::new(source.as_ref(), &self.include_paths, Some(&clang_target))?;
166
167 let namespace = Namespace::parse(&unit.cursor(), &self)?;
168
169 let mut printer = RustPrinter::new(sink, &self);
170 printer.print_namespace(&namespace)?;
171
172 Ok(())
173 }
174}