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