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 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 let bindings = bindgen_builder.generate()?;
89
90 let mut out = bindings.to_string();
92
93 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 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 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}