apple_bindgen/
builder.rs

1pub use crate::{
2    config::{Config, ConfigMap, FileConfig},
3    sdk::{SdkPath, SdkPathError},
4};
5
6#[derive(Debug)]
7pub struct Builder {
8    framework: String,
9    sdk: SdkPath,
10    target: Option<String>,
11    config: Config,
12}
13
14impl Builder {
15    pub fn new(
16        framework: &str,
17        sdk: impl TryInto<SdkPath, Error = SdkPathError>,
18        config: Config,
19    ) -> Result<Self, SdkPathError> {
20        Ok(Self {
21            framework: framework.to_owned(),
22            sdk: sdk.try_into()?,
23            target: None,
24            config,
25        })
26    }
27
28    pub fn with_builtin_config(
29        framework: &str,
30        sdk: impl TryInto<SdkPath, Error = SdkPathError>,
31    ) -> Result<Self, SdkPathError> {
32        Self::new(
33            framework,
34            sdk,
35            ConfigMap::with_builtin_config().framework_config(framework),
36        )
37    }
38
39    pub fn target(mut self, target: impl AsRef<str>) -> Self {
40        assert!(self.target.is_none());
41        self.target = Some(target.as_ref().to_owned());
42        self
43    }
44
45    pub fn bindgen_builder(&self) -> bindgen::Builder {
46        // Begin building the bindgen params.
47        let mut builder = bindgen::Builder::default();
48
49        let mut clang_args = vec!["-x", "objective-c", "-fblocks", "-fmodules"];
50        let target_arg;
51        if let Some(target) = self.target.as_ref() {
52            target_arg = format!("--target={}", target);
53            clang_args.push(&target_arg);
54        }
55
56        clang_args.extend(&[
57            "-isysroot",
58            self.sdk
59                .path()
60                .to_str()
61                .expect("sdk path is not utf-8 representable"),
62        ]);
63
64        builder = builder
65            .clang_args(&clang_args)
66            .layout_tests(self.config.layout_tests)
67            .rustfmt_bindings(true);
68
69        for opaque_type in &self.config.opaque_types {
70            builder = builder.opaque_type(opaque_type);
71        }
72        for blocklist_item in &self.config.blocklist_items {
73            builder = builder.blocklist_item(blocklist_item);
74        }
75
76        builder = builder.header_contents(
77            &format!("{}.h", self.framework),
78            &format!("@import {};", self.framework),
79        );
80
81        builder
82    }
83
84    pub fn generate(&self) -> Result<String, bindgen::BindgenError> {
85        let bindgen_builder = self.bindgen_builder();
86
87        // Generate the bindings.
88        let bindings = bindgen_builder.generate()?;
89
90        // TODO: find the best way to do this post-processing
91        let mut out = bindings.to_string();
92
93        // remove redundant and malformed definitions of `id`
94        out = out.replace("pub type id = *mut objc::runtime::Object", "PUB-TYPE-ID");
95        let re = regex::Regex::new("pub type id = .*;").unwrap();
96        out = re.replace_all(&mut out, "").into_owned();
97        out = out.replace("PUB-TYPE-ID", "pub type id = *mut objc::runtime::Object");
98
99        // Bindgen.toml `replacements`
100        for replacement in &self.config.replacements {
101            let (old, new) = replacement
102                .split_once(" #=># ")
103                .expect("Bindgen.toml is malformed");
104            out = out.replace(old, new);
105        }
106
107        // Bindgen.toml `impl_debugs`
108        for ty in &self.config.impl_debugs {
109            if out.contains(ty) {
110                out.push_str(&format!(
111                    r#"
112impl ::std::fmt::Debug for {ty} {{
113    fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {{
114        f.debug_struct(stringify!(#ty))
115            .finish()
116    }}
117}}
118                "#
119                ));
120            }
121        }
122        Ok(out)
123    }
124}