swift_bridge_build/
lib.rs

1//! Parse Rust source files for #\[swift_bridge::bridge\] modules and then generate the
2//! corresponding C header files and Swift files.
3
4#![deny(missing_docs)]
5
6mod package;
7use crate::generate_core::write_core_swift_and_c;
8pub use package::*;
9use std::path::Path;
10use swift_bridge_ir::{CodegenConfig, SwiftBridgeModule};
11use syn::__private::ToTokens;
12use syn::{File, Item};
13
14mod generate_core;
15
16/// Parse rust sources files for `#\[swift_bridge::bridge\]` headers and generate the corresponding
17/// Swift files.
18pub fn parse_bridges(
19    rust_source_files: impl IntoIterator<Item = impl AsRef<Path>>,
20) -> GeneratedCode {
21    let mut generated_code = GeneratedCode::new();
22
23    for rust_file in rust_source_files.into_iter() {
24        let rust_file: &Path = rust_file.as_ref();
25
26        let file = std::fs::read_to_string(rust_file).unwrap();
27        let gen = match parse_file_contents(&file) {
28            Ok(generated) => generated,
29            Err(e) => {
30                // TODO: Return an error...
31                panic!(
32                    r#"
33Error while parsing {:?}
34{}
35"#,
36                    rust_file, e
37                )
38            }
39        };
40
41        generated_code.generated.push(gen);
42    }
43
44    generated_code
45}
46
47/// Generated Swift files and C headers.
48pub struct GeneratedCode {
49    generated: Vec<GeneratedFromSwiftBridgeModule>,
50}
51
52impl GeneratedCode {
53    fn new() -> Self {
54        GeneratedCode { generated: vec![] }
55    }
56}
57
58impl GeneratedCode {
59    /// Write all of the generated Swift to a single Swift file and all of the generated C headers
60    /// to a single header file.
61    pub fn write_all_concatenated(&self, swift_bridge_out_dir: impl AsRef<Path>, crate_name: &str) {
62        let swift_bridge_out_dir = swift_bridge_out_dir.as_ref();
63
64        let mut concatenated_swift = "".to_string();
65        let mut concatenated_c = "".to_string();
66
67        for gen in &self.generated {
68            concatenated_swift += &gen.swift;
69            concatenated_c += &gen.c_header;
70        }
71
72        let out = swift_bridge_out_dir.join(&crate_name);
73        match std::fs::create_dir_all(&out) {
74            Ok(_) => {}
75            Err(_) => {}
76        };
77
78        std::fs::write(out.join(format!("{}.h", crate_name)), concatenated_c).unwrap();
79        std::fs::write(
80            out.join(format!("{}.swift", crate_name)),
81            concatenated_swift,
82        )
83        .unwrap();
84
85        write_core_swift_and_c(swift_bridge_out_dir.as_ref());
86    }
87
88    /// Concatenate all of the generated Swift code into one file.
89    pub fn concat_swift(&self) -> String {
90        let mut swift = "".to_string();
91
92        for gen in &self.generated {
93            swift += &gen.swift;
94        }
95
96        swift
97    }
98
99    /// Concatenate all of the generated C code into one file.
100    pub fn concat_c(&self) -> String {
101        let mut c_header = "".to_string();
102
103        for gen in &self.generated {
104            c_header += &gen.c_header;
105        }
106
107        c_header
108    }
109}
110
111fn parse_file_contents(file: &str) -> syn::Result<GeneratedFromSwiftBridgeModule> {
112    let file: File = syn::parse_str(file)?;
113
114    let mut generated = GeneratedFromSwiftBridgeModule {
115        c_header: "".to_string(),
116        swift: "".to_string(),
117    };
118
119    for item in file.items {
120        match item {
121            Item::Mod(module) => {
122                // TODO: Move this check into the `impl Parse for SwiftBridgeModule`.. Modify our
123                //  tests in swift-bridge-ir to annotate modules with `#[swift_bridge::bridge]`
124                if module.attrs.iter().any(|a| {
125                    let attrib = a.path.to_token_stream().to_string();
126                    attrib == "swift_bridge :: bridge" || attrib == "swift_bridge_macro :: bridge"
127                }) {
128                    let module: SwiftBridgeModule = syn::parse2(module.to_token_stream())?;
129
130                    let config = CodegenConfig {
131                        crate_feature_lookup: Box::new(|feature_name| {
132                            let normalized_feature_name = feature_name.replace("-", "_");
133                            let normalized_feature_name = normalized_feature_name.to_uppercase();
134
135                            let env_var_name = format!("CARGO_FEATURE_{}", normalized_feature_name);
136                            std::env::var(env_var_name).is_ok()
137                        }),
138                    };
139                    let swift_and_c = module.generate_swift_code_and_c_header(config);
140
141                    generated.c_header += &swift_and_c.c_header;
142                    generated.c_header += "\n\n";
143
144                    let swift = &swift_and_c.swift;
145                    generated.swift += &swift;
146                    generated.swift += "\n\n";
147                }
148            }
149            _ => {}
150        }
151    }
152
153    Ok(generated)
154}
155
156#[derive(Debug)]
157struct GeneratedFromSwiftBridgeModule {
158    c_header: String,
159    swift: String,
160}