1use crate::{
2 CallParenType, CollapseSimpleStatement, Config, IndentType, LineEndings, QuoteStyle,
3 SortRequiresConfig, SpaceAfterFunctionNames,
4};
5use ec4rs::{
6 properties_of,
7 property::{EndOfLine, IndentSize, IndentStyle, MaxLineLen, TabWidth, UnknownValueError},
8 rawvalue::RawValue,
9 Error, Properties, PropertyKey, PropertyValue,
10};
11use std::path::Path;
12
13macro_rules! property_choice {
15 ($prop_id:ident, $name:literal; $(($variant:ident, $string:literal)),+) => {
16 #[derive(Clone, Copy, PartialEq, Eq, Debug)]
17 #[repr(u8)]
18 pub enum $prop_id {$($variant),+}
19
20 impl PropertyValue for $prop_id {
21 const MAYBE_UNSET: bool = false;
22 type Err = UnknownValueError;
23 fn parse(raw: &RawValue) -> Result<Self, Self::Err> {
24 match raw.into_str().to_lowercase().as_str() {
25 $($string => Ok($prop_id::$variant),)+
26 _ => Err(UnknownValueError)
27 }
28 }
29 }
30
31 impl From<$prop_id> for RawValue {
32 fn from(val: $prop_id) -> RawValue {
33 match val {
34 $($prop_id::$variant => RawValue::from($string)),*
35 }
36 }
37 }
38
39 impl PropertyKey for $prop_id {
40 fn key() -> &'static str {$name}
41 }
42
43 impl std::fmt::Display for $prop_id {
44 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
45 write!(f, "{}", match self {
46 $($prop_id::$variant => $string),*
47 })
48 }
49 }
50 }
51}
52
53property_choice! {
54 QuoteTypeChoice, "quote_type";
55 (Double, "double"),
56 (Single, "single"),
57 (Auto, "auto")
58}
59
60property_choice! {
61 CallParenthesesChoice, "call_parentheses";
62 (Always, "always"),
63 (NoSingleString, "nosinglestring"),
64 (NoSingleTable, "nosingletable"),
65 (None, "none")
66}
67
68property_choice! {
69 SpaceAfterFunctionNamesChoice, "space_after_function_names";
70 (Always, "always"),
71 (Definitions, "definitions"),
72 (Calls, "calls"),
73 (Never, "never")
74}
75
76property_choice! {
77 CollapseSimpleStatementChoice, "collapse_simple_statement";
78 (Never, "never"),
79 (FunctionOnly, "functiononly"),
80 (ConditionalOnly, "conditionalonly"),
81 (Always, "always")
82}
83
84property_choice! {
85 SortRequiresChoice, "sort_requires";
86 (True, "true"),
87 (False, "false")
88}
89
90fn load(mut config: Config, properties: &Properties) -> Config {
92 if let Ok(end_of_line) = properties.get::<EndOfLine>() {
93 match end_of_line {
94 EndOfLine::Cr | EndOfLine::Lf => config.line_endings = LineEndings::Unix,
95 EndOfLine::CrLf => config.line_endings = LineEndings::Windows,
96 }
97 }
98 if let Ok(indent_size) = properties.get::<IndentSize>() {
99 match indent_size {
100 IndentSize::Value(indent_width) => config.indent_width = indent_width,
101 IndentSize::UseTabWidth => {
102 if let Ok(TabWidth::Value(indent_width)) = properties.get::<TabWidth>() {
103 config.indent_width = indent_width
104 }
105 }
106 }
107 }
108 if let Ok(indent_style) = properties.get::<IndentStyle>() {
109 match indent_style {
110 IndentStyle::Tabs => config.indent_type = IndentType::Tabs,
111 IndentStyle::Spaces => config.indent_type = IndentType::Spaces,
112 }
113 }
114 if let Ok(max_line_length) = properties.get::<MaxLineLen>() {
115 match max_line_length {
116 MaxLineLen::Value(column_width) => config.column_width = column_width,
117 MaxLineLen::Off => config.column_width = usize::MAX,
118 }
119 }
120 if let Ok(quote_type) = properties.get::<QuoteTypeChoice>() {
121 match quote_type {
122 QuoteTypeChoice::Double => config.quote_style = QuoteStyle::AutoPreferDouble,
123 QuoteTypeChoice::Single => config.quote_style = QuoteStyle::AutoPreferSingle,
124 QuoteTypeChoice::Auto => (),
125 }
126 }
127 if let Ok(call_parentheses) = properties.get::<CallParenthesesChoice>() {
128 match call_parentheses {
129 CallParenthesesChoice::Always => config.call_parentheses = CallParenType::Always,
130 CallParenthesesChoice::NoSingleString => {
131 config.call_parentheses = CallParenType::NoSingleString
132 }
133 CallParenthesesChoice::NoSingleTable => {
134 config.call_parentheses = CallParenType::NoSingleTable
135 }
136 CallParenthesesChoice::None => config.call_parentheses = CallParenType::None,
137 }
138 }
139 if let Ok(space_after_function_names) = properties.get::<SpaceAfterFunctionNamesChoice>() {
140 match space_after_function_names {
141 SpaceAfterFunctionNamesChoice::Always => {
142 config.space_after_function_names = SpaceAfterFunctionNames::Always
143 }
144 SpaceAfterFunctionNamesChoice::Definitions => {
145 config.space_after_function_names = SpaceAfterFunctionNames::Definitions
146 }
147 SpaceAfterFunctionNamesChoice::Calls => {
148 config.space_after_function_names = SpaceAfterFunctionNames::Calls
149 }
150 SpaceAfterFunctionNamesChoice::Never => {
151 config.space_after_function_names = SpaceAfterFunctionNames::Never
152 }
153 }
154 }
155 if let Ok(collapse_simple_statement) = properties.get::<CollapseSimpleStatementChoice>() {
156 match collapse_simple_statement {
157 CollapseSimpleStatementChoice::Never => {
158 config.collapse_simple_statement = CollapseSimpleStatement::Never
159 }
160 CollapseSimpleStatementChoice::FunctionOnly => {
161 config.collapse_simple_statement = CollapseSimpleStatement::FunctionOnly
162 }
163 CollapseSimpleStatementChoice::ConditionalOnly => {
164 config.collapse_simple_statement = CollapseSimpleStatement::ConditionalOnly
165 }
166 CollapseSimpleStatementChoice::Always => {
167 config.collapse_simple_statement = CollapseSimpleStatement::Always
168 }
169 }
170 }
171 if let Ok(sort_requires) = properties.get::<SortRequiresChoice>() {
172 match sort_requires {
173 SortRequiresChoice::True => config.sort_requires = SortRequiresConfig { enabled: true },
174 SortRequiresChoice::False => {
175 config.sort_requires = SortRequiresConfig { enabled: false }
176 }
177 }
178 }
179
180 config
181}
182
183pub fn parse(config: Config, path: &Path) -> Result<Config, Error> {
185 let properties = properties_of(path)?;
186
187 if properties.iter().count() == 0 {
188 return Ok(config);
189 }
190
191 log::debug!("editorconfig: found properties for {}", path.display());
192 let new_config = load(config, &properties);
193
194 Ok(new_config)
195}
196
197#[cfg(test)]
198mod tests {
199 use super::*;
200
201 impl From<&Properties> for Config {
202 fn from(properties: &Properties) -> Self {
203 load(Config::default(), properties)
204 }
205 }
206
207 #[test]
208 fn test_end_of_line_cr() {
209 let mut properties = Properties::new();
210 properties.insert_raw_for_key("end_of_line", "CR");
211 let config = Config::from(&properties);
212 assert_eq!(config.line_endings, LineEndings::Unix);
213 }
214
215 #[test]
216 fn test_end_of_line_lf() {
217 let mut properties = Properties::new();
218 properties.insert_raw_for_key("end_of_line", "lf");
219 let config = Config::from(&properties);
220 assert_eq!(config.line_endings, LineEndings::Unix);
221 }
222
223 #[test]
224 fn test_end_of_line_crlf() {
225 let mut properties = Properties::new();
226 properties.insert_raw_for_key("end_of_line", "CrLf");
227 let config = Config::from(&properties);
228 assert_eq!(config.line_endings, LineEndings::Windows);
229 }
230
231 #[test]
232 fn test_indent_size() {
233 let mut properties = Properties::new();
234 properties.insert_raw_for_key("indent_size", "2");
235 let config = Config::from(&properties);
236 assert_eq!(config.indent_width, 2);
237 }
238
239 #[test]
240 fn test_indent_size_use_tab_width() {
241 let mut properties = Properties::new();
242 properties.insert_raw_for_key("tab_width", "8");
243 properties.insert_raw_for_key("indent_size", "tab");
244 let config = Config::from(&properties);
245 assert_eq!(config.indent_width, 8);
246 }
247
248 #[test]
249 fn test_indent_style_space() {
250 let mut properties = Properties::new();
251 properties.insert_raw_for_key("indent_style", "space");
252 let config = Config::from(&properties);
253 assert_eq!(config.indent_type, IndentType::Spaces);
254 }
255
256 #[test]
257 fn test_indent_style_tab() {
258 let mut properties = Properties::new();
259 properties.insert_raw_for_key("indent_style", "Tab");
260 let config = Config::from(&properties);
261 assert_eq!(config.indent_type, IndentType::Tabs);
262 }
263
264 #[test]
265 fn test_max_line_length() {
266 let mut properties = Properties::new();
267 properties.insert_raw_for_key("max_line_length", "80");
268 let config = Config::from(&properties);
269 assert_eq!(config.column_width, 80);
270 }
271
272 #[test]
273 fn test_max_line_length_off() {
274 let mut properties = Properties::new();
275 properties.insert_raw_for_key("max_line_length", "off");
276 let config = Config::from(&properties);
277 assert_eq!(config.column_width, usize::MAX);
278 }
279
280 #[test]
281 fn test_quote_type_double() {
282 let mut properties = Properties::new();
283 properties.insert_raw_for_key("quote_type", "double");
284 let config = Config::from(&properties);
285 assert_eq!(config.quote_style, QuoteStyle::AutoPreferDouble);
286 }
287
288 #[test]
289 fn test_quote_type_single() {
290 let mut properties = Properties::new();
291 properties.insert_raw_for_key("quote_type", "Single");
292 let config = Config::from(&properties);
293 assert_eq!(config.quote_style, QuoteStyle::AutoPreferSingle);
294 }
295
296 #[test]
297 fn test_quote_type_auto() {
298 let mut properties = Properties::new();
299 properties.insert_raw_for_key("quote_type", "auto");
300 let config = Config::from(&properties);
301 assert_eq!(config.quote_style, QuoteStyle::AutoPreferDouble);
302 }
303
304 #[test]
305 fn test_call_parentheses_always() {
306 let mut properties = Properties::new();
307 properties.insert_raw_for_key("call_parentheses", "always");
308 let config = Config::from(&properties);
309 assert_eq!(config.call_parentheses, CallParenType::Always);
310 }
311
312 #[test]
313 fn test_call_parentheses_no_single_string() {
314 let mut properties = Properties::new();
315 properties.insert_raw_for_key("call_parentheses", "NoSingleString");
316 let config = Config::from(&properties);
317 assert_eq!(config.call_parentheses, CallParenType::NoSingleString);
318 }
319
320 #[test]
321 fn test_call_parentheses_no_single_table() {
322 let mut properties = Properties::new();
323 properties.insert_raw_for_key("call_parentheses", "NoSingleTable");
324 let config = Config::from(&properties);
325 assert_eq!(config.call_parentheses, CallParenType::NoSingleTable);
326 }
327
328 #[test]
329 fn test_call_parentheses_none() {
330 let mut properties = Properties::new();
331 properties.insert_raw_for_key("call_parentheses", "None");
332 let config = Config::from(&properties);
333 assert_eq!(config.call_parentheses, CallParenType::None);
334 }
335
336 #[test]
337 fn test_space_after_function_names_always() {
338 let mut properties = Properties::new();
339 properties.insert_raw_for_key("space_after_function_names", "Always");
340 let config = Config::from(&properties);
341 assert_eq!(
342 config.space_after_function_names,
343 SpaceAfterFunctionNames::Always
344 );
345 }
346
347 #[test]
348 fn test_space_after_function_names_definitions() {
349 let mut properties = Properties::new();
350 properties.insert_raw_for_key("space_after_function_names", "Definitions");
351 let config = Config::from(&properties);
352 assert_eq!(
353 config.space_after_function_names,
354 SpaceAfterFunctionNames::Definitions
355 );
356 }
357
358 #[test]
359 fn test_space_after_function_names_calls() {
360 let mut properties = Properties::new();
361 properties.insert_raw_for_key("space_after_function_names", "Calls");
362 let config = Config::from(&properties);
363 assert_eq!(
364 config.space_after_function_names,
365 SpaceAfterFunctionNames::Calls
366 );
367 }
368
369 #[test]
370 fn test_space_after_function_names_never() {
371 let mut properties = Properties::new();
372 properties.insert_raw_for_key("space_after_function_names", "Never");
373 let config = Config::from(&properties);
374 assert_eq!(
375 config.space_after_function_names,
376 SpaceAfterFunctionNames::Never
377 );
378 }
379
380 #[test]
381 fn test_collapse_simple_statement_never() {
382 let mut properties = Properties::new();
383 properties.insert_raw_for_key("collapse_simple_statement", "Never");
384 let config = Config::from(&properties);
385 assert_eq!(
386 config.collapse_simple_statement,
387 CollapseSimpleStatement::Never
388 );
389 }
390
391 #[test]
392 fn test_collapse_simple_statement_function_only() {
393 let mut properties = Properties::new();
394 properties.insert_raw_for_key("collapse_simple_statement", "FunctionOnly");
395 let config = Config::from(&properties);
396 assert_eq!(
397 config.collapse_simple_statement,
398 CollapseSimpleStatement::FunctionOnly
399 );
400 }
401
402 #[test]
403 fn test_collapse_simple_statement_conditional_only() {
404 let mut properties = Properties::new();
405 properties.insert_raw_for_key("collapse_simple_statement", "ConditionalOnly");
406 let config = Config::from(&properties);
407 assert_eq!(
408 config.collapse_simple_statement,
409 CollapseSimpleStatement::ConditionalOnly
410 );
411 }
412
413 #[test]
414 fn test_collapse_simple_statement_always() {
415 let mut properties = Properties::new();
416 properties.insert_raw_for_key("collapse_simple_statement", "always");
417 let config = Config::from(&properties);
418 assert_eq!(
419 config.collapse_simple_statement,
420 CollapseSimpleStatement::Always
421 );
422 }
423
424 #[test]
425 fn test_sort_requires_enabled() {
426 let mut properties = Properties::new();
427 properties.insert_raw_for_key("sort_requires", "true");
428 let config = Config::from(&properties);
429 assert!(config.sort_requires.enabled);
430 }
431
432 #[test]
433 fn test_sort_requires_disabled() {
434 let mut properties = Properties::new();
435 properties.insert_raw_for_key("sort_requires", "false");
436 let config = Config::from(&properties);
437 assert!(!config.sort_requires.enabled);
438 }
439
440 #[test]
441 fn test_invalid_properties() {
442 let mut properties = Properties::new();
443 let default_config = Config::new();
444 let invalid_value = " ";
445 for key in [
446 "end_of_line",
447 "indent_size",
448 "indent_style",
449 "quote_style",
450 "call_parentheses",
451 "collapse_simple_statement",
452 "sort_requires",
453 ] {
454 properties.insert_raw_for_key(key, invalid_value);
455 }
456 let config = Config::from(&properties);
457 assert_eq!(config.line_endings, default_config.line_endings);
458 assert_eq!(config.indent_width, default_config.indent_width);
459 assert_eq!(config.indent_type, default_config.indent_type);
460 assert_eq!(config.column_width, default_config.column_width);
461 assert_eq!(config.quote_style, default_config.quote_style);
462 assert_eq!(config.call_parentheses, default_config.call_parentheses);
463 assert_eq!(
464 config.collapse_simple_statement,
465 default_config.collapse_simple_statement
466 );
467 assert_eq!(
468 config.sort_requires.enabled,
469 default_config.sort_requires.enabled
470 );
471 }
472}