@owenlamont/ryl 0.4.1 → 0.4.3

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 (217) hide show
  1. package/README.md +13 -0
  2. package/bin/ryl.js +96 -1
  3. package/npm-platforms.json +124 -0
  4. package/package.json +46 -12
  5. package/.github/CODEOWNERS +0 -1
  6. package/.github/dependabot.yml +0 -13
  7. package/.github/workflows/ci.yml +0 -107
  8. package/.github/workflows/release.yml +0 -613
  9. package/.github/workflows/update_dependencies.yml +0 -61
  10. package/.github/workflows/update_linters.yml +0 -56
  11. package/.pre-commit-config.yaml +0 -87
  12. package/.yamllint +0 -4
  13. package/AGENTS.md +0 -200
  14. package/Cargo.lock +0 -908
  15. package/Cargo.toml +0 -32
  16. package/clippy.toml +0 -1
  17. package/docs/config-presets.md +0 -100
  18. package/img/benchmark-5x5-5runs.svg +0 -2176
  19. package/pyproject.toml +0 -42
  20. package/ruff.toml +0 -107
  21. package/rumdl.toml +0 -20
  22. package/rust-toolchain.toml +0 -3
  23. package/rustfmt.toml +0 -3
  24. package/scripts/benchmark_perf_vs_yamllint.py +0 -400
  25. package/scripts/coverage-missing.ps1 +0 -80
  26. package/scripts/coverage-missing.sh +0 -60
  27. package/src/bin/discover_config_bin.rs +0 -24
  28. package/src/cli_support.rs +0 -33
  29. package/src/conf/mod.rs +0 -85
  30. package/src/config.rs +0 -2099
  31. package/src/decoder.rs +0 -326
  32. package/src/discover.rs +0 -31
  33. package/src/lib.rs +0 -19
  34. package/src/lint.rs +0 -558
  35. package/src/main.rs +0 -535
  36. package/src/migrate.rs +0 -233
  37. package/src/rules/anchors.rs +0 -517
  38. package/src/rules/braces.rs +0 -77
  39. package/src/rules/brackets.rs +0 -77
  40. package/src/rules/colons.rs +0 -475
  41. package/src/rules/commas.rs +0 -372
  42. package/src/rules/comments.rs +0 -299
  43. package/src/rules/comments_indentation.rs +0 -243
  44. package/src/rules/document_end.rs +0 -175
  45. package/src/rules/document_start.rs +0 -84
  46. package/src/rules/empty_lines.rs +0 -152
  47. package/src/rules/empty_values.rs +0 -255
  48. package/src/rules/float_values.rs +0 -259
  49. package/src/rules/flow_collection.rs +0 -562
  50. package/src/rules/hyphens.rs +0 -104
  51. package/src/rules/indentation.rs +0 -803
  52. package/src/rules/key_duplicates.rs +0 -218
  53. package/src/rules/key_ordering.rs +0 -303
  54. package/src/rules/line_length.rs +0 -326
  55. package/src/rules/mod.rs +0 -25
  56. package/src/rules/new_line_at_end_of_file.rs +0 -23
  57. package/src/rules/new_lines.rs +0 -95
  58. package/src/rules/octal_values.rs +0 -121
  59. package/src/rules/quoted_strings.rs +0 -577
  60. package/src/rules/span_utils.rs +0 -37
  61. package/src/rules/trailing_spaces.rs +0 -65
  62. package/src/rules/truthy.rs +0 -420
  63. package/tests/brackets_carriage_return.rs +0 -114
  64. package/tests/build_global_cfg_error.rs +0 -23
  65. package/tests/cli_anchors_rule.rs +0 -143
  66. package/tests/cli_braces_rule.rs +0 -104
  67. package/tests/cli_brackets_rule.rs +0 -104
  68. package/tests/cli_colons_rule.rs +0 -65
  69. package/tests/cli_commas_rule.rs +0 -104
  70. package/tests/cli_comments_indentation_rule.rs +0 -61
  71. package/tests/cli_comments_rule.rs +0 -67
  72. package/tests/cli_config_data_error.rs +0 -30
  73. package/tests/cli_config_flags.rs +0 -66
  74. package/tests/cli_config_migrate.rs +0 -229
  75. package/tests/cli_document_end_rule.rs +0 -92
  76. package/tests/cli_document_start_rule.rs +0 -92
  77. package/tests/cli_empty_lines_rule.rs +0 -87
  78. package/tests/cli_empty_values_rule.rs +0 -68
  79. package/tests/cli_env_config.rs +0 -34
  80. package/tests/cli_exit_and_errors.rs +0 -41
  81. package/tests/cli_file_encoding.rs +0 -203
  82. package/tests/cli_float_values_rule.rs +0 -64
  83. package/tests/cli_format_options.rs +0 -316
  84. package/tests/cli_global_cfg_relaxed.rs +0 -20
  85. package/tests/cli_hyphens_rule.rs +0 -104
  86. package/tests/cli_indentation_rule.rs +0 -65
  87. package/tests/cli_invalid_project_config.rs +0 -39
  88. package/tests/cli_key_duplicates_rule.rs +0 -104
  89. package/tests/cli_key_ordering_rule.rs +0 -59
  90. package/tests/cli_line_length_rule.rs +0 -85
  91. package/tests/cli_list_files.rs +0 -29
  92. package/tests/cli_new_line_rule.rs +0 -141
  93. package/tests/cli_new_lines_rule.rs +0 -119
  94. package/tests/cli_octal_values_rule.rs +0 -60
  95. package/tests/cli_quoted_strings_rule.rs +0 -47
  96. package/tests/cli_toml_config.rs +0 -119
  97. package/tests/cli_trailing_spaces_rule.rs +0 -77
  98. package/tests/cli_truthy_rule.rs +0 -83
  99. package/tests/cli_yaml_files_negation.rs +0 -45
  100. package/tests/colons_rule.rs +0 -303
  101. package/tests/common/compat.rs +0 -114
  102. package/tests/common/fake_env.rs +0 -93
  103. package/tests/common/mod.rs +0 -1
  104. package/tests/conf_builtin.rs +0 -9
  105. package/tests/config_anchors.rs +0 -84
  106. package/tests/config_braces.rs +0 -121
  107. package/tests/config_brackets.rs +0 -127
  108. package/tests/config_commas.rs +0 -79
  109. package/tests/config_comments.rs +0 -65
  110. package/tests/config_comments_indentation.rs +0 -20
  111. package/tests/config_deep_merge_nonstring_key.rs +0 -24
  112. package/tests/config_document_end.rs +0 -54
  113. package/tests/config_document_start.rs +0 -55
  114. package/tests/config_empty_lines.rs +0 -48
  115. package/tests/config_empty_values.rs +0 -35
  116. package/tests/config_env_errors.rs +0 -23
  117. package/tests/config_env_invalid_inline.rs +0 -15
  118. package/tests/config_env_missing.rs +0 -63
  119. package/tests/config_env_shim.rs +0 -301
  120. package/tests/config_explicit_file_parse_error.rs +0 -55
  121. package/tests/config_extended_features.rs +0 -225
  122. package/tests/config_extends_inline.rs +0 -185
  123. package/tests/config_extends_sequence.rs +0 -18
  124. package/tests/config_find_project_home_boundary.rs +0 -54
  125. package/tests/config_find_project_two_files_in_cwd.rs +0 -47
  126. package/tests/config_float_values.rs +0 -34
  127. package/tests/config_from_yaml_paths.rs +0 -32
  128. package/tests/config_hyphens.rs +0 -51
  129. package/tests/config_ignore_errors.rs +0 -243
  130. package/tests/config_ignore_overrides.rs +0 -83
  131. package/tests/config_indentation.rs +0 -65
  132. package/tests/config_invalid_globs.rs +0 -16
  133. package/tests/config_invalid_types.rs +0 -19
  134. package/tests/config_key_duplicates.rs +0 -34
  135. package/tests/config_key_ordering.rs +0 -70
  136. package/tests/config_line_length.rs +0 -65
  137. package/tests/config_locale.rs +0 -111
  138. package/tests/config_merge.rs +0 -26
  139. package/tests/config_new_lines.rs +0 -89
  140. package/tests/config_octal_values.rs +0 -33
  141. package/tests/config_quoted_strings.rs +0 -195
  142. package/tests/config_rule_level.rs +0 -147
  143. package/tests/config_rules_non_string_keys.rs +0 -23
  144. package/tests/config_scalar_overrides.rs +0 -27
  145. package/tests/config_to_toml.rs +0 -110
  146. package/tests/config_toml_coverage.rs +0 -80
  147. package/tests/config_toml_discovery.rs +0 -304
  148. package/tests/config_trailing_spaces.rs +0 -152
  149. package/tests/config_truthy.rs +0 -77
  150. package/tests/config_yaml_files.rs +0 -62
  151. package/tests/config_yaml_files_all_non_string.rs +0 -15
  152. package/tests/config_yaml_files_empty.rs +0 -30
  153. package/tests/coverage_commas.rs +0 -46
  154. package/tests/decoder_decode.rs +0 -338
  155. package/tests/discover_config_bin_all.rs +0 -66
  156. package/tests/discover_config_bin_env_invalid_yaml.rs +0 -26
  157. package/tests/discover_config_bin_project_config_parse_error.rs +0 -24
  158. package/tests/discover_config_bin_user_global_error.rs +0 -26
  159. package/tests/discover_module.rs +0 -30
  160. package/tests/discover_per_file_dir.rs +0 -10
  161. package/tests/discover_per_file_project_config_error.rs +0 -21
  162. package/tests/float_values.rs +0 -43
  163. package/tests/lint_multi_errors.rs +0 -32
  164. package/tests/main_yaml_ok_filtering.rs +0 -30
  165. package/tests/migrate_module.rs +0 -259
  166. package/tests/resolve_ctx_empty_parent.rs +0 -16
  167. package/tests/rule_anchors.rs +0 -442
  168. package/tests/rule_braces.rs +0 -258
  169. package/tests/rule_brackets.rs +0 -217
  170. package/tests/rule_commas.rs +0 -205
  171. package/tests/rule_comments.rs +0 -197
  172. package/tests/rule_comments_indentation.rs +0 -127
  173. package/tests/rule_document_end.rs +0 -118
  174. package/tests/rule_document_start.rs +0 -60
  175. package/tests/rule_empty_lines.rs +0 -96
  176. package/tests/rule_empty_values.rs +0 -102
  177. package/tests/rule_float_values.rs +0 -109
  178. package/tests/rule_hyphens.rs +0 -65
  179. package/tests/rule_indentation.rs +0 -455
  180. package/tests/rule_key_duplicates.rs +0 -76
  181. package/tests/rule_key_ordering.rs +0 -207
  182. package/tests/rule_line_length.rs +0 -200
  183. package/tests/rule_new_lines.rs +0 -51
  184. package/tests/rule_octal_values.rs +0 -53
  185. package/tests/rule_quoted_strings.rs +0 -290
  186. package/tests/rule_trailing_spaces.rs +0 -41
  187. package/tests/rule_truthy.rs +0 -236
  188. package/tests/user_global_invalid_yaml.rs +0 -32
  189. package/tests/yamllint_compat_anchors.rs +0 -280
  190. package/tests/yamllint_compat_braces.rs +0 -411
  191. package/tests/yamllint_compat_brackets.rs +0 -364
  192. package/tests/yamllint_compat_colons.rs +0 -298
  193. package/tests/yamllint_compat_colors.rs +0 -80
  194. package/tests/yamllint_compat_commas.rs +0 -375
  195. package/tests/yamllint_compat_comments.rs +0 -167
  196. package/tests/yamllint_compat_comments_indentation.rs +0 -281
  197. package/tests/yamllint_compat_config.rs +0 -170
  198. package/tests/yamllint_compat_document_end.rs +0 -243
  199. package/tests/yamllint_compat_document_start.rs +0 -136
  200. package/tests/yamllint_compat_empty_lines.rs +0 -117
  201. package/tests/yamllint_compat_empty_values.rs +0 -179
  202. package/tests/yamllint_compat_float_values.rs +0 -216
  203. package/tests/yamllint_compat_hyphens.rs +0 -223
  204. package/tests/yamllint_compat_indentation.rs +0 -398
  205. package/tests/yamllint_compat_key_duplicates.rs +0 -139
  206. package/tests/yamllint_compat_key_ordering.rs +0 -170
  207. package/tests/yamllint_compat_line_length.rs +0 -375
  208. package/tests/yamllint_compat_list.rs +0 -127
  209. package/tests/yamllint_compat_new_line.rs +0 -133
  210. package/tests/yamllint_compat_newline_types.rs +0 -185
  211. package/tests/yamllint_compat_octal_values.rs +0 -172
  212. package/tests/yamllint_compat_quoted_strings.rs +0 -154
  213. package/tests/yamllint_compat_syntax.rs +0 -200
  214. package/tests/yamllint_compat_trailing_spaces.rs +0 -162
  215. package/tests/yamllint_compat_truthy.rs +0 -130
  216. package/tests/yamllint_compat_yaml_files.rs +0 -81
  217. package/typos.toml +0 -2
package/src/main.rs DELETED
@@ -1,535 +0,0 @@
1
- #![forbid(unsafe_code)]
2
- #![deny(
3
- clippy::all,
4
- clippy::pedantic,
5
- clippy::cargo,
6
- clippy::cognitive_complexity
7
- )]
8
-
9
- use std::collections::HashMap;
10
- use std::collections::HashSet;
11
- use std::io::IsTerminal;
12
- use std::path::{Path, PathBuf};
13
- use std::process::ExitCode;
14
-
15
- use clap::{Parser, ValueEnum};
16
- use ignore::WalkBuilder;
17
- use rayon::prelude::*;
18
- use ryl::cli_support::resolve_ctx;
19
- use ryl::config::{ConfigContext, Overrides, YamlLintConfig, discover_config};
20
- use ryl::migrate::{
21
- MigrateOptions, OutputMode as MigrateOutputMode, SourceCleanup, WriteMode,
22
- migrate_configs,
23
- };
24
- use ryl::{LintProblem, Severity, lint_file};
25
-
26
- fn gather_inputs(inputs: &[PathBuf]) -> (Vec<PathBuf>, Vec<PathBuf>) {
27
- let mut explicit_files = Vec::new();
28
- let mut candidates = Vec::new();
29
- for p in inputs {
30
- if p.is_dir() {
31
- let walker = WalkBuilder::new(p)
32
- .hidden(false)
33
- .ignore(true)
34
- .git_ignore(true)
35
- .git_global(true)
36
- .git_exclude(true)
37
- .follow_links(false)
38
- .build();
39
- for e in walker.flatten() {
40
- let fp = e.path().to_path_buf();
41
- if fp.is_file() {
42
- candidates.push(fp);
43
- }
44
- }
45
- } else {
46
- explicit_files.push(p.clone());
47
- }
48
- }
49
- (candidates, explicit_files)
50
- }
51
-
52
- fn build_global_cfg(
53
- inputs: &[PathBuf],
54
- cli: &Cli,
55
- ) -> Result<Option<ConfigContext>, String> {
56
- if cli.config_data.is_some()
57
- || cli.config_file.is_some()
58
- || std::env::var("YAMLLINT_CONFIG_FILE").is_ok()
59
- {
60
- let config_data = cli.config_data.as_ref().map(|raw| {
61
- if !raw.is_empty() && !raw.contains(':') {
62
- format!("extends: {raw}")
63
- } else {
64
- raw.clone()
65
- }
66
- });
67
- discover_config(
68
- inputs,
69
- &Overrides {
70
- config_file: cli.config_file.clone(),
71
- config_data,
72
- },
73
- )
74
- .map(Some)
75
- } else {
76
- Ok(None)
77
- }
78
- }
79
-
80
- fn run_migration(cli: &Cli) -> Result<ExitCode, String> {
81
- let cleanup = if let Some(suffix) = &cli.migrate.rename_old {
82
- SourceCleanup::RenameSuffix(suffix.clone())
83
- } else if cli.migrate.delete_old {
84
- SourceCleanup::Delete
85
- } else {
86
- SourceCleanup::Keep
87
- };
88
- let options = MigrateOptions {
89
- root: cli
90
- .migrate
91
- .root
92
- .clone()
93
- .unwrap_or_else(|| PathBuf::from(".")),
94
- write_mode: if cli.migrate.write {
95
- WriteMode::Write
96
- } else {
97
- WriteMode::Preview
98
- },
99
- output_mode: if cli.migrate.stdout {
100
- MigrateOutputMode::IncludeToml
101
- } else {
102
- MigrateOutputMode::SummaryOnly
103
- },
104
- cleanup,
105
- };
106
- let result = migrate_configs(&options)?;
107
- for warning in result.warnings {
108
- eprintln!("{warning}");
109
- }
110
- if result.entries.is_empty() {
111
- println!(
112
- "No legacy YAML config files found under {}",
113
- options.root.display()
114
- );
115
- return Ok(ExitCode::SUCCESS);
116
- }
117
-
118
- for entry in &result.entries {
119
- println!("{} -> {}", entry.source.display(), entry.target.display());
120
- }
121
- if options.output_mode == MigrateOutputMode::IncludeToml {
122
- for entry in &result.entries {
123
- println!("# {}", entry.target.display());
124
- println!("{}", entry.toml);
125
- }
126
- }
127
- Ok(ExitCode::SUCCESS)
128
- }
129
-
130
- #[derive(Clone, Copy, Debug, PartialEq, Eq, ValueEnum)]
131
- enum CliFormat {
132
- Auto,
133
- Standard,
134
- Colored,
135
- Github,
136
- Parsable,
137
- }
138
-
139
- #[derive(Parser, Debug)]
140
- #[command(name = "ryl", version, about = "Fast YAML linter written in Rust")]
141
- struct Cli {
142
- /// One or more paths: files and/or directories
143
- #[arg(value_name = "PATH_OR_FILE")]
144
- inputs: Vec<PathBuf>,
145
-
146
- /// Path to configuration file (YAML or TOML)
147
- #[arg(short = 'c', long = "config-file", value_name = "FILE")]
148
- config_file: Option<PathBuf>,
149
-
150
- /// Inline configuration data (yaml)
151
- #[arg(short = 'd', long = "config-data", value_name = "YAML")]
152
- config_data: Option<String>,
153
-
154
- /// Output format (auto, standard, colored, github, parsable)
155
- #[arg(short = 'f', long = "format", default_value_t = CliFormat::Auto, value_enum)]
156
- format: CliFormat,
157
-
158
- /// Convert discovered legacy YAML config files into .ryl.toml files
159
- #[arg(long = "migrate-configs", default_value_t = false)]
160
- migrate_configs: bool,
161
-
162
- #[command(flatten)]
163
- lint: LintFlags,
164
-
165
- #[command(flatten)]
166
- migrate: MigrateFlags,
167
- }
168
-
169
- #[derive(clap::Args, Debug, Default)]
170
- struct LintFlags {
171
- /// List files that would be linted (reserved)
172
- #[arg(long = "list-files", default_value_t = false)]
173
- list_files: bool,
174
-
175
- /// Strict mode (reserved)
176
- #[arg(short = 's', long = "strict", default_value_t = false)]
177
- strict: bool,
178
-
179
- /// Suppress warnings (reserved)
180
- #[arg(long = "no-warnings", default_value_t = false)]
181
- no_warnings: bool,
182
- }
183
-
184
- #[derive(clap::Args, Debug, Default)]
185
- struct MigrateFlags {
186
- /// Root path to search for legacy YAML config files (default: .)
187
- #[arg(
188
- long = "migrate-root",
189
- value_name = "DIR",
190
- requires = "migrate_configs"
191
- )]
192
- root: Option<PathBuf>,
193
-
194
- /// Write migrated .ryl.toml files (otherwise preview only)
195
- #[arg(
196
- long = "migrate-write",
197
- default_value_t = false,
198
- requires = "migrate_configs"
199
- )]
200
- write: bool,
201
-
202
- /// Print generated TOML to stdout during migration
203
- #[arg(
204
- long = "migrate-stdout",
205
- default_value_t = false,
206
- requires = "migrate_configs"
207
- )]
208
- stdout: bool,
209
-
210
- /// Rename source YAML configs by appending this suffix after migration
211
- #[arg(
212
- long = "migrate-rename-old",
213
- value_name = "SUFFIX",
214
- conflicts_with = "delete_old",
215
- requires_all = ["write", "migrate_configs"]
216
- )]
217
- rename_old: Option<String>,
218
-
219
- /// Delete source YAML configs after migration
220
- #[arg(
221
- long = "migrate-delete-old",
222
- default_value_t = false,
223
- conflicts_with = "rename_old",
224
- requires_all = ["write", "migrate_configs"]
225
- )]
226
- delete_old: bool,
227
- }
228
-
229
- #[derive(Clone, Copy, Debug, PartialEq, Eq)]
230
- enum OutputFormat {
231
- Standard,
232
- Colored,
233
- Github,
234
- Parsable,
235
- }
236
-
237
- fn detect_output_format(choice: CliFormat) -> OutputFormat {
238
- match choice {
239
- CliFormat::Standard => OutputFormat::Standard,
240
- CliFormat::Colored => OutputFormat::Colored,
241
- CliFormat::Github => OutputFormat::Github,
242
- CliFormat::Parsable => OutputFormat::Parsable,
243
- CliFormat::Auto => {
244
- if github_env_active() {
245
- OutputFormat::Github
246
- } else if supports_color() {
247
- OutputFormat::Colored
248
- } else {
249
- OutputFormat::Standard
250
- }
251
- }
252
- }
253
- }
254
-
255
- fn github_env_active() -> bool {
256
- std::env::var_os("GITHUB_ACTIONS").is_some()
257
- && std::env::var_os("GITHUB_WORKFLOW").is_some()
258
- }
259
-
260
- fn supports_color() -> bool {
261
- if std::env::var_os("NO_COLOR").is_some() {
262
- return false;
263
- }
264
- if std::env::var_os("FORCE_COLOR").is_some() {
265
- return true;
266
- }
267
- std::io::stderr().is_terminal()
268
- }
269
-
270
- fn main() -> ExitCode {
271
- let cli = Cli::parse();
272
-
273
- if cli.migrate_configs {
274
- return match run_migration(&cli) {
275
- Ok(code) => code,
276
- Err(err) => {
277
- eprintln!("{err}");
278
- ExitCode::from(2)
279
- }
280
- };
281
- }
282
-
283
- if cli.inputs.is_empty() {
284
- eprintln!("error: expected one or more paths (files and/or directories)");
285
- return ExitCode::from(2);
286
- }
287
-
288
- // Build a global config if -d/-c provided or env var set; else None for per-file discovery.
289
- let global_cfg = match build_global_cfg(&cli.inputs, &cli) {
290
- Ok(cfg) => cfg,
291
- Err(e) => {
292
- eprintln!("{e}");
293
- return ExitCode::from(2);
294
- }
295
- };
296
- if let Some(cfg) = &global_cfg {
297
- for notice in &cfg.notices {
298
- eprintln!("{notice}");
299
- }
300
- }
301
- let inputs = cli.inputs;
302
-
303
- // Determine files to parse from mixed inputs.
304
- // - Directories: recursively gather only .yml/.yaml
305
- // - Files: include as-is (even if extension isn't yaml)
306
- let (candidates, explicit_files) = gather_inputs(&inputs);
307
-
308
- // Filter directory candidates via ignores, respecting global vs per-file behavior.
309
- let mut cache: HashMap<PathBuf, (PathBuf, YamlLintConfig)> = HashMap::new();
310
- let mut emitted_notices: HashSet<String> = HashSet::new();
311
- let mut files: Vec<(PathBuf, PathBuf, YamlLintConfig)> = Vec::new();
312
- for f in candidates {
313
- let (base_dir, cfg, notices) =
314
- match resolve_ctx(&f, global_cfg.as_ref(), &mut cache) {
315
- Ok(pair) => pair,
316
- Err(e) => {
317
- eprintln!("{e}");
318
- return ExitCode::from(2);
319
- }
320
- };
321
- for notice in notices {
322
- if emitted_notices.insert(notice.clone()) {
323
- eprintln!("{notice}");
324
- }
325
- }
326
- let ignored = cfg.is_file_ignored(&f, &base_dir);
327
- let yaml_ok = cfg.is_yaml_candidate(&f, &base_dir);
328
- if !ignored && yaml_ok {
329
- files.push((f, base_dir, cfg));
330
- }
331
- }
332
-
333
- for ef in explicit_files {
334
- let (base_dir, cfg, notices) =
335
- match resolve_ctx(&ef, global_cfg.as_ref(), &mut cache) {
336
- Ok(pair) => pair,
337
- Err(e) => {
338
- eprintln!("{e}");
339
- return ExitCode::from(2);
340
- }
341
- };
342
- for notice in notices {
343
- if emitted_notices.insert(notice.clone()) {
344
- eprintln!("{notice}");
345
- }
346
- }
347
- let ignored = cfg.is_file_ignored(&ef, &base_dir);
348
- let yaml_ok = cfg.is_yaml_candidate(&ef, &base_dir);
349
- if !ignored && yaml_ok {
350
- files.push((ef, base_dir, cfg));
351
- }
352
- }
353
-
354
- if cli.lint.list_files {
355
- for (path, ..) in &files {
356
- println!("{}", path.display());
357
- }
358
- return ExitCode::SUCCESS;
359
- }
360
-
361
- if files.is_empty() {
362
- return ExitCode::SUCCESS;
363
- }
364
-
365
- let mut results: Vec<(usize, Result<Vec<LintProblem>, String>)> = files
366
- .par_iter()
367
- .enumerate()
368
- .map(|(idx, (path, base_dir, cfg))| (idx, lint_file(path, cfg, base_dir)))
369
- .collect();
370
-
371
- results.sort_by_key(|(idx, _)| *idx);
372
-
373
- let output_format = detect_output_format(cli.format);
374
- let (has_error, has_warning) =
375
- process_results(&files, results, output_format, cli.lint.no_warnings);
376
-
377
- if has_error {
378
- ExitCode::from(1)
379
- } else if has_warning && cli.lint.strict {
380
- ExitCode::from(2)
381
- } else {
382
- ExitCode::SUCCESS
383
- }
384
- }
385
-
386
- fn process_results(
387
- files: &[(PathBuf, PathBuf, YamlLintConfig)],
388
- results: Vec<(usize, Result<Vec<LintProblem>, String>)>,
389
- output_format: OutputFormat,
390
- no_warnings: bool,
391
- ) -> (bool, bool) {
392
- let mut has_error = false;
393
- let mut has_warning = false;
394
-
395
- for (idx, outcome) in results {
396
- let (path, ..) = &files[idx];
397
- match outcome {
398
- Err(message) => {
399
- eprintln!("{message}");
400
- has_error = true;
401
- }
402
- Ok(diagnostics) => {
403
- let mut problems = diagnostics
404
- .iter()
405
- .filter(|problem| {
406
- !(no_warnings && problem.level == Severity::Warning)
407
- })
408
- .peekable();
409
-
410
- if problems.peek().is_none() {
411
- continue;
412
- }
413
-
414
- match output_format {
415
- OutputFormat::Standard => {
416
- eprintln!("{}", path.display());
417
- for problem in problems {
418
- eprintln!("{}", format_standard(problem));
419
- match problem.level {
420
- Severity::Error => has_error = true,
421
- Severity::Warning => has_warning = true,
422
- }
423
- }
424
- eprintln!();
425
- }
426
- OutputFormat::Colored => {
427
- eprintln!("\u{001b}[4m{}\u{001b}[0m", path.display());
428
- for problem in problems {
429
- eprintln!("{}", format_colored(problem));
430
- match problem.level {
431
- Severity::Error => has_error = true,
432
- Severity::Warning => has_warning = true,
433
- }
434
- }
435
- eprintln!();
436
- }
437
- OutputFormat::Github => {
438
- eprintln!("::group::{}", path.display());
439
- for problem in problems {
440
- eprintln!("{}", format_github(problem, path));
441
- match problem.level {
442
- Severity::Error => has_error = true,
443
- Severity::Warning => has_warning = true,
444
- }
445
- }
446
- eprintln!("::endgroup::");
447
- eprintln!();
448
- }
449
- OutputFormat::Parsable => {
450
- for problem in problems {
451
- eprintln!("{}", format_parsable(problem, path));
452
- match problem.level {
453
- Severity::Error => has_error = true,
454
- Severity::Warning => has_warning = true,
455
- }
456
- }
457
- }
458
- }
459
- }
460
- }
461
- }
462
-
463
- (has_error, has_warning)
464
- }
465
-
466
- fn format_standard(problem: &LintProblem) -> String {
467
- let mut line = format!(" {}:{}", problem.line, problem.column);
468
- line.push_str(&" ".repeat(12usize.saturating_sub(line.len())));
469
- line.push_str(problem.level.as_str());
470
- line.push_str(&" ".repeat(21usize.saturating_sub(line.len())));
471
- line.push_str(&problem.message);
472
- if let Some(rule) = problem.rule {
473
- line.push_str(" (");
474
- line.push_str(rule);
475
- line.push(')');
476
- }
477
- line
478
- }
479
-
480
- fn format_colored(problem: &LintProblem) -> String {
481
- let mut line = format!(
482
- " \u{001b}[2m{}:{}\u{001b}[0m",
483
- problem.line, problem.column
484
- );
485
- line.push_str(&" ".repeat(20usize.saturating_sub(line.len())));
486
- let level_str = match problem.level {
487
- Severity::Warning => "\u{001b}[33mwarning\u{001b}[0m",
488
- Severity::Error => "\u{001b}[31merror\u{001b}[0m",
489
- };
490
- line.push_str(level_str);
491
- line.push_str(&" ".repeat(38usize.saturating_sub(line.len())));
492
- line.push_str(&problem.message);
493
- if let Some(rule) = problem.rule {
494
- line.push_str(" \u{001b}[2m(");
495
- line.push_str(rule);
496
- line.push_str(")\u{001b}[0m");
497
- }
498
- line
499
- }
500
-
501
- fn format_github(problem: &LintProblem, path: &Path) -> String {
502
- let mut line = format!(
503
- "::{} file={},line={},col={}::{}:{} ",
504
- problem.level.as_str(),
505
- path.display(),
506
- problem.line,
507
- problem.column,
508
- problem.line,
509
- problem.column
510
- );
511
- if let Some(rule) = problem.rule {
512
- line.push('[');
513
- line.push_str(rule);
514
- line.push_str("] ");
515
- }
516
- line.push_str(&problem.message);
517
- line
518
- }
519
-
520
- fn format_parsable(problem: &LintProblem, path: &Path) -> String {
521
- let mut line = format!(
522
- "{}:{}:{}: [{}] {}",
523
- path.display(),
524
- problem.line,
525
- problem.column,
526
- problem.level.as_str(),
527
- problem.message
528
- );
529
- if let Some(rule) = problem.rule {
530
- line.push_str(" (");
531
- line.push_str(rule);
532
- line.push(')');
533
- }
534
- line
535
- }