yerba 0.8.0-arm-linux-gnu → 0.9.0-arm-linux-gnu

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 (46) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +170 -19
  3. data/exe/arm-linux-gnu/yerba +0 -0
  4. data/ext/yerba/include/yerba.h +7 -0
  5. data/ext/yerba/yerba.c +54 -5
  6. data/lib/yerba/3.2/yerba.so +0 -0
  7. data/lib/yerba/3.3/yerba.so +0 -0
  8. data/lib/yerba/3.4/yerba.so +0 -0
  9. data/lib/yerba/4.0/yerba.so +0 -0
  10. data/lib/yerba/formatting.rb +9 -13
  11. data/lib/yerba/map.rb +11 -8
  12. data/lib/yerba/sequence.rb +2 -7
  13. data/lib/yerba/version.rb +1 -1
  14. data/rust/Cargo.lock +2 -342
  15. data/rust/Cargo.toml +15 -6
  16. data/rust/src/commands/blank_lines.rs +23 -3
  17. data/rust/src/commands/directives.rs +4 -4
  18. data/rust/src/commands/get.rs +7 -10
  19. data/rust/src/commands/init.rs +10 -4
  20. data/rust/src/commands/insert.rs +18 -12
  21. data/rust/src/commands/location.rs +2 -2
  22. data/rust/src/commands/mate.rs +7 -7
  23. data/rust/src/commands/mod.rs +118 -90
  24. data/rust/src/commands/move_key.rs +9 -6
  25. data/rust/src/commands/quote_style.rs +4 -5
  26. data/rust/src/commands/schema.rs +6 -6
  27. data/rust/src/commands/selectors.rs +2 -2
  28. data/rust/src/commands/set.rs +18 -7
  29. data/rust/src/commands/sort.rs +36 -42
  30. data/rust/src/commands/sort_keys.rs +2 -2
  31. data/rust/src/commands/ui.rs +156 -0
  32. data/rust/src/commands/unique.rs +7 -7
  33. data/rust/src/commands/version.rs +13 -3
  34. data/rust/src/document/insert.rs +25 -9
  35. data/rust/src/document/mod.rs +7 -3
  36. data/rust/src/document/set.rs +115 -33
  37. data/rust/src/document/style.rs +131 -11
  38. data/rust/src/error.rs +27 -0
  39. data/rust/src/ffi.rs +55 -12
  40. data/rust/src/lib.rs +12 -2
  41. data/rust/src/quote_style.rs +12 -11
  42. data/rust/src/syntax.rs +285 -11
  43. data/rust/src/validation.rs +30 -0
  44. data/rust/src/yaml_writer.rs +9 -21
  45. data/rust/src/yerbafile.rs +76 -4
  46. metadata +4 -2
@@ -1,3 +1,4 @@
1
+ use super::ui;
1
2
  use std::sync::LazyLock;
2
3
 
3
4
  use indoc::indoc;
@@ -7,13 +8,13 @@ use super::{output, parse_file, resolve_files, run_op};
7
8
 
8
9
  static EXAMPLES: LazyLock<String> = LazyLock::new(|| {
9
10
  colorize_examples(indoc! {r#"
10
- yerba insert config.yml "database.ssl" true
11
- yerba insert config.yml "database.ssl" true --after "host"
12
- yerba insert config.yml "database.ssl" true --before "port"
11
+ yerba insert config.yml "database.ssl" true --plain
12
+ yerba insert config.yml "database.ssl" true --plain --after "host"
13
+ yerba insert config.yml "database.ssl" true --plain --before "port"
13
14
  yerba insert config.yml "tags" "yaml"
14
15
  yerba insert config.yml "tags" "yaml" --at 0
15
16
  yerba insert config.yml "tags" "yaml" --after "ruby"
16
- yerba insert speakers.yml "" "name: Bob" --after ".name == Alice"
17
+ yerba insert speakers.yml "" "name: Bob" --plain --after ".name == Alice"
17
18
  yerba insert videos.yml "[0].speakers" "Diana" --before ".name == Charlie"
18
19
  yerba insert videos.yml "" --from "new_talk.yml" --after ".id == first-talk"
19
20
  "#})
@@ -28,6 +29,7 @@ static EXAMPLES: LazyLock<String> = LazyLock::new(|| {
28
29
  pub struct Args {
29
30
  file: String,
30
31
  selector: String,
32
+ #[arg(allow_hyphen_values = true)]
31
33
  value: Option<String>,
32
34
  #[arg(long, help = "Read value from a file (use - for stdin)")]
33
35
  from: Option<String>,
@@ -37,6 +39,8 @@ pub struct Args {
37
39
  after: Option<String>,
38
40
  #[arg(long)]
39
41
  at: Option<usize>,
42
+ #[arg(long, help = "Write the value as raw YAML, for literals like null, true or 42 and for map or sequence fragments")]
43
+ plain: bool,
40
44
  #[arg(long)]
41
45
  dry_run: bool,
42
46
  }
@@ -49,8 +53,8 @@ impl Args {
49
53
  let mut buffer = String::new();
50
54
 
51
55
  std::io::stdin().read_to_string(&mut buffer).unwrap_or_else(|error| {
52
- use super::color::*;
53
- eprintln!("{RED}Error:{RESET} reading stdin: {}", error);
56
+ use super::ui;
57
+ eprintln!("{} reading stdin: {}", ui::failure("Error:"), error);
54
58
 
55
59
  std::process::exit(1);
56
60
  });
@@ -59,18 +63,20 @@ impl Args {
59
63
  } else {
60
64
  std::fs::read_to_string(&from_path)
61
65
  .unwrap_or_else(|error| {
62
- use super::color::*;
63
- eprintln!("{RED}Error:{RESET} reading {}: {}", from_path, error);
66
+ eprintln!("{} reading {}: {}", ui::failure("Error:"), from_path, error);
64
67
  std::process::exit(1);
65
68
  })
66
69
  .trim()
67
70
  .to_string()
68
71
  }
69
- } else if let Some(val) = self.value {
70
- val
72
+ } else if let Some(value) = self.value {
73
+ if self.plain {
74
+ value
75
+ } else {
76
+ yerba::quote_scalar(&value)
77
+ }
71
78
  } else {
72
- use super::color::*;
73
- eprintln!("{RED}Error:{RESET} either a value argument or --from is required");
79
+ eprintln!("{} either a value argument or --from is required", ui::failure("Error:"));
74
80
 
75
81
  std::process::exit(1);
76
82
  };
@@ -28,13 +28,13 @@ pub struct Args {
28
28
 
29
29
  impl Args {
30
30
  pub fn run(self) {
31
- use super::color::*;
31
+ use super::ui;
32
32
 
33
33
  let document = parse_file(&self.file);
34
34
  let info = document.get_node_info(&self.selector);
35
35
 
36
36
  if info.location.start_line == 0 && info.location.end_line == 0 {
37
- eprintln!("{RED}Error:{RESET} selector \"{}\" not found in {}", self.selector, self.file);
37
+ eprintln!("{} selector \"{}\" not found in {}", ui::failure("Error:"), self.selector, self.file);
38
38
 
39
39
  super::show_similar_selectors(&self.file, &document, &self.selector);
40
40
  process::exit(1);
@@ -1,12 +1,12 @@
1
- use super::color::*;
1
+ use super::ui;
2
2
 
3
3
  pub fn run() {
4
- let g = GREEN;
5
- let b = BOLD;
6
- let d = DIM;
7
- let i = "\x1b[3m"; // italic
8
- let y = YELLOW;
9
- let r = RESET;
4
+ let g = ui::raw::green();
5
+ let b = ui::raw::bold();
6
+ let d = ui::raw::dim();
7
+ let i = ui::raw::italic();
8
+ let y = ui::raw::yellow();
9
+ let r = ui::raw::reset();
10
10
  let hr = format!(" {d}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}\u{2500}{r}");
11
11
 
12
12
  println!();
@@ -18,6 +18,7 @@ pub mod selectors;
18
18
  pub mod set;
19
19
  pub mod sort;
20
20
  pub mod sort_keys;
21
+ pub mod ui;
21
22
  pub mod unique;
22
23
  pub mod version;
23
24
 
@@ -30,56 +31,15 @@ pub(crate) fn is_github_actions() -> bool {
30
31
  std::env::var("GITHUB_ACTIONS").is_ok()
31
32
  }
32
33
 
33
- pub(crate) mod color {
34
- pub const GREEN: &str = "\x1b[32m";
35
- pub const RED: &str = "\x1b[31m";
36
- pub const YELLOW: &str = "\x1b[33m";
37
- pub const DIM: &str = "\x1b[2m";
38
- pub const BOLD: &str = "\x1b[1m";
39
- pub const RESET: &str = "\x1b[0m";
34
+ pub(crate) fn pluralize(count: usize, singular: &str, plural: &str) -> String {
35
+ format!("{} {}", count, if count == 1 { singular } else { plural })
40
36
  }
41
37
 
42
- // Compile-time ANSI macros for use in concat!() / clap attributes (used in main.rs)
43
- #[allow(unused_macros)]
44
- macro_rules! h {
45
- () => {
46
- "\x1b[1;32m"
47
- };
48
- } // header (bold green)
49
- #[allow(unused_macros)]
50
- macro_rules! b {
51
- () => {
52
- "\x1b[1m"
53
- };
54
- } // bold
55
- #[allow(unused_macros)]
56
- macro_rules! c {
57
- () => {
58
- "\x1b[36m"
59
- };
60
- } // cyan
61
- #[allow(unused_macros)]
62
- macro_rules! d {
63
- () => {
64
- "\x1b[2m"
65
- };
66
- } // dim
67
- #[allow(unused_macros)]
68
- macro_rules! r {
69
- () => {
70
- "\x1b[0m"
71
- };
72
- } // reset
73
- #[allow(unused_imports)]
74
- pub(crate) use {b, c, d, h, r};
75
-
76
38
  pub(crate) fn colorize_examples(input: &str) -> String {
77
39
  colorize_help(&format!("Examples:\n{}", input.trim()))
78
40
  }
79
41
 
80
42
  pub(crate) fn colorize_help(input: &str) -> String {
81
- use color::*;
82
-
83
43
  let mut output = String::new();
84
44
 
85
45
  for line in input.lines() {
@@ -91,7 +51,7 @@ pub(crate) fn colorize_help(input: &str) -> String {
91
51
  }
92
52
 
93
53
  if trimmed.ends_with(':') && !trimmed.contains(' ') {
94
- output.push_str(&format!("{GREEN}{BOLD}{trimmed}{RESET}\n"));
54
+ output.push_str(&format!("{}\n", ui::success(trimmed)));
95
55
  continue;
96
56
  }
97
57
 
@@ -100,11 +60,11 @@ pub(crate) fn colorize_help(input: &str) -> String {
100
60
 
101
61
  match (parts.next(), parts.next(), parts.next()) {
102
62
  (Some(cmd), Some(sub), Some(rest)) => {
103
- output.push_str(&format!(" {BOLD}{cmd}{RESET} \x1b[36m{sub}{RESET} {rest}\n"));
63
+ output.push_str(&format!(" {} {} {}\n", ui::strong(cmd), ui::muted(sub), rest));
104
64
  }
105
65
 
106
66
  (Some(cmd), Some(sub), None) => {
107
- output.push_str(&format!(" {BOLD}{cmd}{RESET} \x1b[36m{sub}{RESET}\n"));
67
+ output.push_str(&format!(" {} {}\n", ui::strong(cmd), ui::muted(sub)));
108
68
  }
109
69
 
110
70
  _ => {
@@ -134,9 +94,14 @@ pub(crate) fn colorize_help(input: &str) -> String {
134
94
  }
135
95
 
136
96
  if columns.len() == 3 {
137
- output.push_str(&format!(" \x1b[36m{:<20}{RESET} {:<21} {DIM}{}{RESET}\n", columns[0], columns[1], columns[2]));
97
+ output.push_str(&format!(
98
+ " {} {:<21} {}\n",
99
+ ui::muted(format!("{:<20}", columns[0])),
100
+ columns[1],
101
+ ui::subtle(columns[2])
102
+ ));
138
103
  } else if columns.len() == 2 {
139
- output.push_str(&format!(" \x1b[36m{:<20}{RESET} {}\n", columns[0], columns[1]));
104
+ output.push_str(&format!(" {} {}\n", ui::muted(format!("{:<20}", columns[0])), columns[1]));
140
105
  } else {
141
106
  output.push_str(&format!(" {trimmed}\n"));
142
107
  }
@@ -204,19 +169,34 @@ impl Command {
204
169
  }
205
170
 
206
171
  pub(crate) fn run_yerbafile(write: bool, files: Vec<String>) {
207
- use color::*;
208
-
209
172
  let yerbafile_path = yerba::Yerbafile::find().unwrap_or_else(|| {
210
- eprintln!("{RED}No Yerbafile found.{RESET} Run {BOLD}yerba init{RESET} to create one.");
173
+ eprintln!(
174
+ "{} No Yerbafile found. Run {} to create one.",
175
+ ui::failure(ui::glyph::FAIL),
176
+ ui::strong("yerba init")
177
+ );
211
178
  process::exit(1);
212
179
  });
213
180
 
214
181
  let yerbafile = yerba::Yerbafile::load(&yerbafile_path).unwrap_or_else(|error| {
215
- eprintln!("{RED}Error loading {}:{RESET} {}", yerbafile_path.display(), error);
182
+ eprintln!(
183
+ "{} {}",
184
+ ui::failure(ui::glyph::FAIL),
185
+ ui::strong(format!("Could not read {}", yerbafile_path.display()))
186
+ );
187
+ eprintln!(" {}", error);
216
188
  process::exit(1);
217
189
  });
218
190
 
219
- eprintln!("🧉 {BOLD}Using{RESET} {}", yerbafile_path.display());
191
+ eprintln!("\n{}\n", ui::banner());
192
+
193
+ eprintln!(
194
+ "{} {}\n",
195
+ ui::strong("Using Yerbafile rules"),
196
+ ui::subtle(format!("(from {})", yerbafile_path.display()))
197
+ );
198
+
199
+ let started = std::time::Instant::now();
220
200
 
221
201
  let results = if files.is_empty() {
222
202
  yerbafile.apply(write)
@@ -224,6 +204,8 @@ pub(crate) fn run_yerbafile(write: bool, files: Vec<String>) {
224
204
  files.iter().flat_map(|file| yerbafile.apply_file(file, write)).collect()
225
205
  };
226
206
 
207
+ let elapsed = started.elapsed();
208
+
227
209
  let mut has_changes = false;
228
210
  let mut has_errors = false;
229
211
 
@@ -231,7 +213,8 @@ pub(crate) fn run_yerbafile(write: bool, files: Vec<String>) {
231
213
 
232
214
  for result in &results {
233
215
  if let Some(error) = &result.error {
234
- eprintln!(" {RED}error:{RESET} {} {DIM}—{RESET} {}", result.file, error);
216
+ eprintln!(" {} {}", ui::failure(ui::glyph::FAIL), ui::strong(&result.file));
217
+ eprintln!(" {}", ui::muted(error));
235
218
 
236
219
  if github {
237
220
  use yerba::error::GitHubAnnotations;
@@ -244,12 +227,15 @@ pub(crate) fn run_yerbafile(write: bool, files: Vec<String>) {
244
227
  has_errors = true;
245
228
  } else if result.changed {
246
229
  if write {
247
- eprintln!(" {GREEN}updated:{RESET} {}", result.file);
230
+ eprintln!(" {} {}", ui::success(ui::glyph::OK), result.file);
248
231
  } else {
249
- eprintln!(" {YELLOW}would change:{RESET} {}", result.file);
232
+ eprintln!(" {} {}", ui::pending(ui::glyph::PENDING), result.file);
250
233
 
251
234
  if github {
252
- eprintln!("::error file={}::File does not match Yerbafile rules", result.file);
235
+ eprintln!(
236
+ "::error file={}::File does not match Yerbafile rules. Run `yerba apply` to update it.",
237
+ result.file
238
+ );
253
239
  }
254
240
  }
255
241
 
@@ -257,19 +243,70 @@ pub(crate) fn run_yerbafile(write: bool, files: Vec<String>) {
257
243
  }
258
244
  }
259
245
 
260
- if !has_changes && !has_errors {
261
- eprintln!("\n{BOLD}{GREEN}All files match the rules.{RESET}");
246
+ let changed = results.iter().filter(|result| result.error.is_none() && result.changed).count();
247
+ let seen: std::collections::HashSet<&String> = results.iter().map(|result| &result.file).collect();
248
+ let rules = yerbafile.rules.len() + usize::from(!yerbafile.pipeline.is_empty());
249
+ let steps = yerbafile.pipeline.len() + yerbafile.rules.iter().map(|rule| rule.pipeline.len()).sum::<usize>();
250
+
251
+ if seen.is_empty() {
252
+ eprintln!(
253
+ "{} {} {}",
254
+ ui::pending(ui::glyph::PENDING),
255
+ ui::pending("No files matched."),
256
+ ui::subtle("Check the `files:` patterns in your Yerbafile.")
257
+ );
258
+ } else if !has_changes && !has_errors {
259
+ eprintln!("{} {}", ui::success(ui::glyph::OK), ui::success("Everything's up to date."));
260
+ }
261
+
262
+ if write && has_changes {
263
+ eprintln!(
264
+ "\n{} {}",
265
+ ui::success(ui::glyph::OK),
266
+ ui::success(format!("Freshly brewed {}.", pluralize(changed, "file", "files")))
267
+ );
262
268
  }
263
269
 
264
270
  if !write && has_changes {
265
- process::exit(1);
271
+ let command = if files.is_empty() {
272
+ "yerba apply".to_string()
273
+ } else {
274
+ format!("yerba apply {}", files.join(" "))
275
+ };
276
+
277
+ eprintln!(
278
+ "\n{} {} {}",
279
+ ui::pending(ui::glyph::PENDING),
280
+ ui::pending(format!("{} out of date.", pluralize(changed, "file", "files"))),
281
+ ui::subtle(format!("Run {} to update {}.", command, if changed == 1 { "it" } else { "them" }))
282
+ );
266
283
  }
267
284
 
268
- if has_errors {
285
+ eprintln!(
286
+ "{}{}",
287
+ ui::STATUS_INDENT,
288
+ ui::subtle(format!(
289
+ "{} · {} · {} · {}",
290
+ pluralize(seen.len(), "file", "files"),
291
+ pluralize(rules, "rule", "rules"),
292
+ pluralize(steps, "step", "steps"),
293
+ format_duration(elapsed)
294
+ ))
295
+ );
296
+
297
+ if has_errors || (!write && has_changes) {
269
298
  process::exit(1);
270
299
  }
271
300
  }
272
301
 
302
+ fn format_duration(elapsed: std::time::Duration) -> String {
303
+ if elapsed.as_secs() == 0 {
304
+ format!("{}ms", elapsed.as_millis())
305
+ } else {
306
+ format!("{:.1}s", elapsed.as_secs_f64())
307
+ }
308
+ }
309
+
273
310
  pub(crate) fn resolve_move_indexes(
274
311
  document: &yerba::Document,
275
312
  path: &str,
@@ -279,10 +316,8 @@ pub(crate) fn resolve_move_indexes(
279
316
  to: Option<usize>,
280
317
  resolve: impl Fn(&yerba::Document, &str, &str) -> Result<usize, yerba::YerbaError>,
281
318
  ) -> (usize, usize) {
282
- use color::*;
283
-
284
319
  let from_index = resolve(document, path, item).unwrap_or_else(|error| {
285
- eprintln!("{RED}Error:{RESET} {}", error);
320
+ eprintln!("{} {}", ui::failure("Error:"), error);
286
321
  process::exit(1);
287
322
  });
288
323
 
@@ -290,7 +325,7 @@ pub(crate) fn resolve_move_indexes(
290
325
  index
291
326
  } else if let Some(target) = &before {
292
327
  let target_index = resolve(document, path, target).unwrap_or_else(|error| {
293
- eprintln!("{RED}Error:{RESET} {}", error);
328
+ eprintln!("{} {}", ui::failure("Error:"), error);
294
329
  process::exit(1);
295
330
  });
296
331
 
@@ -301,7 +336,7 @@ pub(crate) fn resolve_move_indexes(
301
336
  }
302
337
  } else if let Some(target) = &after {
303
338
  let target_index = resolve(document, path, target).unwrap_or_else(|error| {
304
- eprintln!("{RED}Error:{RESET} {}", error);
339
+ eprintln!("{} {}", ui::failure("Error:"), error);
305
340
  process::exit(1);
306
341
  });
307
342
 
@@ -311,7 +346,7 @@ pub(crate) fn resolve_move_indexes(
311
346
  target_index + 1
312
347
  }
313
348
  } else {
314
- eprintln!("{RED}Error:{RESET} specify --before, --after, or --to");
349
+ eprintln!("{} specify --before, --after, or --to", ui::failure("Error:"));
315
350
  process::exit(1);
316
351
  };
317
352
 
@@ -319,12 +354,10 @@ pub(crate) fn resolve_move_indexes(
319
354
  }
320
355
 
321
356
  pub(crate) fn resolve_files(pattern: &str) -> Vec<String> {
322
- use color::*;
323
-
324
357
  if pattern.contains('*') || pattern.contains('?') || pattern.contains('[') {
325
358
  let paths: Vec<String> = glob::glob(pattern)
326
359
  .unwrap_or_else(|error| {
327
- eprintln!("{RED}Error:{RESET} invalid glob pattern '{}': {}", pattern, error);
360
+ eprintln!("{} invalid glob pattern '{}': {}", ui::failure("Error:"), pattern, error);
328
361
  process::exit(1);
329
362
  })
330
363
  .filter_map(|entry| entry.ok())
@@ -332,7 +365,7 @@ pub(crate) fn resolve_files(pattern: &str) -> Vec<String> {
332
365
  .collect();
333
366
 
334
367
  if paths.is_empty() {
335
- eprintln!("{RED}Error:{RESET} no files matched pattern: {}", pattern);
368
+ eprintln!("{} no files matched pattern: {}", ui::failure("Error:"), pattern);
336
369
  process::exit(1);
337
370
  }
338
371
 
@@ -343,18 +376,16 @@ pub(crate) fn resolve_files(pattern: &str) -> Vec<String> {
343
376
  }
344
377
 
345
378
  pub(crate) fn parse_file(file: &str) -> yerba::Document {
346
- use color::*;
347
-
348
379
  yerba::parse_file(file).unwrap_or_else(|error| {
349
380
  match &error {
350
381
  yerba::YerbaError::IoError(io_error) => match io_error.kind() {
351
- std::io::ErrorKind::NotFound => eprintln!("{RED}Error:{RESET} file not found: {}", file),
382
+ std::io::ErrorKind::NotFound => eprintln!("{} file not found: {}", ui::failure("Error:"), file),
352
383
  std::io::ErrorKind::PermissionDenied => {
353
- eprintln!("{RED}Error:{RESET} permission denied: {}", file)
384
+ eprintln!("{} permission denied: {}", ui::failure("Error:"), file)
354
385
  }
355
- _ => eprintln!("{RED}Error:{RESET} reading {}: {}", file, io_error),
386
+ _ => eprintln!("{} reading {}: {}", ui::failure("Error:"), file, io_error),
356
387
  },
357
- _ => eprintln!("{RED}Error:{RESET} parsing {}: {}", file, error),
388
+ _ => eprintln!("{} parsing {}: {}", ui::failure("Error:"), file, error),
358
389
  }
359
390
 
360
391
  process::exit(1);
@@ -366,21 +397,19 @@ pub(crate) fn run_op(display_file: &str, document: &yerba::Document, result: Res
366
397
  }
367
398
 
368
399
  pub(crate) fn run_op_with_hint(display_file: &str, document: &yerba::Document, result: Result<(), yerba::YerbaError>, hint: Option<&str>) {
369
- use color::*;
370
-
371
400
  if let Err(error) = result {
372
401
  if let yerba::YerbaError::SelectorNotFound(selector) = &error {
373
- eprintln!("{RED}Error:{RESET} selector \"{selector}\" not found in {display_file}");
402
+ eprintln!("{} selector \"{selector}\" not found in {display_file}", ui::failure("Error:"));
374
403
 
375
404
  show_similar_selectors(display_file, document, selector);
376
405
  } else {
377
- eprintln!("{RED}Error:{RESET} {}", error);
406
+ eprintln!("{} {}", ui::failure("Error:"), error);
378
407
  }
379
408
 
380
409
  if let Some(hint) = hint {
381
410
  if matches!(error, yerba::YerbaError::SelectorNotFound(_)) {
382
411
  eprintln!();
383
- eprintln!(" {DIM}Hint: {hint}{RESET}");
412
+ eprintln!(" {}", ui::subtle(format!("Hint: {}", hint)));
384
413
  }
385
414
  }
386
415
 
@@ -389,7 +418,6 @@ pub(crate) fn run_op_with_hint(display_file: &str, document: &yerba::Document, r
389
418
  }
390
419
 
391
420
  pub(crate) fn show_similar_selectors(file: &str, document: &yerba::Document, invalid_path: &str) {
392
- use color::*;
393
421
  use yerba::didyoumean::didyoumean_ranked;
394
422
 
395
423
  let selectors = document.selectors();
@@ -405,19 +433,19 @@ pub(crate) fn show_similar_selectors(file: &str, document: &yerba::Document, inv
405
433
  eprintln!();
406
434
 
407
435
  if close.is_empty() {
408
- eprintln!(" {BOLD}Available selectors in {file}:{RESET}");
436
+ eprintln!(" {}", ui::strong(format!("Available selectors in {}:", file)));
409
437
 
410
438
  for selector in selectors.iter().take(10) {
411
- eprintln!(" {DIM}{selector}{RESET}");
439
+ eprintln!(" {}", ui::subtle(selector));
412
440
  }
413
441
 
414
442
  if selectors.len() > 10 {
415
- eprintln!(" {DIM}... and {} more{RESET}", selectors.len() - 10);
443
+ eprintln!(" {}", ui::subtle(format!("... and {} more", selectors.len() - 10)));
416
444
  }
417
445
  } else if close.len() == 1 {
418
- eprintln!(" {BOLD}Did you mean this selector?{RESET} {}", close[0]);
446
+ eprintln!(" {} {}", ui::strong("Did you mean this selector?"), close[0]);
419
447
  } else {
420
- eprintln!(" {BOLD}Did you mean one of these selectors?{RESET}");
448
+ eprintln!(" {}", ui::strong("Did you mean one of these selectors?"));
421
449
 
422
450
  for selector in close.iter().take(5) {
423
451
  eprintln!(" {selector}");
@@ -425,8 +453,8 @@ pub(crate) fn show_similar_selectors(file: &str, document: &yerba::Document, inv
425
453
  }
426
454
 
427
455
  eprintln!();
428
- eprintln!(" {BOLD}To see all valid selectors, run:{RESET}");
429
- eprintln!(" yerba selectors \"{file}\"{RESET}");
456
+ eprintln!(" {}", ui::strong("To see all valid selectors, run:"));
457
+ eprintln!(" yerba selectors \"{file}\"");
430
458
  }
431
459
 
432
460
  pub(crate) fn output(file: &str, document: &yerba::Document, dry_run: bool) {
@@ -1,3 +1,4 @@
1
+ use super::ui;
1
2
  use std::process;
2
3
  use std::sync::LazyLock;
3
4
 
@@ -43,10 +44,11 @@ impl Args {
43
44
  let (target_parent, target_key) = target.rsplit_once('.').unwrap_or(("", &target));
44
45
 
45
46
  if target_parent != parent_path {
46
- use super::color::*;
47
47
  eprintln!(
48
- "{RED}Error:{RESET} cannot move key across different maps ({} \u{2192} {})\n\n Use 'yerba rename' to relocate keys to a different path.",
49
- self.selector, target
48
+ "{} cannot move key across different maps ({} \u{2192} {})\n\n Use 'yerba rename' to relocate keys to a different path.",
49
+ ui::failure("Error:"),
50
+ self.selector,
51
+ target
50
52
  );
51
53
 
52
54
  process::exit(1);
@@ -59,10 +61,11 @@ impl Args {
59
61
  let (target_parent, target_key) = target.rsplit_once('.').unwrap_or(("", &target));
60
62
 
61
63
  if target_parent != parent_path {
62
- use super::color::*;
63
64
  eprintln!(
64
- "{RED}Error:{RESET} cannot move key across different maps ({} \u{2192} {})\n\n Use 'yerba rename' to relocate keys to a different path.",
65
- self.selector, target
65
+ "{} cannot move key across different maps ({} \u{2192} {})\n\n Use 'yerba rename' to relocate keys to a different path.",
66
+ ui::failure("Error:"),
67
+ self.selector,
68
+ target
66
69
  );
67
70
 
68
71
  process::exit(1);
@@ -1,3 +1,4 @@
1
+ use super::ui;
1
2
  use std::sync::LazyLock;
2
3
 
3
4
  use indoc::indoc;
@@ -38,8 +39,8 @@ pub struct Args {
38
39
  impl Args {
39
40
  pub fn run(self) {
40
41
  if self.values.is_none() && self.keys.is_none() {
41
- use super::color::*;
42
- eprintln!("{RED}Error:{RESET} specify --values, --keys, or both");
42
+ use super::ui;
43
+ eprintln!("{} specify --values, --keys, or both", ui::failure("Error:"));
43
44
  std::process::exit(1);
44
45
  }
45
46
 
@@ -55,9 +56,7 @@ impl Args {
55
56
  if let Some(style) = &self.values {
56
57
  if let Ok(warnings) = document.enforce_quotes_at(style, selector) {
57
58
  for warning in warnings {
58
- use super::color::*;
59
-
60
- eprintln!("{YELLOW}Warning:{RESET} {} — {}", resolved_file, warning);
59
+ eprintln!("{} {} — {}", ui::pending("Warning:"), resolved_file, warning);
61
60
  }
62
61
  }
63
62
  }
@@ -1,7 +1,7 @@
1
1
  use std::process;
2
2
 
3
- use super::color::*;
4
3
  use super::resolve_files;
4
+ use super::ui;
5
5
 
6
6
  #[derive(clap::Args)]
7
7
  #[command(
@@ -32,7 +32,7 @@ impl Args {
32
32
  let schema = match yerba::schema::load_schema(&self.schema) {
33
33
  Ok(schema) => schema,
34
34
  Err(error) => {
35
- eprintln!("{RED}Error:{RESET} {}", error);
35
+ eprintln!("{} {}", ui::failure("Error:"), error);
36
36
  process::exit(1);
37
37
  }
38
38
  };
@@ -40,7 +40,7 @@ impl Args {
40
40
  let files = resolve_files(&self.file);
41
41
 
42
42
  if files.is_empty() {
43
- eprintln!("{RED}Error:{RESET} no files matching: {}", self.file);
43
+ eprintln!("{} no files matching: {}", ui::failure("Error:"), self.file);
44
44
  process::exit(1);
45
45
  }
46
46
 
@@ -51,7 +51,7 @@ impl Args {
51
51
  let document = match yerba::Document::parse_file(file) {
52
52
  Ok(document) => document,
53
53
  Err(error) => {
54
- eprintln!(" {RED}error:{RESET} {} {DIM}—{RESET} {}", file, error);
54
+ eprintln!(" {} {} {} {}", ui::failure("error:"), ui::subtle("—"), file, error);
55
55
  has_errors = true;
56
56
  continue;
57
57
  }
@@ -66,7 +66,7 @@ impl Args {
66
66
  has_errors = true;
67
67
 
68
68
  let relative = file.strip_prefix("./").unwrap_or(file);
69
- eprintln!(" {RED}error:{RESET} {relative}");
69
+ eprintln!(" {} {relative}", ui::failure("error:"));
70
70
 
71
71
  for error in &errors {
72
72
  eprintln!(" {error}");
@@ -78,7 +78,7 @@ impl Args {
78
78
  if has_errors {
79
79
  process::exit(1);
80
80
  } else {
81
- eprintln!("{GREEN}All {count} files valid.{RESET}", count = files.len());
81
+ eprintln!("{}", ui::success(format!("All {} files valid.", files.len())));
82
82
  }
83
83
  }
84
84
  }
@@ -4,7 +4,7 @@ use std::sync::LazyLock;
4
4
  use indoc::indoc;
5
5
 
6
6
  use super::colorize_examples;
7
- use super::{color, parse_file, resolve_files};
7
+ use super::{parse_file, resolve_files, ui};
8
8
 
9
9
  static EXAMPLES: LazyLock<String> = LazyLock::new(|| {
10
10
  colorize_examples(indoc! {r#"
@@ -108,7 +108,7 @@ impl Args {
108
108
  if let Some(label) = info.count_label() {
109
109
  let padding = max_selector_len - selector.len() + 2;
110
110
 
111
- println!("{}{}{}{}{}", selector, " ".repeat(padding), color::DIM, label, color::RESET);
111
+ println!("{}{}{}", selector, " ".repeat(padding), ui::subtle(label));
112
112
  } else {
113
113
  println!("{}", selector);
114
114
  }