@frozenproductions/niteo 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/Cargo.lock +458 -0
  2. package/Cargo.toml +13 -0
  3. package/LICENSE +21 -0
  4. package/README.md +119 -0
  5. package/bin/niteo.js +51 -0
  6. package/package.json +27 -0
  7. package/scripts/build-binary.js +23 -0
  8. package/src/app.rs +132 -0
  9. package/src/cli.rs +43 -0
  10. package/src/config.rs +989 -0
  11. package/src/discovery.rs +42 -0
  12. package/src/git.rs +63 -0
  13. package/src/main.rs +13 -0
  14. package/src/report.rs +490 -0
  15. package/src/rules/max_directory_depth.rs +125 -0
  16. package/src/rules/max_file_exports.rs +478 -0
  17. package/src/rules/max_items_per_directory.rs +145 -0
  18. package/src/rules/min_items_per_directory.rs +146 -0
  19. package/src/rules/no_barrel_files.rs +284 -0
  20. package/src/rules/no_comments.rs +222 -0
  21. package/src/rules/no_console.rs +242 -0
  22. package/src/rules/no_debugger.rs +209 -0
  23. package/src/rules/no_default_export.rs +241 -0
  24. package/src/rules/no_duplicate_file_names.rs +196 -0
  25. package/src/rules/no_empty_directories.rs +467 -0
  26. package/src/rules/no_empty_interface.rs +280 -0
  27. package/src/rules/no_enums.rs +208 -0
  28. package/src/rules/no_eval.rs +268 -0
  29. package/src/rules/no_export_star.rs +252 -0
  30. package/src/rules/no_inline_types.rs +367 -0
  31. package/src/rules/no_interface.rs +570 -0
  32. package/src/rules/no_large_file.rs +98 -0
  33. package/src/rules/no_logic_in_barrel.rs +346 -0
  34. package/src/rules/no_logic_in_domain.rs +987 -0
  35. package/src/rules/no_mutable_exports.rs +253 -0
  36. package/src/rules/no_upward_import.rs +427 -0
  37. package/src/rules/prefer_satisfies.rs +319 -0
  38. package/src/rules.rs +247 -0
package/src/config.rs ADDED
@@ -0,0 +1,989 @@
1
+ use anyhow::{Context, Result, bail};
2
+ use serde::Deserialize;
3
+ use std::collections::HashMap;
4
+ use std::fs;
5
+ use std::path::{Path, PathBuf};
6
+
7
+ const CONFIG_FILE_NAME: &str = "niteo.toml";
8
+ const DEFAULT_CONFIG_SOURCE: &str = r#"[project]
9
+ root = "src"
10
+ respect-gitignore = true
11
+
12
+ [rules]
13
+ [rules.no-comments]
14
+ severity = "warn"
15
+ allow-doc-comments = true
16
+
17
+ [rules.no-logic-in-barrel]
18
+ severity = "warn"
19
+
20
+ [rules.no-default-export]
21
+ severity = "warn"
22
+
23
+ [rules.no-export-star]
24
+ severity = "warn"
25
+
26
+ [rules.no-inline-types]
27
+ severity = "warn"
28
+
29
+ [rules.max-file-exports]
30
+ severity = "warn"
31
+ max-exports = 10
32
+
33
+ [rules.no-upward-import]
34
+ severity = "warn"
35
+ max-depth = 0
36
+
37
+ [rules.no-large-file]
38
+ severity = "warn"
39
+ max-lines = 500
40
+
41
+ [rules.no-enums]
42
+ severity = "warn"
43
+
44
+ [rules.no-barrel-files]
45
+ severity = "warn"
46
+
47
+ [rules.no-console]
48
+ severity = "warn"
49
+ allow-patterns = []
50
+
51
+ [rules.no-debugger]
52
+ severity = "warn"
53
+
54
+ [rules.no-eval]
55
+ severity = "warn"
56
+
57
+ [rules.no-logic-in-domain]
58
+ severity = "warn"
59
+ extra-folders = []
60
+ extra-file-suffixes = []
61
+
62
+ [rules.no-empty-directories]
63
+ severity = "warn"
64
+ ignore-dirs = []
65
+
66
+ [rules.no-duplicate-file-names]
67
+ severity = "warn"
68
+ ignore-names = []
69
+
70
+ [rules.max-items-per-directory]
71
+ severity = "warn"
72
+ max-items = 20
73
+ ignore-dirs = []
74
+ count-folders = false
75
+
76
+ [rules.min-items-per-directory]
77
+ severity = "warn"
78
+ min-items = 3
79
+ ignore-dirs = []
80
+ count-folders = false
81
+
82
+ [rules.max-directory-depth]
83
+ severity = "warn"
84
+ max-depth = 5
85
+ ignore-dirs = []
86
+
87
+ [rules.no-empty-interface]
88
+ severity = "error"
89
+
90
+ [rules.no-interface]
91
+ severity = "warn"
92
+ allow-declaration-merging = true
93
+
94
+ [rules.no-mutable-exports]
95
+ severity = "warn"
96
+
97
+ [rules.prefer-satisfies]
98
+ severity = "info"
99
+ "#;
100
+
101
+ #[derive(Debug, Clone)]
102
+ pub struct ProjectConfig {
103
+ pub root: PathBuf,
104
+ pub no_comments: CommentsRuleConfig,
105
+ pub no_logic_in_barrel: RuleConfig,
106
+ pub no_default_export: RuleConfig,
107
+ pub no_export_star: RuleConfig,
108
+ pub no_inline_types: RuleConfig,
109
+ pub max_file_exports: FileExportsRuleConfig,
110
+ pub no_upward_import: UpwardImportRuleConfig,
111
+ pub no_large_file: FileLengthRuleConfig,
112
+ pub no_enums: RuleConfig,
113
+ pub no_barrel_files: RuleConfig,
114
+ pub no_console: NoConsoleRuleConfig,
115
+ pub no_debugger: RuleConfig,
116
+ pub no_eval: RuleConfig,
117
+ pub no_logic_in_domain: NoLogicInDomainRuleConfig,
118
+ pub no_empty_directories: NoEmptyDirectoriesRuleConfig,
119
+ pub no_duplicate_file_names: NoDuplicateFileNamesRuleConfig,
120
+ pub max_items_per_directory: MaxItemsPerDirectoryRuleConfig,
121
+ pub min_items_per_directory: MinItemsPerDirectoryRuleConfig,
122
+ pub max_directory_depth: MaxDirectoryDepthRuleConfig,
123
+ pub no_empty_interface: RuleConfig,
124
+ pub no_interface: NoInterfaceRuleConfig,
125
+ pub no_mutable_exports: RuleConfig,
126
+ pub prefer_satisfies: RuleConfig,
127
+ pub gitignore: GitignoreConfig,
128
+ }
129
+
130
+ impl Default for ProjectConfig {
131
+ fn default() -> Self {
132
+ Self {
133
+ root: PathBuf::from("."),
134
+ no_comments: CommentsRuleConfig::default(),
135
+ no_logic_in_barrel: RuleConfig::default(),
136
+ no_default_export: RuleConfig::default(),
137
+ no_export_star: RuleConfig::default(),
138
+ no_inline_types: RuleConfig::default(),
139
+ max_file_exports: FileExportsRuleConfig::default(),
140
+ no_upward_import: UpwardImportRuleConfig::default(),
141
+ no_large_file: FileLengthRuleConfig::default(),
142
+ no_enums: RuleConfig::default(),
143
+ no_barrel_files: RuleConfig::default(),
144
+ no_console: NoConsoleRuleConfig::default(),
145
+ no_debugger: RuleConfig::default(),
146
+ no_eval: RuleConfig::default(),
147
+ no_logic_in_domain: NoLogicInDomainRuleConfig::default(),
148
+ no_empty_directories: NoEmptyDirectoriesRuleConfig::default(),
149
+ no_duplicate_file_names: NoDuplicateFileNamesRuleConfig::default(),
150
+ max_items_per_directory: MaxItemsPerDirectoryRuleConfig::default(),
151
+ min_items_per_directory: MinItemsPerDirectoryRuleConfig::default(),
152
+ max_directory_depth: MaxDirectoryDepthRuleConfig::default(),
153
+ no_empty_interface: RuleConfig::default(),
154
+ no_interface: NoInterfaceRuleConfig::default(),
155
+ no_mutable_exports: RuleConfig::default(),
156
+ prefer_satisfies: RuleConfig::default(),
157
+ gitignore: GitignoreConfig::default(),
158
+ }
159
+ }
160
+ }
161
+
162
+ impl ProjectConfig {
163
+ pub fn resolve(workspace: &Path, root_override: Option<PathBuf>) -> Result<Self> {
164
+ let config = read_config_file(workspace)?;
165
+
166
+ if let Some(root) = root_override {
167
+ return Ok(Self {
168
+ root: absolutize(workspace, root),
169
+ no_comments: config.no_comments(),
170
+ no_logic_in_barrel: config.no_logic_in_barrel(),
171
+ no_default_export: config.no_default_export(),
172
+ no_export_star: config.no_export_star(),
173
+ no_inline_types: config.no_inline_types(),
174
+ max_file_exports: config.max_file_exports(),
175
+ no_upward_import: config.no_upward_import(),
176
+ no_large_file: config.no_large_file(),
177
+ no_enums: config.no_enums(),
178
+ no_barrel_files: config.no_barrel_files(),
179
+ no_console: config.no_console(),
180
+ no_debugger: config.no_debugger(),
181
+ no_eval: config.no_eval(),
182
+ no_logic_in_domain: config.no_logic_in_domain(),
183
+ no_empty_directories: config.no_empty_directories(),
184
+ no_duplicate_file_names: config.no_duplicate_file_names(),
185
+ max_items_per_directory: config.max_items_per_directory(),
186
+ min_items_per_directory: config.min_items_per_directory(),
187
+ max_directory_depth: config.max_directory_depth(),
188
+ no_empty_interface: config.no_empty_interface(),
189
+ no_interface: config.no_interface(),
190
+ no_mutable_exports: config.no_mutable_exports(),
191
+ prefer_satisfies: config.prefer_satisfies(),
192
+ gitignore: config.gitignore(),
193
+ });
194
+ }
195
+
196
+ if let Some(root) = config
197
+ .project
198
+ .as_ref()
199
+ .and_then(|project| project.root.as_ref())
200
+ {
201
+ return Ok(Self {
202
+ root: absolutize(workspace, root.to_path_buf()),
203
+ no_comments: config.no_comments(),
204
+ no_logic_in_barrel: config.no_logic_in_barrel(),
205
+ no_default_export: config.no_default_export(),
206
+ no_export_star: config.no_export_star(),
207
+ no_inline_types: config.no_inline_types(),
208
+ max_file_exports: config.max_file_exports(),
209
+ no_upward_import: config.no_upward_import(),
210
+ no_large_file: config.no_large_file(),
211
+ no_enums: config.no_enums(),
212
+ no_barrel_files: config.no_barrel_files(),
213
+ no_console: config.no_console(),
214
+ no_debugger: config.no_debugger(),
215
+ no_eval: config.no_eval(),
216
+ no_logic_in_domain: config.no_logic_in_domain(),
217
+ no_empty_directories: config.no_empty_directories(),
218
+ no_duplicate_file_names: config.no_duplicate_file_names(),
219
+ max_items_per_directory: config.max_items_per_directory(),
220
+ min_items_per_directory: config.min_items_per_directory(),
221
+ max_directory_depth: config.max_directory_depth(),
222
+ no_empty_interface: config.no_empty_interface(),
223
+ no_interface: config.no_interface(),
224
+ no_mutable_exports: config.no_mutable_exports(),
225
+ prefer_satisfies: config.prefer_satisfies(),
226
+ gitignore: config.gitignore(),
227
+ });
228
+ }
229
+
230
+ let source_root = workspace.join("src");
231
+ if source_root.is_dir() {
232
+ return Ok(Self {
233
+ root: source_root,
234
+ no_comments: config.no_comments(),
235
+ no_logic_in_barrel: config.no_logic_in_barrel(),
236
+ no_default_export: config.no_default_export(),
237
+ no_export_star: config.no_export_star(),
238
+ no_inline_types: config.no_inline_types(),
239
+ max_file_exports: config.max_file_exports(),
240
+ no_upward_import: config.no_upward_import(),
241
+ no_large_file: config.no_large_file(),
242
+ no_enums: config.no_enums(),
243
+ no_barrel_files: config.no_barrel_files(),
244
+ no_console: config.no_console(),
245
+ no_debugger: config.no_debugger(),
246
+ no_eval: config.no_eval(),
247
+ no_logic_in_domain: config.no_logic_in_domain(),
248
+ no_empty_directories: config.no_empty_directories(),
249
+ no_duplicate_file_names: config.no_duplicate_file_names(),
250
+ max_items_per_directory: config.max_items_per_directory(),
251
+ min_items_per_directory: config.min_items_per_directory(),
252
+ max_directory_depth: config.max_directory_depth(),
253
+ no_empty_interface: config.no_empty_interface(),
254
+ no_interface: config.no_interface(),
255
+ no_mutable_exports: config.no_mutable_exports(),
256
+ prefer_satisfies: config.prefer_satisfies(),
257
+ gitignore: config.gitignore(),
258
+ });
259
+ }
260
+
261
+ Ok(Self {
262
+ root: workspace.to_path_buf(),
263
+ no_comments: config.no_comments(),
264
+ no_logic_in_barrel: config.no_logic_in_barrel(),
265
+ no_default_export: config.no_default_export(),
266
+ no_export_star: config.no_export_star(),
267
+ no_inline_types: config.no_inline_types(),
268
+ max_file_exports: config.max_file_exports(),
269
+ no_upward_import: config.no_upward_import(),
270
+ no_large_file: config.no_large_file(),
271
+ no_enums: config.no_enums(),
272
+ no_barrel_files: config.no_barrel_files(),
273
+ no_console: config.no_console(),
274
+ no_debugger: config.no_debugger(),
275
+ no_eval: config.no_eval(),
276
+ no_logic_in_domain: config.no_logic_in_domain(),
277
+ no_empty_directories: config.no_empty_directories(),
278
+ no_duplicate_file_names: config.no_duplicate_file_names(),
279
+ max_items_per_directory: config.max_items_per_directory(),
280
+ min_items_per_directory: config.min_items_per_directory(),
281
+ max_directory_depth: config.max_directory_depth(),
282
+ no_empty_interface: config.no_empty_interface(),
283
+ no_interface: config.no_interface(),
284
+ no_mutable_exports: config.no_mutable_exports(),
285
+ prefer_satisfies: config.prefer_satisfies(),
286
+ gitignore: config.gitignore(),
287
+ })
288
+ }
289
+ }
290
+
291
+ #[derive(Debug, Clone, Copy, PartialEq, Eq)]
292
+ pub enum Severity {
293
+ Off,
294
+ Info,
295
+ Warn,
296
+ Error,
297
+ }
298
+
299
+ impl Severity {
300
+ pub fn from_str(value: &str) -> Self {
301
+ match value {
302
+ "off" => Self::Off,
303
+ "info" => Self::Info,
304
+ "error" => Self::Error,
305
+ _ => Self::Warn,
306
+ }
307
+ }
308
+
309
+ pub fn is_enabled(self) -> bool {
310
+ self != Self::Off
311
+ }
312
+ }
313
+
314
+ #[derive(Debug, Clone)]
315
+ pub struct CommentsRuleConfig {
316
+ pub severity: Severity,
317
+ pub allow_doc_comments: bool,
318
+ }
319
+
320
+ impl Default for CommentsRuleConfig {
321
+ fn default() -> Self {
322
+ Self {
323
+ severity: Severity::Warn,
324
+ allow_doc_comments: true,
325
+ }
326
+ }
327
+ }
328
+
329
+ #[derive(Debug, Clone)]
330
+ pub struct RuleConfig {
331
+ pub severity: Severity,
332
+ }
333
+
334
+ impl Default for RuleConfig {
335
+ fn default() -> Self {
336
+ Self {
337
+ severity: Severity::Warn,
338
+ }
339
+ }
340
+ }
341
+
342
+ #[derive(Debug, Clone)]
343
+ pub struct FileLengthRuleConfig {
344
+ pub severity: Severity,
345
+ pub max_lines: usize,
346
+ }
347
+
348
+ impl Default for FileLengthRuleConfig {
349
+ fn default() -> Self {
350
+ Self {
351
+ severity: Severity::Warn,
352
+ max_lines: 300,
353
+ }
354
+ }
355
+ }
356
+
357
+ #[derive(Debug, Clone)]
358
+ pub struct FileExportsRuleConfig {
359
+ pub severity: Severity,
360
+ pub max_exports: usize,
361
+ }
362
+
363
+ impl Default for FileExportsRuleConfig {
364
+ fn default() -> Self {
365
+ Self {
366
+ severity: Severity::Warn,
367
+ max_exports: 10,
368
+ }
369
+ }
370
+ }
371
+
372
+ #[derive(Debug, Clone)]
373
+ pub struct UpwardImportRuleConfig {
374
+ pub severity: Severity,
375
+ pub max_depth: usize,
376
+ }
377
+
378
+ impl Default for UpwardImportRuleConfig {
379
+ fn default() -> Self {
380
+ Self {
381
+ severity: Severity::Warn,
382
+ max_depth: 0,
383
+ }
384
+ }
385
+ }
386
+
387
+ #[derive(Debug, Clone)]
388
+ pub struct GitignoreConfig {
389
+ pub enabled: bool,
390
+ }
391
+
392
+ impl Default for GitignoreConfig {
393
+ fn default() -> Self {
394
+ Self { enabled: true }
395
+ }
396
+ }
397
+
398
+ #[derive(Debug, Clone)]
399
+ pub struct NoEmptyDirectoriesRuleConfig {
400
+ pub severity: Severity,
401
+ pub ignore_dirs: Vec<String>,
402
+ }
403
+
404
+ impl Default for NoEmptyDirectoriesRuleConfig {
405
+ fn default() -> Self {
406
+ Self {
407
+ severity: Severity::Warn,
408
+ ignore_dirs: vec![],
409
+ }
410
+ }
411
+ }
412
+
413
+ #[derive(Debug, Clone)]
414
+ pub struct NoDuplicateFileNamesRuleConfig {
415
+ pub severity: Severity,
416
+ pub ignore_names: Vec<String>,
417
+ }
418
+
419
+ impl Default for NoDuplicateFileNamesRuleConfig {
420
+ fn default() -> Self {
421
+ Self {
422
+ severity: Severity::Warn,
423
+ ignore_names: vec![],
424
+ }
425
+ }
426
+ }
427
+
428
+ #[derive(Debug, Clone)]
429
+ pub struct MaxItemsPerDirectoryRuleConfig {
430
+ pub severity: Severity,
431
+ pub max_items: usize,
432
+ pub ignore_dirs: Vec<String>,
433
+ pub count_folders: bool,
434
+ }
435
+
436
+ impl Default for MaxItemsPerDirectoryRuleConfig {
437
+ fn default() -> Self {
438
+ Self {
439
+ severity: Severity::Warn,
440
+ max_items: 20,
441
+ ignore_dirs: vec![],
442
+ count_folders: false,
443
+ }
444
+ }
445
+ }
446
+
447
+ #[derive(Debug, Clone)]
448
+ pub struct MinItemsPerDirectoryRuleConfig {
449
+ pub severity: Severity,
450
+ pub min_items: usize,
451
+ pub ignore_dirs: Vec<String>,
452
+ pub count_folders: bool,
453
+ }
454
+
455
+ impl Default for MinItemsPerDirectoryRuleConfig {
456
+ fn default() -> Self {
457
+ Self {
458
+ severity: Severity::Warn,
459
+ min_items: 3,
460
+ ignore_dirs: vec![],
461
+ count_folders: false,
462
+ }
463
+ }
464
+ }
465
+
466
+ #[derive(Debug, Clone)]
467
+ pub struct MaxDirectoryDepthRuleConfig {
468
+ pub severity: Severity,
469
+ pub max_depth: usize,
470
+ pub ignore_dirs: Vec<String>,
471
+ }
472
+
473
+ impl Default for MaxDirectoryDepthRuleConfig {
474
+ fn default() -> Self {
475
+ Self {
476
+ severity: Severity::Warn,
477
+ max_depth: 5,
478
+ ignore_dirs: vec![],
479
+ }
480
+ }
481
+ }
482
+
483
+ #[derive(Debug, Clone)]
484
+ pub struct NoLogicInDomainRuleConfig {
485
+ pub severity: Severity,
486
+ pub extra_folders: Vec<String>,
487
+ pub extra_file_suffixes: Vec<String>,
488
+ }
489
+
490
+ impl Default for NoLogicInDomainRuleConfig {
491
+ fn default() -> Self {
492
+ Self {
493
+ severity: Severity::Warn,
494
+ extra_folders: vec![],
495
+ extra_file_suffixes: vec![],
496
+ }
497
+ }
498
+ }
499
+
500
+ #[derive(Debug, Clone)]
501
+ pub struct NoConsoleRuleConfig {
502
+ pub severity: Severity,
503
+ pub allow_patterns: Vec<String>,
504
+ }
505
+
506
+ impl Default for NoConsoleRuleConfig {
507
+ fn default() -> Self {
508
+ Self {
509
+ severity: Severity::Warn,
510
+ allow_patterns: vec![],
511
+ }
512
+ }
513
+ }
514
+
515
+ #[derive(Debug, Clone)]
516
+ pub struct NoInterfaceRuleConfig {
517
+ pub severity: Severity,
518
+ pub allow_declaration_merging: bool,
519
+ }
520
+
521
+ impl Default for NoInterfaceRuleConfig {
522
+ fn default() -> Self {
523
+ Self {
524
+ severity: Severity::Warn,
525
+ allow_declaration_merging: true,
526
+ }
527
+ }
528
+ }
529
+
530
+ pub fn write_default_config(workspace: &Path) -> Result<PathBuf> {
531
+ let config_path = workspace.join(CONFIG_FILE_NAME);
532
+ if config_path.exists() {
533
+ bail!("{} already exists", config_path.display());
534
+ }
535
+
536
+ fs::write(&config_path, DEFAULT_CONFIG_SOURCE)
537
+ .with_context(|| format!("failed to write {}", config_path.display()))?;
538
+
539
+ Ok(config_path)
540
+ }
541
+
542
+ fn absolutize(workspace: &Path, path: PathBuf) -> PathBuf {
543
+ if path.is_absolute() {
544
+ return path;
545
+ }
546
+
547
+ workspace.join(path)
548
+ }
549
+
550
+ fn read_config_file(workspace: &Path) -> Result<RawConfig> {
551
+ let config_path = workspace.join(CONFIG_FILE_NAME);
552
+ if !config_path.exists() {
553
+ return Ok(RawConfig::default());
554
+ }
555
+
556
+ let source = fs::read_to_string(&config_path)
557
+ .with_context(|| format!("failed to read {}", config_path.display()))?;
558
+ let config = toml::from_str(&source)
559
+ .with_context(|| format!("failed to parse {}", config_path.display()))?;
560
+
561
+ Ok(config)
562
+ }
563
+
564
+ #[derive(Debug, Default, Deserialize)]
565
+ struct RawConfig {
566
+ project: Option<RawProjectConfig>,
567
+ rules: Option<HashMap<String, RawRuleConfig>>,
568
+ }
569
+
570
+ impl RawConfig {
571
+ fn no_comments(&self) -> CommentsRuleConfig {
572
+ self.rules
573
+ .as_ref()
574
+ .and_then(|rules| rules.get("no-comments"))
575
+ .map(RawRuleConfig::to_comments_config)
576
+ .unwrap_or_default()
577
+ }
578
+
579
+ fn no_logic_in_barrel(&self) -> RuleConfig {
580
+ self.rules
581
+ .as_ref()
582
+ .and_then(|rules| rules.get("no-logic-in-barrel"))
583
+ .map(RawRuleConfig::to_rule_config)
584
+ .unwrap_or_default()
585
+ }
586
+
587
+ fn no_default_export(&self) -> RuleConfig {
588
+ self.rules
589
+ .as_ref()
590
+ .and_then(|rules| rules.get("no-default-export"))
591
+ .map(RawRuleConfig::to_rule_config)
592
+ .unwrap_or_default()
593
+ }
594
+
595
+ fn no_export_star(&self) -> RuleConfig {
596
+ self.rules
597
+ .as_ref()
598
+ .and_then(|rules| rules.get("no-export-star"))
599
+ .map(RawRuleConfig::to_rule_config)
600
+ .unwrap_or_default()
601
+ }
602
+
603
+ fn no_inline_types(&self) -> RuleConfig {
604
+ self.rules
605
+ .as_ref()
606
+ .and_then(|rules| rules.get("no-inline-types"))
607
+ .map(RawRuleConfig::to_rule_config)
608
+ .unwrap_or_default()
609
+ }
610
+
611
+ fn max_file_exports(&self) -> FileExportsRuleConfig {
612
+ self.rules
613
+ .as_ref()
614
+ .and_then(|rules| rules.get("max-file-exports"))
615
+ .map(RawRuleConfig::to_file_exports_config)
616
+ .unwrap_or_default()
617
+ }
618
+
619
+ fn no_upward_import(&self) -> UpwardImportRuleConfig {
620
+ self.rules
621
+ .as_ref()
622
+ .and_then(|rules| rules.get("no-upward-import"))
623
+ .map(RawRuleConfig::to_upward_import_config)
624
+ .unwrap_or_default()
625
+ }
626
+
627
+ fn no_large_file(&self) -> FileLengthRuleConfig {
628
+ self.rules
629
+ .as_ref()
630
+ .and_then(|rules| rules.get("no-large-file"))
631
+ .map(RawRuleConfig::to_file_length_config)
632
+ .unwrap_or_default()
633
+ }
634
+
635
+ fn no_enums(&self) -> RuleConfig {
636
+ self.rules
637
+ .as_ref()
638
+ .and_then(|rules| rules.get("no-enums"))
639
+ .map(RawRuleConfig::to_rule_config)
640
+ .unwrap_or_default()
641
+ }
642
+
643
+ fn no_barrel_files(&self) -> RuleConfig {
644
+ self.rules
645
+ .as_ref()
646
+ .and_then(|rules| rules.get("no-barrel-files"))
647
+ .map(RawRuleConfig::to_rule_config)
648
+ .unwrap_or_default()
649
+ }
650
+
651
+ fn no_console(&self) -> NoConsoleRuleConfig {
652
+ self.rules
653
+ .as_ref()
654
+ .and_then(|rules| rules.get("no-console"))
655
+ .map(RawRuleConfig::to_no_console_config)
656
+ .unwrap_or_default()
657
+ }
658
+
659
+ fn no_debugger(&self) -> RuleConfig {
660
+ self.rules
661
+ .as_ref()
662
+ .and_then(|rules| rules.get("no-debugger"))
663
+ .map(RawRuleConfig::to_rule_config)
664
+ .unwrap_or_default()
665
+ }
666
+
667
+ fn no_eval(&self) -> RuleConfig {
668
+ self.rules
669
+ .as_ref()
670
+ .and_then(|rules| rules.get("no-eval"))
671
+ .map(RawRuleConfig::to_rule_config)
672
+ .unwrap_or_default()
673
+ }
674
+
675
+ fn no_logic_in_domain(&self) -> NoLogicInDomainRuleConfig {
676
+ self.rules
677
+ .as_ref()
678
+ .and_then(|rules| rules.get("no-logic-in-domain"))
679
+ .map(RawRuleConfig::to_no_logic_in_domain_config)
680
+ .unwrap_or_default()
681
+ }
682
+
683
+ fn no_empty_directories(&self) -> NoEmptyDirectoriesRuleConfig {
684
+ self.rules
685
+ .as_ref()
686
+ .and_then(|rules| rules.get("no-empty-directories"))
687
+ .map(RawRuleConfig::to_no_empty_directories_config)
688
+ .unwrap_or_default()
689
+ }
690
+
691
+ fn no_duplicate_file_names(&self) -> NoDuplicateFileNamesRuleConfig {
692
+ self.rules
693
+ .as_ref()
694
+ .and_then(|rules| rules.get("no-duplicate-file-names"))
695
+ .map(RawRuleConfig::to_no_duplicate_file_names_config)
696
+ .unwrap_or_default()
697
+ }
698
+
699
+ fn max_items_per_directory(&self) -> MaxItemsPerDirectoryRuleConfig {
700
+ self.rules
701
+ .as_ref()
702
+ .and_then(|rules| rules.get("max-items-per-directory"))
703
+ .map(RawRuleConfig::to_max_items_per_directory_config)
704
+ .unwrap_or_default()
705
+ }
706
+
707
+ fn min_items_per_directory(&self) -> MinItemsPerDirectoryRuleConfig {
708
+ self.rules
709
+ .as_ref()
710
+ .and_then(|rules| rules.get("min-items-per-directory"))
711
+ .map(RawRuleConfig::to_min_items_per_directory_config)
712
+ .unwrap_or_default()
713
+ }
714
+
715
+ fn max_directory_depth(&self) -> MaxDirectoryDepthRuleConfig {
716
+ self.rules
717
+ .as_ref()
718
+ .and_then(|rules| rules.get("max-directory-depth"))
719
+ .map(RawRuleConfig::to_max_directory_depth_config)
720
+ .unwrap_or_default()
721
+ }
722
+
723
+ fn no_empty_interface(&self) -> RuleConfig {
724
+ self.rules
725
+ .as_ref()
726
+ .and_then(|rules| rules.get("no-empty-interface"))
727
+ .map(RawRuleConfig::to_rule_config)
728
+ .unwrap_or_default()
729
+ }
730
+
731
+ fn no_interface(&self) -> NoInterfaceRuleConfig {
732
+ self.rules
733
+ .as_ref()
734
+ .and_then(|rules| rules.get("no-interface"))
735
+ .map(RawRuleConfig::to_no_interface_config)
736
+ .unwrap_or_default()
737
+ }
738
+
739
+ fn no_mutable_exports(&self) -> RuleConfig {
740
+ self.rules
741
+ .as_ref()
742
+ .and_then(|rules| rules.get("no-mutable-exports"))
743
+ .map(RawRuleConfig::to_rule_config)
744
+ .unwrap_or_default()
745
+ }
746
+
747
+ fn prefer_satisfies(&self) -> RuleConfig {
748
+ self.rules
749
+ .as_ref()
750
+ .and_then(|rules| rules.get("prefer-satisfies"))
751
+ .map(RawRuleConfig::to_rule_config)
752
+ .unwrap_or_default()
753
+ }
754
+
755
+ fn gitignore(&self) -> GitignoreConfig {
756
+ let project = self.project.as_ref();
757
+ GitignoreConfig {
758
+ enabled: project
759
+ .and_then(|p| p.respect_gitignore)
760
+ .unwrap_or_default(),
761
+ }
762
+ }
763
+ }
764
+
765
+ #[derive(Debug, Deserialize)]
766
+ struct RawProjectConfig {
767
+ root: Option<PathBuf>,
768
+ #[serde(rename = "respect-gitignore")]
769
+ respect_gitignore: Option<bool>,
770
+ }
771
+
772
+ #[derive(Debug, Deserialize)]
773
+ #[serde(untagged)]
774
+ enum RawRuleConfig {
775
+ Severity(String),
776
+ Options(RawRuleOptions),
777
+ }
778
+
779
+ impl RawRuleConfig {
780
+ fn to_comments_config(&self) -> CommentsRuleConfig {
781
+ match self {
782
+ Self::Severity(severity) => CommentsRuleConfig {
783
+ severity: Severity::from_str(severity),
784
+ allow_doc_comments: true,
785
+ },
786
+ Self::Options(options) => CommentsRuleConfig {
787
+ severity: Severity::from_str(options.severity.as_deref().unwrap_or("warn")),
788
+ allow_doc_comments: options.allow_doc_comments.unwrap_or(true),
789
+ },
790
+ }
791
+ }
792
+
793
+ fn to_rule_config(&self) -> RuleConfig {
794
+ match self {
795
+ Self::Severity(severity) => RuleConfig {
796
+ severity: Severity::from_str(severity),
797
+ },
798
+ Self::Options(options) => RuleConfig {
799
+ severity: Severity::from_str(options.severity.as_deref().unwrap_or("warn")),
800
+ },
801
+ }
802
+ }
803
+
804
+ fn to_file_length_config(&self) -> FileLengthRuleConfig {
805
+ match self {
806
+ Self::Severity(severity) => FileLengthRuleConfig {
807
+ severity: Severity::from_str(severity),
808
+ max_lines: 300,
809
+ },
810
+ Self::Options(options) => FileLengthRuleConfig {
811
+ severity: Severity::from_str(options.severity.as_deref().unwrap_or("warn")),
812
+ max_lines: options.max_lines.unwrap_or(300),
813
+ },
814
+ }
815
+ }
816
+
817
+ fn to_file_exports_config(&self) -> FileExportsRuleConfig {
818
+ match self {
819
+ Self::Severity(severity) => FileExportsRuleConfig {
820
+ severity: Severity::from_str(severity),
821
+ max_exports: 10,
822
+ },
823
+ Self::Options(options) => FileExportsRuleConfig {
824
+ severity: Severity::from_str(options.severity.as_deref().unwrap_or("warn")),
825
+ max_exports: options.max_exports.unwrap_or(10),
826
+ },
827
+ }
828
+ }
829
+
830
+ fn to_upward_import_config(&self) -> UpwardImportRuleConfig {
831
+ match self {
832
+ Self::Severity(severity) => UpwardImportRuleConfig {
833
+ severity: Severity::from_str(severity),
834
+ max_depth: 0,
835
+ },
836
+ Self::Options(options) => UpwardImportRuleConfig {
837
+ severity: Severity::from_str(options.severity.as_deref().unwrap_or("warn")),
838
+ max_depth: options.max_depth.unwrap_or(0),
839
+ },
840
+ }
841
+ }
842
+
843
+ fn to_no_empty_directories_config(&self) -> NoEmptyDirectoriesRuleConfig {
844
+ match self {
845
+ Self::Severity(severity) => NoEmptyDirectoriesRuleConfig {
846
+ severity: Severity::from_str(severity),
847
+ ignore_dirs: vec![],
848
+ },
849
+ Self::Options(options) => NoEmptyDirectoriesRuleConfig {
850
+ severity: Severity::from_str(options.severity.as_deref().unwrap_or("warn")),
851
+ ignore_dirs: options.ignore_dirs.clone().unwrap_or_default(),
852
+ },
853
+ }
854
+ }
855
+
856
+ fn to_no_logic_in_domain_config(&self) -> NoLogicInDomainRuleConfig {
857
+ match self {
858
+ Self::Severity(severity) => NoLogicInDomainRuleConfig {
859
+ severity: Severity::from_str(severity),
860
+ extra_folders: vec![],
861
+ extra_file_suffixes: vec![],
862
+ },
863
+ Self::Options(options) => NoLogicInDomainRuleConfig {
864
+ severity: Severity::from_str(options.severity.as_deref().unwrap_or("warn")),
865
+ extra_folders: options.extra_folders.clone().unwrap_or_default(),
866
+ extra_file_suffixes: options.extra_file_suffixes.clone().unwrap_or_default(),
867
+ },
868
+ }
869
+ }
870
+
871
+ fn to_no_console_config(&self) -> NoConsoleRuleConfig {
872
+ match self {
873
+ Self::Severity(severity) => NoConsoleRuleConfig {
874
+ severity: Severity::from_str(severity),
875
+ allow_patterns: vec![],
876
+ },
877
+ Self::Options(options) => NoConsoleRuleConfig {
878
+ severity: Severity::from_str(options.severity.as_deref().unwrap_or("warn")),
879
+ allow_patterns: options.allow_patterns.clone().unwrap_or_default(),
880
+ },
881
+ }
882
+ }
883
+
884
+ fn to_no_duplicate_file_names_config(&self) -> NoDuplicateFileNamesRuleConfig {
885
+ match self {
886
+ Self::Severity(severity) => NoDuplicateFileNamesRuleConfig {
887
+ severity: Severity::from_str(severity),
888
+ ignore_names: vec![],
889
+ },
890
+ Self::Options(options) => NoDuplicateFileNamesRuleConfig {
891
+ severity: Severity::from_str(options.severity.as_deref().unwrap_or("warn")),
892
+ ignore_names: options.ignore_names.clone().unwrap_or_default(),
893
+ },
894
+ }
895
+ }
896
+
897
+ fn to_max_items_per_directory_config(&self) -> MaxItemsPerDirectoryRuleConfig {
898
+ match self {
899
+ Self::Severity(severity) => MaxItemsPerDirectoryRuleConfig {
900
+ severity: Severity::from_str(severity),
901
+ max_items: 20,
902
+ ignore_dirs: vec![],
903
+ count_folders: false,
904
+ },
905
+ Self::Options(options) => MaxItemsPerDirectoryRuleConfig {
906
+ severity: Severity::from_str(options.severity.as_deref().unwrap_or("warn")),
907
+ max_items: options.max_items.unwrap_or(20),
908
+ ignore_dirs: options.ignore_dirs.clone().unwrap_or_default(),
909
+ count_folders: options.count_folders.unwrap_or(false),
910
+ },
911
+ }
912
+ }
913
+
914
+ fn to_min_items_per_directory_config(&self) -> MinItemsPerDirectoryRuleConfig {
915
+ match self {
916
+ Self::Severity(severity) => MinItemsPerDirectoryRuleConfig {
917
+ severity: Severity::from_str(severity),
918
+ min_items: 3,
919
+ ignore_dirs: vec![],
920
+ count_folders: false,
921
+ },
922
+ Self::Options(options) => MinItemsPerDirectoryRuleConfig {
923
+ severity: Severity::from_str(options.severity.as_deref().unwrap_or("warn")),
924
+ min_items: options.min_items.unwrap_or(3),
925
+ ignore_dirs: options.ignore_dirs.clone().unwrap_or_default(),
926
+ count_folders: options.count_folders.unwrap_or(false),
927
+ },
928
+ }
929
+ }
930
+
931
+ fn to_max_directory_depth_config(&self) -> MaxDirectoryDepthRuleConfig {
932
+ match self {
933
+ Self::Severity(severity) => MaxDirectoryDepthRuleConfig {
934
+ severity: Severity::from_str(severity),
935
+ max_depth: 5,
936
+ ignore_dirs: vec![],
937
+ },
938
+ Self::Options(options) => MaxDirectoryDepthRuleConfig {
939
+ severity: Severity::from_str(options.severity.as_deref().unwrap_or("warn")),
940
+ max_depth: options.max_depth.unwrap_or(5),
941
+ ignore_dirs: options.ignore_dirs.clone().unwrap_or_default(),
942
+ },
943
+ }
944
+ }
945
+
946
+ fn to_no_interface_config(&self) -> NoInterfaceRuleConfig {
947
+ match self {
948
+ Self::Severity(severity) => NoInterfaceRuleConfig {
949
+ severity: Severity::from_str(severity),
950
+ allow_declaration_merging: true,
951
+ },
952
+ Self::Options(options) => NoInterfaceRuleConfig {
953
+ severity: Severity::from_str(options.severity.as_deref().unwrap_or("warn")),
954
+ allow_declaration_merging: options.allow_declaration_merging.unwrap_or(true),
955
+ },
956
+ }
957
+ }
958
+ }
959
+
960
+ #[derive(Debug, Deserialize)]
961
+ struct RawRuleOptions {
962
+ severity: Option<String>,
963
+ #[serde(rename = "allow-doc-comments")]
964
+ allow_doc_comments: Option<bool>,
965
+ #[serde(rename = "max-lines")]
966
+ max_lines: Option<usize>,
967
+ #[serde(rename = "max-exports")]
968
+ max_exports: Option<usize>,
969
+ #[serde(rename = "max-depth")]
970
+ max_depth: Option<usize>,
971
+ #[serde(rename = "allow-patterns")]
972
+ allow_patterns: Option<Vec<String>>,
973
+ #[serde(rename = "extra-folders")]
974
+ extra_folders: Option<Vec<String>>,
975
+ #[serde(rename = "extra-file-suffixes")]
976
+ extra_file_suffixes: Option<Vec<String>>,
977
+ #[serde(rename = "ignore-dirs")]
978
+ ignore_dirs: Option<Vec<String>>,
979
+ #[serde(rename = "ignore-names")]
980
+ ignore_names: Option<Vec<String>>,
981
+ #[serde(rename = "max-items")]
982
+ max_items: Option<usize>,
983
+ #[serde(rename = "min-items")]
984
+ min_items: Option<usize>,
985
+ #[serde(rename = "count-folders")]
986
+ count_folders: Option<bool>,
987
+ #[serde(rename = "allow-declaration-merging")]
988
+ allow_declaration_merging: Option<bool>,
989
+ }