com_scrape/
generator.rs

1use std::borrow::Cow;
2use std::collections::{HashMap, 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
14// Some target triples are different between rustc and clang.
15// See https://github.com/rust-lang/rust-bindgen/blob/05ebcace15a8784e5a5b1001a3b755b866fac901/bindgen/lib.rs#L670
16fn 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
24/// Builder struct for configuring and generating bindings.
25pub 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) override_typedef_types: HashMap<String, String>,
31    pub(crate) override_constant_types: HashMap<String, String>,
32    pub(crate) override_constant_values: HashMap<String, String>,
33    pub(crate) constant_parser: Option<Box<dyn Fn(&[&str]) -> Option<String>>>,
34    pub(crate) iid_generator: Option<Box<dyn Fn(&str) -> String>>,
35    pub(crate) query_interface_fn: Option<String>,
36    pub(crate) add_ref_fn: Option<String>,
37    pub(crate) release_fn: Option<String>,
38}
39
40impl Default for Generator {
41    fn default() -> Generator {
42        Generator {
43            include_paths: Vec::new(),
44            target: None,
45            skip_types: HashSet::new(),
46            skip_interface_traits: HashSet::new(),
47            override_typedef_types: HashMap::new(),
48            override_constant_types: HashMap::new(),
49            override_constant_values: HashMap::new(),
50            constant_parser: None,
51            iid_generator: None,
52            query_interface_fn: None,
53            add_ref_fn: None,
54            release_fn: None,
55        }
56    }
57}
58
59impl Generator {
60    /// Adds `path` to the list of include paths to pass to `libclang`.
61    pub fn include_path<T: AsRef<Path>>(mut self, path: T) -> Self {
62        self.include_paths.push(path.as_ref().to_path_buf());
63        self
64    }
65
66    /// Specify the target triple for which bindings should be generated.
67    pub fn target<T: AsRef<str>>(mut self, target: T) -> Self {
68        self.target = Some(target.as_ref().to_string());
69        self
70    }
71
72    /// Do not generate bindings for `type_`.
73    pub fn skip_type<T: AsRef<str>>(mut self, type_: T) -> Self {
74        self.skip_types.insert(type_.as_ref().to_string());
75        self
76    }
77
78    /// Do not generate bindings for `types`.
79    pub fn skip_types<S: AsRef<str>, T: AsRef<[S]>>(mut self, types: T) -> Self {
80        self.skip_types
81            .extend(types.as_ref().iter().map(|s| s.as_ref().to_string()));
82        self
83    }
84
85    /// Do not generate an interface trait for `interface`.
86    pub fn skip_interface_trait<T: AsRef<str>>(mut self, interface: T) -> Self {
87        self.skip_interface_traits
88            .insert(interface.as_ref().to_string());
89        self
90    }
91
92    /// Do not generate interface traits for `interfaces`.
93    pub fn skip_interface_traits<'a, T: AsRef<[&'a str]>>(mut self, interfaces: T) -> Self {
94        self.skip_interface_traits
95            .extend(interfaces.as_ref().iter().map(|s| s.to_string()));
96        self
97    }
98
99    /// Override the type of `typedef` in the generator's output.
100    pub fn override_typedef_type<T: AsRef<str>, U: AsRef<str>>(
101        mut self,
102        typedef: T,
103        type_: U,
104    ) -> Self {
105        self.override_typedef_types
106            .insert(typedef.as_ref().to_string(), type_.as_ref().to_string());
107        self
108    }
109
110    /// Override the types of typedefs in the generator's output based on the typedef-type pairs in
111    /// `typedefs`.
112    pub fn override_typedef_types<T, U, I>(mut self, typedefs: I) -> Self
113    where
114        T: AsRef<str>,
115        U: AsRef<str>,
116        I: AsRef<[(T, U)]>,
117    {
118        self.override_typedef_types.extend(
119            typedefs
120                .as_ref()
121                .into_iter()
122                .map(|(k, v)| (k.as_ref().to_string(), v.as_ref().to_string())),
123        );
124        self
125    }
126
127    /// Override the type of `constant` in the generator's output.
128    pub fn override_constant_type<T: AsRef<str>, U: AsRef<str>>(
129        mut self,
130        constant: T,
131        type_: U,
132    ) -> Self {
133        self.override_constant_types
134            .insert(constant.as_ref().to_string(), type_.as_ref().to_string());
135        self
136    }
137
138    /// Override the types of constants in the generator's output based on the constant-type pairs in
139    /// `constants`.
140    pub fn override_constant_types<T, U, I>(mut self, constants: I) -> Self
141    where
142        T: AsRef<str>,
143        U: AsRef<str>,
144        I: AsRef<[(T, U)]>,
145    {
146        self.override_constant_types.extend(
147            constants
148                .as_ref()
149                .into_iter()
150                .map(|(k, v)| (k.as_ref().to_string(), v.as_ref().to_string())),
151        );
152        self
153    }
154
155    /// Override the value of `constant` in the generator's output.
156    pub fn override_constant_value<T: AsRef<str>, U: AsRef<str>>(
157        mut self,
158        constant: T,
159        value: U,
160    ) -> Self {
161        self.override_constant_values
162            .insert(constant.as_ref().to_string(), value.as_ref().to_string());
163        self
164    }
165
166    /// Override the values of constants in the generator's output based on the constant-value pairs
167    /// in `constants`.
168    pub fn override_constant_values<T, U, I>(mut self, constants: I) -> Self
169    where
170        T: AsRef<str>,
171        U: AsRef<str>,
172        I: AsRef<[(T, U)]>,
173    {
174        self.override_constant_values.extend(
175            constants
176                .as_ref()
177                .into_iter()
178                .map(|(k, v)| (k.as_ref().to_string(), v.as_ref().to_string())),
179        );
180        self
181    }
182
183    /// Registers a callback for parsing constant definitions which `libclang` is not able to
184    /// evaluate.
185    ///
186    /// The callback will be passed a slice of tokens, and its output (if not `None`) will be
187    /// included in the generated bindings.
188    pub fn constant_parser<F>(mut self, f: F) -> Self
189    where
190        F: Fn(&[&str]) -> Option<String> + 'static,
191    {
192        self.constant_parser = Some(Box::new(f));
193        self
194    }
195
196    /// Registers a callback which should, when given the name of an interface as a string, return
197    /// a string containing a Rust expression evaluating to the `Guid` value for that interface.
198    pub fn iid_generator<F>(mut self, f: F) -> Self
199    where
200        F: Fn(&str) -> String + 'static,
201    {
202        self.iid_generator = Some(Box::new(f));
203        self
204    }
205
206    /// Registers a function which will be called by the implementations of
207    /// `Unknown::query_interface` for generated interface types.
208    ///
209    /// The function should be in scope where the resulting bindings are placed, and it should have
210    /// the same type signature as `Unknown::query_interface`.
211    pub fn query_interface_fn<T: AsRef<str>>(mut self, f: T) -> Self {
212        self.query_interface_fn = Some(f.as_ref().to_string());
213        self
214    }
215
216    /// Registers a function which will be called by the implementations of `Unknown::add_ref` for
217    /// generated interface types.
218    ///
219    /// The function should be in scope where the resulting bindings are placed, and it should have
220    /// the same type signature as `Unknown::add_ref`.
221    pub fn add_ref_fn<T: AsRef<str>>(mut self, f: T) -> Self {
222        self.add_ref_fn = Some(f.as_ref().to_string());
223        self
224    }
225
226    /// Registers a function which will be called by the implementations of `Unknown::release` for
227    /// generated interface types.
228    ///
229    /// The function should be in scope where the resulting bindings are placed, and it should have
230    /// the same type signature as `Unknown::release`.
231    pub fn release_fn<T: AsRef<str>>(mut self, f: T) -> Self {
232        self.release_fn = Some(f.as_ref().to_string());
233        self
234    }
235
236    /// Generates Rust bindings for the C++ definitions in `source` and outputs them via `sink`.
237    pub fn generate<T: AsRef<str>, W: Write>(
238        &self,
239        source: T,
240        sink: W,
241    ) -> Result<(), Box<dyn Error>> {
242        if !clang_sys::is_loaded() {
243            clang_sys::load()?;
244        }
245
246        let rust_target = if let Some(target) = &self.target {
247            Cow::from(target)
248        } else if let Ok(target) = env::var("TARGET") {
249            Cow::from(target)
250        } else {
251            Cow::from(HOST_TARGET)
252        };
253        let clang_target = rust_to_clang_target(&rust_target);
254
255        let unit = TranslationUnit::new(source.as_ref(), &self.include_paths, Some(&clang_target))?;
256
257        let namespace = Namespace::parse(&unit.cursor(), &self)?;
258
259        let mut printer = RustPrinter::new(sink, &self);
260        printer.print_namespace(&namespace)?;
261
262        Ok(())
263    }
264}