1use 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#[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#[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#[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#[derive(Debug, Clone, Deserialize, Default)]
112#[serde(rename_all = "snake_case")]
113#[serde(deny_unknown_fields)]
114#[serde(default)]
115pub struct ExportConfig {
116 pub include: Vec<String>,
119 pub exclude: Vec<String>,
121 pub rename: HashMap<String, String>,
123 pub body: HashMap<String, String>,
125 pub prefix: Option<String>,
127 pub item_types: Vec<ItemType>,
129 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#[derive(Debug, Default, Clone, Deserialize)]
157#[serde(rename_all = "snake_case")]
158#[serde(deny_unknown_fields)]
159#[serde(default)]
160pub struct LayoutConfig {
161 pub packed: Option<String>,
163 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#[derive(Debug, Clone, Deserialize)]
180#[serde(rename_all = "snake_case")]
181#[serde(deny_unknown_fields)]
182#[serde(default)]
183pub struct FunctionConfig {
184 pub prefix: Option<String>,
186 pub postfix: Option<String>,
188 pub must_use: Option<String>,
190 pub args: Layout,
192 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#[derive(Debug, Default, Clone, Deserialize)]
226#[serde(rename_all = "snake_case")]
227#[serde(deny_unknown_fields)]
228#[serde(default)]
229pub struct StructConfig {
230 pub rename_fields: Option<RenameRule>,
232 pub derive_constructor: bool,
235 pub derive_eq: bool,
237 pub derive_neq: bool,
239 pub derive_lt: bool,
241 pub derive_lte: bool,
243 pub derive_gt: bool,
245 pub derive_gte: bool,
247 pub associated_constants_in_body: bool,
250 pub must_use: Option<String>,
252}
253
254#[derive(Debug, Clone, Deserialize)]
256#[serde(rename_all = "snake_case")]
257#[serde(deny_unknown_fields)]
258#[serde(default)]
259pub struct EnumConfig {
260 pub rename_variants: Option<RenameRule>,
262 pub add_sentinel: bool,
265 pub prefix_with_name: bool,
267 pub derive_helper_methods: bool,
270 pub derive_const_casts: bool,
272 pub derive_mut_casts: bool,
274 pub cast_assert_name: Option<String>,
278 pub must_use: Option<String>,
280 pub derive_tagged_enum_destructor: bool,
282 pub derive_tagged_enum_copy_constructor: bool,
284 pub derive_tagged_enum_copy_assignment: bool,
289 pub enum_class: bool,
292 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#[derive(Debug, Clone, Deserialize)]
319#[serde(rename_all = "snake_case")]
320#[serde(deny_unknown_fields)]
321#[serde(default)]
322pub struct ConstantConfig {
323 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#[derive(Debug, Clone, Deserialize, Default)]
337#[serde(rename_all = "snake_case")]
338#[serde(deny_unknown_fields)]
339#[serde(default)]
340pub struct MacroExpansionConfig {
341 pub bitflags: bool,
343}
344
345#[derive(Debug, Clone, Deserialize)]
347#[serde(rename_all = "snake_case")]
348#[serde(deny_unknown_fields)]
349#[serde(default)]
350pub struct ParseExpandConfig {
351 pub crates: Vec<String>,
353 pub all_features: bool,
355 pub default_features: bool,
357 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
373fn 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#[derive(Debug, Default, Clone, Deserialize)]
413#[serde(rename_all = "snake_case")]
414#[serde(deny_unknown_fields)]
415#[serde(default)]
416pub struct ParseConfig {
417 pub parse_deps: bool,
426 pub include: Option<Vec<String>>,
428 pub exclude: Vec<String>,
430 #[serde(deserialize_with = "retrocomp_parse_expand_config_deserialize")]
432 pub expand: ParseExpandConfig,
433 pub clean: bool,
436 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 return true;
450 }
451
452 self.extra_bindings.iter().any(|dep| dep == crate_name)
453 }
454}
455
456#[derive(Debug, Clone, Deserialize)]
458#[serde(rename_all = "snake_case")]
459#[serde(deny_unknown_fields)]
460#[serde(default)]
461pub struct Config {
462 pub header: Option<String>,
464 pub includes: Vec<String>,
466 pub imports: Vec<String>,
468 pub trailer: Option<String>,
470 pub no_includes: bool,
475 pub autogen_warning: Option<String>,
477 pub include_version: bool,
479 pub line_length: usize,
481 pub tab_width: usize,
483 pub parse: ParseConfig,
485 pub export: ExportConfig,
487 pub macro_expansion: MacroExpansionConfig,
489 pub layout: LayoutConfig,
491 #[serde(rename = "fn")]
493 pub function: FunctionConfig,
494 #[serde(rename = "struct")]
496 pub structure: StructConfig,
497 #[serde(rename = "enum")]
499 pub enumeration: EnumConfig,
500 #[serde(rename = "const")]
502 pub constant: ConstantConfig,
503 pub defines: HashMap<String, String>,
505 pub documentation: bool,
507 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}