Skip to main content

nbindgen/bindgen/
config.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5use std::collections::HashMap;
6use std::default::Default;
7use std::str::FromStr;
8use std::{fmt, fs, path::Path as StdPath};
9
10use serde::de::value::{MapAccessDeserializer, SeqAccessDeserializer};
11use serde::de::{Deserialize, Deserializer, MapAccess, SeqAccess, Visitor};
12
13use toml;
14
15use crate::bindgen::ir::annotation::AnnotationSet;
16use crate::bindgen::ir::path::Path;
17use crate::bindgen::ir::repr::ReprAlign;
18pub use crate::bindgen::rename::RenameRule;
19
20pub const VERSION: &str = env!("CARGO_PKG_VERSION");
21
22/// A type of layout to use when generating long lines of code.
23#[derive(Debug, Clone, PartialEq)]
24pub enum Layout {
25    Horizontal,
26    Vertical,
27    Auto,
28}
29
30impl FromStr for Layout {
31    type Err = String;
32
33    fn from_str(s: &str) -> Result<Layout, Self::Err> {
34        match s {
35            "Horizontal" => Ok(Layout::Horizontal),
36            "horizontal" => Ok(Layout::Horizontal),
37            "Vertical" => Ok(Layout::Vertical),
38            "vertical" => Ok(Layout::Vertical),
39            "Auto" => Ok(Layout::Auto),
40            "auto" => Ok(Layout::Auto),
41            _ => Err(format!("Unrecognized Layout: '{}'.", s)),
42        }
43    }
44}
45
46deserialize_enum_str!(Layout);
47
48/// How the comments containing documentation should be styled.
49#[derive(Debug, Clone, PartialEq, Copy)]
50pub enum DocumentationStyle {
51    C,
52    C99,
53    Doxy,
54    Cxx,
55    Auto,
56}
57
58impl FromStr for DocumentationStyle {
59    type Err = String;
60
61    fn from_str(s: &str) -> Result<DocumentationStyle, Self::Err> {
62        match s.to_lowercase().as_ref() {
63            "c" => Ok(DocumentationStyle::C),
64            "c99" => Ok(DocumentationStyle::C99),
65            "cxx" => Ok(DocumentationStyle::Cxx),
66            "c++" => Ok(DocumentationStyle::Cxx),
67            "doxy" => Ok(DocumentationStyle::Doxy),
68            "auto" => Ok(DocumentationStyle::Auto),
69            _ => Err(format!("Unrecognized documentation style: '{}'.", s)),
70        }
71    }
72}
73
74deserialize_enum_str!(DocumentationStyle);
75
76/// Different item types that we can generate and filter.
77#[derive(Debug, Clone, PartialEq)]
78pub enum ItemType {
79    Constants,
80    Globals,
81    Enums,
82    Structs,
83    Unions,
84    Typedefs,
85    OpaqueItems,
86    Functions,
87}
88
89impl FromStr for ItemType {
90    type Err = String;
91
92    fn from_str(s: &str) -> Result<Self, Self::Err> {
93        use self::ItemType::*;
94        Ok(match &*s.to_lowercase() {
95            "constants" => Constants,
96            "globals" => Globals,
97            "enums" => Enums,
98            "structs" => Structs,
99            "unions" => Unions,
100            "typedefs" => Typedefs,
101            "opaque" => OpaqueItems,
102            "functions" => Functions,
103            _ => return Err(format!("Unrecognized Style: '{}'.", s)),
104        })
105    }
106}
107
108deserialize_enum_str!(ItemType);
109
110/// Settings to apply when exporting items.
111#[derive(Debug, Clone, Deserialize, Default)]
112#[serde(rename_all = "snake_case")]
113#[serde(deny_unknown_fields)]
114#[serde(default)]
115pub struct ExportConfig {
116    /// A list of additional items not used by exported functions to include in
117    /// the generated bindings
118    pub include: Vec<String>,
119    /// A list of items to not include in the generated bindings
120    pub exclude: Vec<String>,
121    /// Table of name conversions to apply to item names
122    pub rename: HashMap<String, String>,
123    /// Table of raw strings to append to the body of items.
124    pub body: HashMap<String, String>,
125    /// A prefix to add before the name of every item
126    pub prefix: Option<String>,
127    /// Types of items to generate.
128    pub item_types: Vec<ItemType>,
129    /// Whether renaming overrides or extends prefixing.
130    pub renaming_overrides_prefixing: bool,
131}
132
133impl ExportConfig {
134    pub(crate) fn should_generate(&self, item_type: ItemType) -> bool {
135        self.item_types.is_empty() || self.item_types.contains(&item_type)
136    }
137
138    pub(crate) fn extra_body(&self, path: &Path) -> Option<&str> {
139        self.body.get(path.name()).map(|s| s.trim_matches('\n'))
140    }
141
142    pub(crate) fn rename(&self, item_name: &mut String) {
143        if let Some(name) = self.rename.get(item_name) {
144            *item_name = name.clone();
145            if self.renaming_overrides_prefixing {
146                return;
147            }
148        }
149        if let Some(ref prefix) = self.prefix {
150            item_name.insert_str(0, &prefix);
151        }
152    }
153}
154
155/// Settings to apply to generated types with layout modifiers.
156#[derive(Debug, Default, Clone, Deserialize)]
157#[serde(rename_all = "snake_case")]
158#[serde(deny_unknown_fields)]
159#[serde(default)]
160pub struct LayoutConfig {
161    /// The way to annotate C types as #[repr(packed)].
162    pub packed: Option<String>,
163    /// The way to annotate C types as #[repr(align(...))]. This is assumed to be a functional
164    /// macro which takes a single argument (the alignment).
165    pub aligned_n: Option<String>,
166}
167
168impl LayoutConfig {
169    pub(crate) fn ensure_safe_to_represent(&self, align: &ReprAlign) -> Result<(), String> {
170        match (align, &self.packed, &self.aligned_n) {
171            (ReprAlign::Packed, None, _) => Err("Cannot safely represent #[repr(packed)] type without configured 'packed' annotation.".to_string()),
172            (ReprAlign::Align(_), _, None) => Err("Cannot safely represent #[repr(aligned(...))] type without configured 'aligned_n' annotation.".to_string()),
173            _ => Ok(()),
174        }
175    }
176}
177
178/// Settings to apply to generated functions.
179#[derive(Debug, Clone, Deserialize)]
180#[serde(rename_all = "snake_case")]
181#[serde(deny_unknown_fields)]
182#[serde(default)]
183pub struct FunctionConfig {
184    /// Optional text to output before each function declaration
185    pub prefix: Option<String>,
186    /// Optional text to output after each function declaration
187    pub postfix: Option<String>,
188    /// The way to annotation this function as #[must_use].
189    pub must_use: Option<String>,
190    /// The style to layout the args
191    pub args: Layout,
192    /// The rename rule to apply to function args
193    pub rename_args: Option<RenameRule>,
194}
195
196impl Default for FunctionConfig {
197    fn default() -> FunctionConfig {
198        FunctionConfig {
199            prefix: None,
200            postfix: None,
201            must_use: None,
202            args: Layout::Auto,
203            rename_args: None,
204        }
205    }
206}
207
208impl FunctionConfig {
209    pub(crate) fn prefix(&self, annotations: &AnnotationSet) -> Option<String> {
210        if let Some(x) = annotations.atom("prefix") {
211            return x;
212        }
213        self.prefix.clone()
214    }
215
216    pub(crate) fn postfix(&self, annotations: &AnnotationSet) -> Option<String> {
217        if let Some(x) = annotations.atom("postfix") {
218            return x;
219        }
220        self.postfix.clone()
221    }
222}
223
224/// Settings to apply to generated structs.
225#[derive(Debug, Default, Clone, Deserialize)]
226#[serde(rename_all = "snake_case")]
227#[serde(deny_unknown_fields)]
228#[serde(default)]
229pub struct StructConfig {
230    /// The rename rule to apply to the name of struct fields
231    pub rename_fields: Option<RenameRule>,
232    /// Whether to generate a constructor for the struct (which takes
233    /// arguments to initialize all the members)
234    pub derive_constructor: bool,
235    /// Whether to generate a piecewise equality operator
236    pub derive_eq: bool,
237    /// Whether to generate a piecewise inequality operator
238    pub derive_neq: bool,
239    /// Whether to generate a less than operator on structs with one field
240    pub derive_lt: bool,
241    /// Whether to generate a less than or equal to operator on structs with one field
242    pub derive_lte: bool,
243    /// Whether to generate a greater than operator on structs with one field
244    pub derive_gt: bool,
245    /// Whether to generate a greater than or equal to operator on structs with one field
246    pub derive_gte: bool,
247    /// Whether associated constants should be in the body. Only applicable to
248    /// non-transparent structs, and in C++-only.
249    pub associated_constants_in_body: bool,
250    /// The way to annotate this struct as #[must_use].
251    pub must_use: Option<String>,
252}
253
254/// Settings to apply to generated enums.
255#[derive(Debug, Clone, Deserialize)]
256#[serde(rename_all = "snake_case")]
257#[serde(deny_unknown_fields)]
258#[serde(default)]
259pub struct EnumConfig {
260    /// The rename rule to apply to the name of enum variants
261    pub rename_variants: Option<RenameRule>,
262    /// Whether to add a `Sentinel` value at the end of every enum
263    /// This is useful in Gecko for IPC serialization
264    pub add_sentinel: bool,
265    /// Whether the enum variants should be prefixed with the enum name
266    pub prefix_with_name: bool,
267    /// Whether to generate static `::X(..)` constructors and `IsX()`
268    /// methods for tagged enums.
269    pub derive_helper_methods: bool,
270    /// Whether to generate `AsX() const` methods for tagged enums.
271    pub derive_const_casts: bool,
272    /// Whether to generate `AsX()` methods for tagged enums.
273    pub derive_mut_casts: bool,
274    /// The name of the macro to use for `derive_{const,mut}casts`. If custom, you're
275    /// responsible to provide the necessary header, otherwise `assert` will be
276    /// used, and `<cassert>` will be included.
277    pub cast_assert_name: Option<String>,
278    /// The way to annotation this enum as #[must_use].
279    pub must_use: Option<String>,
280    /// Whether to generate destructors of tagged enums.
281    pub derive_tagged_enum_destructor: bool,
282    /// Whether to generate copy-constructors of tagged enums.
283    pub derive_tagged_enum_copy_constructor: bool,
284    /// Whether to generate copy-assignment operators of tagged enums.
285    ///
286    /// This is only generated if a copy constructor for the same tagged enum is
287    /// generated as well.
288    pub derive_tagged_enum_copy_assignment: bool,
289    /// Declare the enum as an enum class.
290    /// Only relevant when targeting C++.
291    pub enum_class: bool,
292    /// Whether to generate empty, private default-constructors for tagged
293    /// enums.
294    pub private_default_tagged_enum_constructor: bool,
295}
296
297impl Default for EnumConfig {
298    fn default() -> EnumConfig {
299        EnumConfig {
300            rename_variants: None,
301            add_sentinel: false,
302            prefix_with_name: false,
303            derive_helper_methods: false,
304            derive_const_casts: false,
305            derive_mut_casts: false,
306            cast_assert_name: None,
307            must_use: None,
308            derive_tagged_enum_destructor: false,
309            derive_tagged_enum_copy_constructor: false,
310            derive_tagged_enum_copy_assignment: false,
311            enum_class: true,
312            private_default_tagged_enum_constructor: false,
313        }
314    }
315}
316
317/// Settings to apply to generated constants.
318#[derive(Debug, Clone, Deserialize)]
319#[serde(rename_all = "snake_case")]
320#[serde(deny_unknown_fields)]
321#[serde(default)]
322pub struct ConstantConfig {
323    /// Whether a generated constant can be a static const in C++ mode.
324    pub allow_static_const: bool,
325}
326
327impl Default for ConstantConfig {
328    fn default() -> ConstantConfig {
329        ConstantConfig {
330            allow_static_const: true,
331        }
332    }
333}
334
335/// Settings for custom macro expansion.
336#[derive(Debug, Clone, Deserialize, Default)]
337#[serde(rename_all = "snake_case")]
338#[serde(deny_unknown_fields)]
339#[serde(default)]
340pub struct MacroExpansionConfig {
341    /// Whether the `bitflags` macro should be expanded.
342    pub bitflags: bool,
343}
344
345/// Settings to apply when running `rustc --pretty=expanded`
346#[derive(Debug, Clone, Deserialize)]
347#[serde(rename_all = "snake_case")]
348#[serde(deny_unknown_fields)]
349#[serde(default)]
350pub struct ParseExpandConfig {
351    /// The names of crates to parse with `rustc --pretty=expanded`
352    pub crates: Vec<String>,
353    /// Whether to enable all the features when expanding.
354    pub all_features: bool,
355    /// Whether to use the default feature set when expanding.
356    pub default_features: bool,
357    /// List of features to use when expanding. Combines with `default_features` like in
358    /// `Cargo.toml`.
359    pub features: Option<Vec<String>>,
360}
361
362impl Default for ParseExpandConfig {
363    fn default() -> ParseExpandConfig {
364        ParseExpandConfig {
365            crates: Vec::new(),
366            all_features: false,
367            default_features: true,
368            features: None,
369        }
370    }
371}
372
373// Backwards-compatibility deserializer for ParseExpandConfig. This allows accepting both the
374// simple `expand = ["crate"]` and the more complex `expand = {"crates": ["crate"],
375// "default_features": false}` format for the `expand` key.
376//
377// Note that one (major) difference between the two forms is that, for backwards-compatibility
378// reasons, the `expand = ["crate"]` form will enable the `--all-features` flag by default while
379// the `expand = {"crates": ["crate"]}` form will use the default feature set by default.
380fn retrocomp_parse_expand_config_deserialize<'de, D: Deserializer<'de>>(
381    deserializer: D,
382) -> Result<ParseExpandConfig, D::Error> {
383    struct ParseExpandVisitor;
384
385    impl<'de> Visitor<'de> for ParseExpandVisitor {
386        type Value = ParseExpandConfig;
387
388        fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
389            formatter.write_str("a map or sequence of string")
390        }
391
392        fn visit_seq<A: SeqAccess<'de>>(self, seq: A) -> Result<Self::Value, A::Error> {
393            let crates =
394                <Vec<String> as Deserialize>::deserialize(SeqAccessDeserializer::new(seq))?;
395            Ok(ParseExpandConfig {
396                crates,
397                all_features: true,
398                default_features: true,
399                features: None,
400            })
401        }
402
403        fn visit_map<A: MapAccess<'de>>(self, map: A) -> Result<Self::Value, A::Error> {
404            <ParseExpandConfig as Deserialize>::deserialize(MapAccessDeserializer::new(map))
405        }
406    }
407
408    deserializer.deserialize_any(ParseExpandVisitor)
409}
410
411/// Settings to apply when parsing.
412#[derive(Debug, Default, Clone, Deserialize)]
413#[serde(rename_all = "snake_case")]
414#[serde(deny_unknown_fields)]
415#[serde(default)]
416pub struct ParseConfig {
417    /// Whether to parse dependencies when generating bindings. When this is true,
418    /// each dependent crate is found using a combination of `cargo metadata` and
419    /// `Cargo.lock`. To further control this behavior, crates can be whitelisted or
420    /// blacklisted using `include` and `exclude` respectively. Additionally in cases
421    /// where crates have types to expose in bindings hidden in macros, a crate can
422    /// be marked in `expand` and `cargo expand` will be used to expand the macros
423    /// before parsing. A crate marked in `expand` doesn't need to be added to any
424    /// whitelist.
425    pub parse_deps: bool,
426    /// An optional whitelist of names of crates to parse
427    pub include: Option<Vec<String>>,
428    /// The names of crates to not parse
429    pub exclude: Vec<String>,
430    /// The configuration options for `rustc --pretty=expanded`
431    #[serde(deserialize_with = "retrocomp_parse_expand_config_deserialize")]
432    pub expand: ParseExpandConfig,
433    /// Whether to use a new temporary target directory when running `rustc --pretty=expanded`.
434    /// This may be required for some build processes.
435    pub clean: bool,
436    /// List of crate names which generate consts, statics, and fns. By default
437    /// no dependent crates generate them.
438    pub extra_bindings: Vec<String>,
439}
440
441impl ParseConfig {
442    pub(crate) fn should_generate_top_level_item(
443        &self,
444        crate_name: &str,
445        binding_crate_name: &str,
446    ) -> bool {
447        if crate_name == binding_crate_name {
448            // Always generate items for the binding crate.
449            return true;
450        }
451
452        self.extra_bindings.iter().any(|dep| dep == crate_name)
453    }
454}
455
456/// A collection of settings to customize the generated bindings.
457#[derive(Debug, Clone, Deserialize)]
458#[serde(rename_all = "snake_case")]
459#[serde(deny_unknown_fields)]
460#[serde(default)]
461pub struct Config {
462    /// Optional text to output at the beginning of the file
463    pub header: Option<String>,
464    /// A list of includes to put at the beginning of the generated header (avoid)
465    pub includes: Vec<String>,
466    /// A list of import to put at the beginning of the generated header
467    pub imports: Vec<String>,
468    /// Optional text to output at the end of the file
469    pub trailer: Option<String>,
470    /// Generates no includes at all. Overrides all other include options
471    ///
472    /// This option is useful when using cbindgen with tools such as python's cffi which
473    /// doesn't understand include directives
474    pub no_includes: bool,
475    /// Optional text to output at major sections to deter manual editing
476    pub autogen_warning: Option<String>,
477    /// Include a comment with the version of cbindgen used to generate the file
478    pub include_version: bool,
479    /// The preferred length of a line, used for auto breaking function arguments
480    pub line_length: usize,
481    /// The amount of spaces in a tab
482    pub tab_width: usize,
483    /// The configuration options for parsing
484    pub parse: ParseConfig,
485    /// The configuration options for exporting
486    pub export: ExportConfig,
487    /// The configuration options for macros.
488    pub macro_expansion: MacroExpansionConfig,
489    /// The configuration options for type layouts.
490    pub layout: LayoutConfig,
491    /// The configuration options for functions
492    #[serde(rename = "fn")]
493    pub function: FunctionConfig,
494    /// The configuration options for structs
495    #[serde(rename = "struct")]
496    pub structure: StructConfig,
497    /// The configuration options for enums
498    #[serde(rename = "enum")]
499    pub enumeration: EnumConfig,
500    /// The configuration options for constants
501    #[serde(rename = "const")]
502    pub constant: ConstantConfig,
503    /// Preprocessor defines to use when generating #ifdef's for #[cfg]
504    pub defines: HashMap<String, String>,
505    /// Include doc comments from rust as documentation
506    pub documentation: bool,
507    /// How documentation comments should be styled.
508    pub documentation_style: DocumentationStyle,
509}
510
511impl Default for Config {
512    fn default() -> Config {
513        Config {
514            header: None,
515            includes: Vec::new(),
516            imports: Vec::new(),
517            trailer: None,
518            autogen_warning: None,
519            include_version: false,
520            no_includes: false,
521            line_length: 100,
522            tab_width: 2,
523            macro_expansion: Default::default(),
524            parse: ParseConfig::default(),
525            export: ExportConfig::default(),
526            layout: LayoutConfig::default(),
527            function: FunctionConfig::default(),
528            structure: StructConfig::default(),
529            enumeration: EnumConfig::default(),
530            constant: ConstantConfig::default(),
531            defines: HashMap::new(),
532            documentation: true,
533            documentation_style: DocumentationStyle::Auto,
534        }
535    }
536}
537
538impl Config {
539    pub fn from_file<P: AsRef<StdPath>>(file_name: P) -> Result<Config, String> {
540        let config_text = fs::read_to_string(file_name.as_ref()).or_else(|_| {
541            Err(format!(
542                "Couldn't open config file: {}.",
543                file_name.as_ref().display()
544            ))
545        })?;
546
547        match toml::from_str::<Config>(&config_text) {
548            Ok(x) => Ok(x),
549            Err(e) => Err(format!("Couldn't parse config file: {}.", e)),
550        }
551    }
552
553    pub fn from_root_or_default<P: AsRef<StdPath>>(root: P) -> Config {
554        let c = root.as_ref().join("cbindgen.toml");
555
556        if c.exists() {
557            Config::from_file(c).unwrap()
558        } else {
559            Config::default()
560        }
561    }
562}