atypical_commit/
config.rs1use serde::Deserialize;
5
6use crate::{DelimitedBy, EnclosureToken, Sequence, Tokens};
7
8pub const SECTION: &str = "commit";
9
10#[derive(Debug, Clone, PartialEq)]
11#[derive(Deserialize)]
12#[serde(deny_unknown_fields, rename_all = "kebab-case", default)]
13pub struct CommitConfig {
14 pub keywords: Vec<String>,
15 pub modifiers: Vec<String>,
16 pub enclosures: Vec<EnclosureConfig>,
17 pub separator: char,
18 pub modifier_sequence: Sequence,
19 pub default_ignores: bool,
22}
23
24#[derive(Debug, Clone, PartialEq)]
25#[derive(Deserialize)]
26#[serde(deny_unknown_fields, rename_all = "kebab-case")]
27pub struct EnclosureConfig {
28 pub delimiters: DelimitedBy,
29 pub allowed: Option<Vec<String>>,
31}
32
33impl Default for CommitConfig {
34 fn default() -> Self {
35 (&Tokens::preset_standard()).into()
36 }
37}
38
39impl From<&Tokens<'_>> for CommitConfig {
40 fn from(tokens: &Tokens<'_>) -> Self {
41 fn owned(v: &[&str]) -> Vec<String> {
42 v.iter().map(ToString::to_string).collect()
43 }
44
45 Self {
46 keywords: owned(&tokens.keywords),
47 modifiers: owned(&tokens.modifiers),
48 enclosures: tokens
49 .enclosures
50 .iter()
51 .map(|enclosure| EnclosureConfig {
52 delimiters: enclosure.delimiters(),
53 allowed: match enclosure {
54 EnclosureToken::Flexible(_) => None,
55 EnclosureToken::Strict(_, allowed) => {
56 Some(owned(allowed))
57 }
58 },
59 })
60 .collect(),
61 separator: tokens.separator,
62 modifier_sequence: tokens.modifier_sequence,
63 default_ignores: true,
64 }
65 }
66}
67
68impl<'i> From<&'i CommitConfig> for Tokens<'i> {
69 fn from(config: &'i CommitConfig) -> Self {
70 fn borrowed(v: &[String]) -> Vec<&str> {
71 v.iter().map(String::as_str).collect()
72 }
73
74 Self {
75 keywords: borrowed(&config.keywords),
76 modifiers: borrowed(&config.modifiers),
77 enclosures: config
78 .enclosures
79 .iter()
80 .map(|enclosure| match &enclosure.allowed {
81 None => EnclosureToken::Flexible(enclosure.delimiters),
82 Some(allowed) => EnclosureToken::Strict(
83 enclosure.delimiters,
84 borrowed(allowed),
85 ),
86 })
87 .collect(),
88 separator: config.separator,
89 modifier_sequence: config.modifier_sequence,
90 }
91 }
92}
93
94#[cfg(test)]
95mod tests {
96 use super::*;
97
98 #[test]
99 fn test_default_mirrors_the_standard_preset() {
100 let config = CommitConfig::default();
101
102 assert_eq!(Tokens::from(&config), Tokens::preset_standard());
103 }
104
105 #[test]
106 fn test_partial_section_keeps_preset_defaults() {
107 let config: CommitConfig =
108 toml::from_str(r#"keywords = ["feat", "fix"]"#).unwrap();
109
110 assert_eq!(config.keywords, vec!["feat", "fix"]);
111 assert_eq!(config.modifiers, CommitConfig::default().modifiers);
112 assert_eq!(config.separator, ':');
113 }
114
115 #[test]
116 fn test_enclosures_map_to_strict_and_flexible() {
117 let config: CommitConfig = toml::from_str(indoc::indoc! {r#"
118 [[enclosures]]
119 delimiters = ["(", ")"]
120 allowed = ["core"]
121
122 [[enclosures]]
123 delimiters = ["{", "}"]
124 "#})
125 .unwrap();
126
127 assert_eq!(
128 Tokens::from(&config).enclosures,
129 vec![
130 EnclosureToken::Strict(['(', ')'], vec!["core"]),
131 EnclosureToken::Flexible(['{', '}']),
132 ]
133 );
134 }
135
136 #[test]
137 fn test_default_ignores_is_on_unless_disabled() {
138 assert!(CommitConfig::default().default_ignores);
139
140 let config: CommitConfig =
141 toml::from_str("default-ignores = false").unwrap();
142
143 assert!(!config.default_ignores);
144 }
145
146 #[test]
147 fn test_modifier_sequence_names() {
148 let config: CommitConfig =
149 toml::from_str(r#"modifier-sequence = "post""#).unwrap();
150
151 assert_eq!(config.modifier_sequence, Sequence::Post);
152
153 assert!(
154 toml::from_str::<CommitConfig>(r#"modifier-sequence = "sideways""#)
155 .is_err()
156 );
157 }
158
159 #[test]
160 fn test_unknown_fields_are_rejected() {
161 assert!(
162 toml::from_str::<CommitConfig>(r#"keyword = ["typo"]"#).is_err()
163 );
164 }
165}