csbindgen/
builder.rs

1use std::path::PathBuf;
2use std::{
3    error::Error,
4    fs::{File, OpenOptions},
5    io::{self, Write},
6    path::Path,
7};
8use std::convert::identity;
9
10use crate::{generate, GenerateKind};
11
12pub struct Builder {
13    options: BindgenOptions,
14}
15
16pub struct BindgenOptions {
17    pub input_bindgen_files: Vec<PathBuf>,
18    pub input_extern_files: Vec<PathBuf>,
19    pub method_filter: fn(method_name: String) -> bool,
20    pub rust_method_type_path: String,
21    pub rust_method_prefix: String,
22    pub rust_file_header: String,
23    pub csharp_namespace: String,
24    pub csharp_class_name: String,
25    pub csharp_dll_name: String,
26    pub csharp_disable_emit_dll_name: bool,
27    pub csharp_class_accessibility: String,
28    pub csharp_entry_point_prefix: String,
29    pub csharp_method_prefix: String,
30    pub csharp_if_symbol: String,
31    pub csharp_if_dll_name: String,
32    pub csharp_use_function_pointer: bool,
33    pub csharp_use_nint_types: bool,
34    pub csharp_imported_namespaces: Vec<String>,
35    pub csharp_generate_const_filter: fn(const_name: &str) -> bool,
36    pub csharp_type_rename: fn(type_name: String) -> String,
37    pub csharp_file_header: String,
38    pub csharp_file_footer: String,
39    pub always_included_types: Vec<String>,
40}
41
42impl Default for Builder {
43    fn default() -> Self {
44        Self {
45            options: BindgenOptions {
46                input_bindgen_files: vec![],
47                input_extern_files: vec![],
48                method_filter: |x| !x.starts_with('_'),
49                rust_method_type_path: "".to_string(),
50                rust_method_prefix: "csbindgen_".to_string(),
51                rust_file_header: "".to_string(),
52                csharp_namespace: "CsBindgen".to_string(),
53                csharp_class_name: "NativeMethods".to_string(),
54                csharp_dll_name: "".to_string(),
55                csharp_disable_emit_dll_name: false,
56                csharp_entry_point_prefix: "".to_string(),
57                csharp_method_prefix: "".to_string(),
58                csharp_class_accessibility: "internal".to_string(),
59                csharp_if_symbol: "".to_string(),
60                csharp_if_dll_name: "".to_string(),
61                csharp_use_function_pointer: true,
62                csharp_use_nint_types: true,
63                csharp_imported_namespaces: vec![],
64                csharp_generate_const_filter: |_| false,
65                csharp_type_rename: identity,
66                csharp_file_header: "".to_string(),
67                csharp_file_footer: "".to_string(),
68                always_included_types: vec![],
69            },
70        }
71    }
72}
73
74impl Builder {
75    pub fn new() -> Self {
76        Self::default()
77    }
78
79    /// Add an input .rs file(such as generated from bindgen) to generate binding.
80    pub fn input_bindgen_file<T: AsRef<Path>>(mut self, input_bindgen_file: T) -> Builder {
81        self.options.input_bindgen_files.push(input_bindgen_file.as_ref().to_path_buf());
82        self
83    }
84
85    /// Add an input .rs file for collect extern methods to C# binding.
86    pub fn input_extern_file<T: AsRef<Path>>(mut self, input_extern_file: T) -> Builder {
87        self.options
88            .input_extern_files
89            .push(input_extern_file.as_ref().to_path_buf());
90        self
91    }
92
93    /// Filter generate method callback, default is `!x.starts_with('_')`
94    pub fn method_filter(mut self, method_filter: fn(method_name: String) -> bool) -> Builder {
95        self.options.method_filter = method_filter;
96        self
97    }
98
99    /// Adds a list of types that will always be considered to be included in the
100    /// generated bindings, even if not part of any function signature
101    pub fn always_included_types<I, S>(mut self, always_included_types: I) -> Builder
102        where I: IntoIterator<Item = S>, S: ToString
103    {
104        self.options.always_included_types.extend(always_included_types.into_iter().map(|v| v.to_string()));
105        self
106    }
107
108    /// add original extern call type prefix to rust wrapper,
109    /// `return {rust_method_type_path}::foo()`
110    pub fn rust_method_type_path<T: Into<String>>(mut self, rust_method_type_path: T) -> Builder {
111        self.options.rust_method_type_path = rust_method_type_path.into();
112        self
113    }
114
115    /// add method prefix to rust wrapper, default is `csbindgen_`
116    /// `pub extern "C" fn {rust_method_prefix}foo()`
117    pub fn rust_method_prefix<T: Into<String>>(mut self, rust_method_prefix: T) -> Builder {
118        self.options.rust_method_prefix = rust_method_prefix.into();
119        self
120    }
121
122    /// add file header string to rust wrapper,
123    /// `mod lz4;`, `use super::lz4;`
124    pub fn rust_file_header<T: Into<String>>(mut self, rust_file_header: T) -> Builder {
125        self.options.rust_file_header = rust_file_header.into();
126        self
127    }
128
129    /// configure C# file namespace(default is `CsBindgen`),
130    /// "namespace {csharp_namespace}"
131    pub fn csharp_namespace<T: Into<String>>(mut self, csharp_namespace: T) -> Builder {
132        self.options.csharp_namespace = csharp_namespace.into();
133        self
134    }
135
136    /// configure C# extra import namespace,
137    /// "using {csharp_namespace};"
138    pub fn csharp_import_namespace<T: Into<String>>(mut self, csharp_namespace: T) -> Builder {
139        self.options
140            .csharp_imported_namespaces
141            .push(csharp_namespace.into());
142        self
143    }
144
145    /// configure C# class name(default is `NativeMethods`),
146    /// `public static unsafe partial class {csharp_class_name}`
147    pub fn csharp_class_name<T: Into<String>>(mut self, csharp_class_name: T) -> Builder {
148        self.options.csharp_class_name = csharp_class_name.into();
149        self
150    }
151
152    /// configure C# load dll name,
153    /// `[DllImport({csharp_dll_name})]`
154    pub fn csharp_dll_name<T: Into<String>>(mut self, csharp_dll_name: T) -> Builder {
155        self.options.csharp_dll_name = csharp_dll_name.into();
156        self
157    }
158
159    /// configure don't emit __DllName
160    pub fn csharp_disable_emit_dll_name(mut self, csharp_disable_emit_dll_name: bool) -> Builder {
161        self.options.csharp_disable_emit_dll_name = csharp_disable_emit_dll_name;
162        self
163    }
164
165    /// configure C# DllImport EntryPoint prefix,
166    /// `[DllImport(, EntryPoint ="{csharp_entry_point_prefix}foo")]`
167    pub fn csharp_entry_point_prefix<T: Into<String>>(
168        mut self,
169        csharp_entry_point_prefix: T,
170    ) -> Builder {
171        self.options.csharp_entry_point_prefix = csharp_entry_point_prefix.into();
172        self
173    }
174
175    /// configure C# calling method name prefix,
176    /// `public static extern void {csharp_method_prefix}foo()`
177    pub fn csharp_method_prefix<T: Into<String>>(mut self, csharp_method_prefix: T) -> Builder {
178        self.options.csharp_method_prefix = csharp_method_prefix.into();
179        self
180    }
181
182    /// configure C# class accessibility, default is internal
183    /// `{csharp_class_accessibility} static unsafe partial class NativeMethods`
184    pub fn csharp_class_accessibility<T: Into<String>>(
185        mut self,
186        csharp_class_accessibility: T,
187    ) -> Builder {
188        self.options.csharp_class_accessibility = csharp_class_accessibility.into();
189        self
190    }
191
192    /// configure add C# dll name if directive,
193    /// #if {if_symbol} __DllName = {if_dll_name}
194    pub fn csharp_dll_name_if<T: Into<String>>(mut self, if_symbol: T, if_dll_name: T) -> Builder {
195        self.options.csharp_if_symbol = if_symbol.into();
196        self.options.csharp_if_dll_name = if_dll_name.into();
197        self
198    }
199
200    /// configure C# generate function pointer as delegate* or Func/Action, default is true(generate delegate*)
201    pub fn csharp_use_function_pointer(mut self, csharp_use_function_pointer: bool) -> Builder {
202        self.options.csharp_use_function_pointer = csharp_use_function_pointer;
203        self
204    }
205
206    /// configure C# usage of `nint`/`nuint` in place of `System.IntPtr`/`System.UIntPtr`, default is true
207    /// (use `nint`/`nuint`)
208    pub fn csharp_use_nint_types(mut self, csharp_use_nint_types: bool) -> Builder {
209        self.options.csharp_use_nint_types = csharp_use_nint_types;
210        self
211    }
212
213    /// configure C# generate const, default is false
214    /// equivalent to csharp_generate_const_filter(|_| csharp_generate_const)
215    #[deprecated(note = "User csharp_generate_const_filter instead")]
216    pub fn csharp_generate_const(self, csharp_generate_const: bool) -> Builder {
217        self.csharp_generate_const_filter(if csharp_generate_const { |_| true } else { |_| false })
218    }
219
220    /// configure C# generate const filter, default `|_| false`
221    pub fn csharp_generate_const_filter(mut self, csharp_generate_const_filter: fn(const_name: &str) -> bool) -> Builder {
222        self.options.csharp_generate_const_filter = csharp_generate_const_filter;
223        self
224    }
225
226    /// configure the mappings that C# type name from rust original type name, default `|x| x`
227    pub fn csharp_type_rename(mut self, csharp_type_rename: fn(rust_type_name: String) -> String) -> Builder {
228        self.options.csharp_type_rename = csharp_type_rename;
229        self
230    }
231
232    /// configure the additional header for the generated C# code.
233    pub fn csharp_file_header<T: Into<String>>(mut self, csharp_file_header: T) -> Builder {
234        self.options.csharp_file_header = csharp_file_header.into();
235        self
236    }
237
238    /// configure the additional footer for the generated C# code.
239    pub fn csharp_file_footer<T: Into<String>>(mut self, csharp_file_footer: T) -> Builder {
240        self.options.csharp_file_footer = csharp_file_footer.into();
241        self
242    }
243
244    pub fn generate_csharp_file<P: AsRef<Path>>(
245        &self,
246        csharp_output_path: P,
247    ) -> Result<(), Box<dyn Error>> {
248        if !self.options.input_bindgen_files.is_empty() {
249            let (_, csharp) = generate(GenerateKind::InputBindgen, &self.options)?;
250
251            let mut csharp_file = make_file(csharp_output_path.as_ref())?;
252            csharp_file.write_all(csharp.as_bytes())?;
253            csharp_file.flush()?;
254        }
255
256        if self.has_input_externals() {
257            let (_, csharp) = generate(GenerateKind::InputExtern, &self.options)?;
258
259            let mut csharp_file = make_file(csharp_output_path.as_ref())?;
260            csharp_file.write_all(csharp.as_bytes())?;
261            csharp_file.flush()?;
262        }
263
264        Ok(())
265    }
266
267    fn has_input_files(&self) -> bool {
268        !self.options.input_bindgen_files.is_empty()
269    }
270    fn has_input_externals(&self) -> bool {
271        !self.options.input_extern_files.is_empty()
272    }
273
274    pub fn generate_to_file<P: AsRef<Path>>(
275        &self,
276        rust_output_path: P,
277        csharp_output_path: P,
278    ) -> Result<(), Box<dyn Error>> {
279        if self.has_input_files() {
280            let (rust, csharp) = generate(GenerateKind::InputBindgen, &self.options)?;
281
282            if let Some(rust) = rust {
283                let mut rust_file = make_file(rust_output_path.as_ref())?;
284
285                rust_file.write_all(rust.as_bytes())?;
286                rust_file.flush()?;
287            }
288
289            let mut csharp_file = make_file(csharp_output_path.as_ref())?;
290            csharp_file.write_all(csharp.as_bytes())?;
291            csharp_file.flush()?;
292        }
293
294        if self.has_input_externals() {
295            let (rust, csharp) = generate(GenerateKind::InputExtern, &self.options)?;
296
297            if let Some(rust) = rust {
298                let mut rust_file = make_file(rust_output_path.as_ref())?;
299
300                rust_file.write_all(rust.as_bytes())?;
301                rust_file.flush()?;
302            }
303
304            let mut csharp_file = make_file(csharp_output_path.as_ref())?;
305            csharp_file.write_all(csharp.as_bytes())?;
306            csharp_file.flush()?;
307        }
308
309        Ok(())
310    }
311}
312
313fn make_file<P: AsRef<Path>>(path: P) -> io::Result<File> {
314    let path = path.as_ref();
315    if let Some(parent) = path.parent() {
316        std::fs::create_dir_all(parent)?;
317    }
318    let file = OpenOptions::new()
319        .write(true)
320        .truncate(true)
321        .create(true)
322        .open(path)?;
323    Ok(file)
324}