yerba 0.8.0 → 0.8.1

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ef6a9038318ee4d534952892c7893d4deffc0980da76ad2967996ade55c5c4ed
4
- data.tar.gz: 4c8ae4131876cbbefdfe0ab13bb2c8624161f07332e05dea0287f4a5eb313040
3
+ metadata.gz: a769e82895bc0bea8b8e25391f39cdbf8e915a46d524efd05af951738b4b046a
4
+ data.tar.gz: 83d41f698f888f6065729b8adb5e6646e14b9832871eafde6af2fa60bfd4b862
5
5
  SHA512:
6
- metadata.gz: c8bbc570ef74e8ec8b63cf97ed16d3cd1497ad9d7a77a9b09ace07b3418c9e8f592bcccad2929f33341ecb547411ea2cb3387f905963b86bd5c37e324ff5931f
7
- data.tar.gz: 751785bf9a917245bf914e30cbf0b2bbcf1608be37c3a3d573a94e1f27fbab8fc245d2fc16efae0582e22e670aa5437975fda12bf8075ec15fc6249f697f0dd3
6
+ metadata.gz: 64e3fe5431316bc5faf03187419f234f52c9d8f04710d06afdd6bf13dc5342a80dadae3a4345ee610f4f307976d45520f0cc6b4bcc7a7959e21a9902ae071343
7
+ data.tar.gz: 3f12d6fa79ed13905bca60481eb3a87caffa46fd8205b6a9d56a8fcf250ce708698e218a4cec7126294940eb6ce8f44e02abf445144c12b9f39ad16f417a1da7
data/lib/yerba/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Yerba
4
- VERSION = "0.8.0"
4
+ VERSION = "0.8.1"
5
5
  end
data/rust/Cargo.toml CHANGED
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "yerba"
3
- version = "0.8.0"
3
+ version = "0.8.1"
4
4
  edition = "2021"
5
5
  authors = ["Marco Roth <marco.roth@intergga.ch>"]
6
6
  description = "YAML Editing and Refactoring with Better Accuracy"
@@ -18,21 +18,30 @@ crate-type = ["cdylib", "staticlib", "rlib"]
18
18
  [[bin]]
19
19
  name = "yerba"
20
20
  path = "src/main.rs"
21
+ required-features = ["cli"]
22
+
23
+ [features]
24
+ default = ["cli"]
25
+ cli = ["dep:clap", "dep:indoc", "dep:anstyle-query", "glob", "schema"]
26
+ glob = ["dep:glob", "dep:rayon"]
27
+ schema = ["dep:jsonschema"]
21
28
 
22
29
  [dependencies]
23
30
  yaml_parser = "0.3"
24
31
  rowan = "0.16"
25
- glob = "0.3"
32
+ glob = { version = "0.3", optional = true }
26
33
  serde = { version = "1", features = ["derive"] }
27
34
  yaml_serde = "0.10"
28
35
  serde_json = { version = "1", features = ["preserve_order"] }
29
- clap = { version = "4", features = ["derive"] }
30
- indoc = "2"
31
- rayon = "1"
32
- jsonschema = "0.29"
36
+ clap = { version = "4", features = ["derive"], optional = true }
37
+ anstyle-query = { version = "1", optional = true }
38
+ indoc = { version = "2", optional = true }
39
+ rayon = { version = "1", optional = true }
40
+ jsonschema = { version = "0.29", default-features = false, optional = true }
33
41
 
34
42
  [build-dependencies]
35
43
  cbindgen = "0.28"
36
44
 
37
45
  [dev-dependencies]
46
+ indoc = "2"
38
47
  tempfile = "3"
@@ -37,9 +37,9 @@ impl Args {
37
37
  } else if let Ok(count) = self.first.parse::<usize>() {
38
38
  ("", count)
39
39
  } else {
40
- use super::color::*;
40
+ use super::ui;
41
41
 
42
- eprintln!("{RED}Error:{RESET} expected a number for blank line count, got '{}'", self.first);
42
+ eprintln!("{} expected a number for blank line count, got '{}'", ui::failure("Error:"), self.first);
43
43
 
44
44
  std::process::exit(1);
45
45
  };
@@ -1,3 +1,4 @@
1
+ use super::ui;
1
2
  use std::sync::LazyLock;
2
3
 
3
4
  use indoc::indoc;
@@ -35,14 +36,13 @@ pub struct Args {
35
36
  impl Args {
36
37
  pub fn run(self) {
37
38
  if !self.ensure && !self.remove {
38
- use super::color::*;
39
- eprintln!("{RED}Error:{RESET} specify --ensure or --remove");
39
+ use super::ui;
40
+ eprintln!("{} specify --ensure or --remove", ui::failure("Error:"));
40
41
  std::process::exit(1);
41
42
  }
42
43
 
43
44
  if self.ensure && self.remove {
44
- use super::color::*;
45
- eprintln!("{RED}Error:{RESET} --ensure and --remove are mutually exclusive");
45
+ eprintln!("{} --ensure and --remove are mutually exclusive", ui::failure("Error:"));
46
46
  std::process::exit(1);
47
47
  }
48
48
 
@@ -1,3 +1,4 @@
1
+ use super::ui;
1
2
  use std::process;
2
3
  use std::sync::LazyLock;
3
4
 
@@ -64,9 +65,9 @@ impl Args {
64
65
 
65
66
  if let Some(condition) = &normalized_condition {
66
67
  if let Err(error) = yerba::validate_item_condition(condition) {
67
- use super::color::*;
68
+ use super::ui;
68
69
 
69
- eprintln!("{RED}Error:{RESET} {}", error);
70
+ eprintln!("{} {}", ui::failure("Error:"), error);
70
71
 
71
72
  process::exit(1);
72
73
  }
@@ -85,9 +86,7 @@ impl Args {
85
86
  continue;
86
87
  }
87
88
 
88
- use super::color::*;
89
-
90
- eprintln!("{RED}Error:{RESET} selector \"{}\" not found in {}", self.selector, self.file);
89
+ eprintln!("{} selector \"{}\" not found in {}", ui::failure("Error:"), self.selector, self.file);
91
90
 
92
91
  show_similar_selectors(&self.file, &document, &self.selector);
93
92
  process::exit(1);
@@ -110,8 +109,7 @@ impl Args {
110
109
  break;
111
110
  }
112
111
 
113
- use super::color::*;
114
- eprintln!("{RED}Error:{RESET} select field \"{}\" not found in {}", field.trim(), self.file);
112
+ eprintln!("{} select field \"{}\" not found in {}", ui::failure("Error:"), field.trim(), self.file);
115
113
  show_similar_selectors(&self.file, &document, &full_selector);
116
114
  process::exit(1);
117
115
  }
@@ -190,10 +188,9 @@ impl Args {
190
188
  }
191
189
 
192
190
  if is_glob && all_results.is_empty() && normalized_condition.is_none() {
193
- use super::color::*;
194
-
195
191
  eprintln!(
196
- "{RED}Error:{RESET} selector \"{}\" not found in any of the {} files matching \"{}\"",
192
+ "{} selector \"{}\" not found in any of the {} files matching \"{}\"",
193
+ ui::failure("Error:"),
197
194
  self.selector,
198
195
  files.len(),
199
196
  self.file
@@ -3,13 +3,13 @@ use std::process;
3
3
 
4
4
  use indoc::indoc;
5
5
 
6
- use super::color::*;
6
+ use super::ui;
7
7
 
8
8
  pub fn run() {
9
9
  let filename = "Yerbafile";
10
10
 
11
11
  if Path::new(filename).exists() {
12
- eprintln!("🧉 {YELLOW}{BOLD}Yerbafile already exists.{RESET}");
12
+ eprintln!("🧉 {}", ui::pending("Yerbafile already exists."));
13
13
  process::exit(1);
14
14
  }
15
15
 
@@ -81,9 +81,9 @@ pub fn run() {
81
81
  "#};
82
82
 
83
83
  std::fs::write(filename, content).unwrap_or_else(|error| {
84
- eprintln!("{RED}Error:{RESET} writing Yerbafile: {}", error);
84
+ eprintln!("{} writing Yerbafile: {}", ui::failure("Error:"), error);
85
85
  process::exit(1);
86
86
  });
87
87
 
88
- eprintln!("🧉 {GREEN}{BOLD}Created Yerbafile{RESET}");
88
+ eprintln!("🧉 {}", ui::success("Created Yerbafile"));
89
89
  }
@@ -1,3 +1,4 @@
1
+ use super::ui;
1
2
  use std::sync::LazyLock;
2
3
 
3
4
  use indoc::indoc;
@@ -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>,
@@ -49,8 +51,8 @@ impl Args {
49
51
  let mut buffer = String::new();
50
52
 
51
53
  std::io::stdin().read_to_string(&mut buffer).unwrap_or_else(|error| {
52
- use super::color::*;
53
- eprintln!("{RED}Error:{RESET} reading stdin: {}", error);
54
+ use super::ui;
55
+ eprintln!("{} reading stdin: {}", ui::failure("Error:"), error);
54
56
 
55
57
  std::process::exit(1);
56
58
  });
@@ -59,8 +61,7 @@ impl Args {
59
61
  } else {
60
62
  std::fs::read_to_string(&from_path)
61
63
  .unwrap_or_else(|error| {
62
- use super::color::*;
63
- eprintln!("{RED}Error:{RESET} reading {}: {}", from_path, error);
64
+ eprintln!("{} reading {}: {}", ui::failure("Error:"), from_path, error);
64
65
  std::process::exit(1);
65
66
  })
66
67
  .trim()
@@ -69,8 +70,7 @@ impl Args {
69
70
  } else if let Some(val) = self.value {
70
71
  val
71
72
  } else {
72
- use super::color::*;
73
- eprintln!("{RED}Error:{RESET} either a value argument or --from is required");
73
+ eprintln!("{} either a value argument or --from is required", ui::failure("Error:"));
74
74
 
75
75
  std::process::exit(1);
76
76
  };
@@ -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,35 @@ 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!("{}", ui::banner());
192
+
193
+ eprintln!(
194
+ "{}{} {}\n",
195
+ ui::INDENT,
196
+ ui::strong("Using Yerbafile rules"),
197
+ ui::subtle(format!("(from {})", yerbafile_path.display()))
198
+ );
199
+
200
+ let started = std::time::Instant::now();
220
201
 
221
202
  let results = if files.is_empty() {
222
203
  yerbafile.apply(write)
@@ -224,6 +205,8 @@ pub(crate) fn run_yerbafile(write: bool, files: Vec<String>) {
224
205
  files.iter().flat_map(|file| yerbafile.apply_file(file, write)).collect()
225
206
  };
226
207
 
208
+ let elapsed = started.elapsed();
209
+
227
210
  let mut has_changes = false;
228
211
  let mut has_errors = false;
229
212
 
@@ -231,7 +214,8 @@ pub(crate) fn run_yerbafile(write: bool, files: Vec<String>) {
231
214
 
232
215
  for result in &results {
233
216
  if let Some(error) = &result.error {
234
- eprintln!(" {RED}error:{RESET} {} {DIM}—{RESET} {}", result.file, error);
217
+ eprintln!(" {} {}", ui::failure(ui::glyph::FAIL), ui::strong(&result.file));
218
+ eprintln!(" {}", ui::muted(error));
235
219
 
236
220
  if github {
237
221
  use yerba::error::GitHubAnnotations;
@@ -244,12 +228,15 @@ pub(crate) fn run_yerbafile(write: bool, files: Vec<String>) {
244
228
  has_errors = true;
245
229
  } else if result.changed {
246
230
  if write {
247
- eprintln!(" {GREEN}updated:{RESET} {}", result.file);
231
+ eprintln!(" {} {}", ui::success(ui::glyph::OK), result.file);
248
232
  } else {
249
- eprintln!(" {YELLOW}would change:{RESET} {}", result.file);
233
+ eprintln!(" {} {}", ui::pending(ui::glyph::PENDING), result.file);
250
234
 
251
235
  if github {
252
- eprintln!("::error file={}::File does not match Yerbafile rules", result.file);
236
+ eprintln!(
237
+ "::error file={}::File does not match Yerbafile rules. Run `yerba apply` to update it.",
238
+ result.file
239
+ );
253
240
  }
254
241
  }
255
242
 
@@ -257,19 +244,70 @@ pub(crate) fn run_yerbafile(write: bool, files: Vec<String>) {
257
244
  }
258
245
  }
259
246
 
260
- if !has_changes && !has_errors {
261
- eprintln!("\n{BOLD}{GREEN}All files match the rules.{RESET}");
247
+ let changed = results.iter().filter(|result| result.error.is_none() && result.changed).count();
248
+ let seen: std::collections::HashSet<&String> = results.iter().map(|result| &result.file).collect();
249
+ let rules = yerbafile.rules.len() + usize::from(!yerbafile.pipeline.is_empty());
250
+ let steps = yerbafile.pipeline.len() + yerbafile.rules.iter().map(|rule| rule.pipeline.len()).sum::<usize>();
251
+
252
+ if seen.is_empty() {
253
+ eprintln!(
254
+ "{} {} {}",
255
+ ui::pending(ui::glyph::PENDING),
256
+ ui::pending("No files matched."),
257
+ ui::subtle("Check the `files:` patterns in your Yerbafile.")
258
+ );
259
+ } else if !has_changes && !has_errors {
260
+ eprintln!("{} {}", ui::success(ui::glyph::OK), ui::success("Everything's up to date."));
261
+ }
262
+
263
+ if write && has_changes {
264
+ eprintln!(
265
+ "\n{} {}",
266
+ ui::success(ui::glyph::OK),
267
+ ui::success(format!("Freshly brewed {}.", pluralize(changed, "file", "files")))
268
+ );
262
269
  }
263
270
 
264
271
  if !write && has_changes {
265
- process::exit(1);
272
+ let command = if files.is_empty() {
273
+ "yerba apply".to_string()
274
+ } else {
275
+ format!("yerba apply {}", files.join(" "))
276
+ };
277
+
278
+ eprintln!(
279
+ "\n{} {} {}",
280
+ ui::pending(ui::glyph::PENDING),
281
+ ui::pending(format!("{} out of date.", pluralize(changed, "file", "files"))),
282
+ ui::subtle(format!("Run {} to update {}.", command, if changed == 1 { "it" } else { "them" }))
283
+ );
266
284
  }
267
285
 
268
- if has_errors {
286
+ eprintln!(
287
+ "{}{}",
288
+ ui::STATUS_INDENT,
289
+ ui::subtle(format!(
290
+ "{} · {} · {} · {}",
291
+ pluralize(seen.len(), "file", "files"),
292
+ pluralize(rules, "rule", "rules"),
293
+ pluralize(steps, "step", "steps"),
294
+ format_duration(elapsed)
295
+ ))
296
+ );
297
+
298
+ if has_errors || (!write && has_changes) {
269
299
  process::exit(1);
270
300
  }
271
301
  }
272
302
 
303
+ fn format_duration(elapsed: std::time::Duration) -> String {
304
+ if elapsed.as_secs() == 0 {
305
+ format!("{}ms", elapsed.as_millis())
306
+ } else {
307
+ format!("{:.1}s", elapsed.as_secs_f64())
308
+ }
309
+ }
310
+
273
311
  pub(crate) fn resolve_move_indexes(
274
312
  document: &yerba::Document,
275
313
  path: &str,
@@ -279,10 +317,8 @@ pub(crate) fn resolve_move_indexes(
279
317
  to: Option<usize>,
280
318
  resolve: impl Fn(&yerba::Document, &str, &str) -> Result<usize, yerba::YerbaError>,
281
319
  ) -> (usize, usize) {
282
- use color::*;
283
-
284
320
  let from_index = resolve(document, path, item).unwrap_or_else(|error| {
285
- eprintln!("{RED}Error:{RESET} {}", error);
321
+ eprintln!("{} {}", ui::failure("Error:"), error);
286
322
  process::exit(1);
287
323
  });
288
324
 
@@ -290,7 +326,7 @@ pub(crate) fn resolve_move_indexes(
290
326
  index
291
327
  } else if let Some(target) = &before {
292
328
  let target_index = resolve(document, path, target).unwrap_or_else(|error| {
293
- eprintln!("{RED}Error:{RESET} {}", error);
329
+ eprintln!("{} {}", ui::failure("Error:"), error);
294
330
  process::exit(1);
295
331
  });
296
332
 
@@ -301,7 +337,7 @@ pub(crate) fn resolve_move_indexes(
301
337
  }
302
338
  } else if let Some(target) = &after {
303
339
  let target_index = resolve(document, path, target).unwrap_or_else(|error| {
304
- eprintln!("{RED}Error:{RESET} {}", error);
340
+ eprintln!("{} {}", ui::failure("Error:"), error);
305
341
  process::exit(1);
306
342
  });
307
343
 
@@ -311,7 +347,7 @@ pub(crate) fn resolve_move_indexes(
311
347
  target_index + 1
312
348
  }
313
349
  } else {
314
- eprintln!("{RED}Error:{RESET} specify --before, --after, or --to");
350
+ eprintln!("{} specify --before, --after, or --to", ui::failure("Error:"));
315
351
  process::exit(1);
316
352
  };
317
353
 
@@ -319,12 +355,10 @@ pub(crate) fn resolve_move_indexes(
319
355
  }
320
356
 
321
357
  pub(crate) fn resolve_files(pattern: &str) -> Vec<String> {
322
- use color::*;
323
-
324
358
  if pattern.contains('*') || pattern.contains('?') || pattern.contains('[') {
325
359
  let paths: Vec<String> = glob::glob(pattern)
326
360
  .unwrap_or_else(|error| {
327
- eprintln!("{RED}Error:{RESET} invalid glob pattern '{}': {}", pattern, error);
361
+ eprintln!("{} invalid glob pattern '{}': {}", ui::failure("Error:"), pattern, error);
328
362
  process::exit(1);
329
363
  })
330
364
  .filter_map(|entry| entry.ok())
@@ -332,7 +366,7 @@ pub(crate) fn resolve_files(pattern: &str) -> Vec<String> {
332
366
  .collect();
333
367
 
334
368
  if paths.is_empty() {
335
- eprintln!("{RED}Error:{RESET} no files matched pattern: {}", pattern);
369
+ eprintln!("{} no files matched pattern: {}", ui::failure("Error:"), pattern);
336
370
  process::exit(1);
337
371
  }
338
372
 
@@ -343,18 +377,16 @@ pub(crate) fn resolve_files(pattern: &str) -> Vec<String> {
343
377
  }
344
378
 
345
379
  pub(crate) fn parse_file(file: &str) -> yerba::Document {
346
- use color::*;
347
-
348
380
  yerba::parse_file(file).unwrap_or_else(|error| {
349
381
  match &error {
350
382
  yerba::YerbaError::IoError(io_error) => match io_error.kind() {
351
- std::io::ErrorKind::NotFound => eprintln!("{RED}Error:{RESET} file not found: {}", file),
383
+ std::io::ErrorKind::NotFound => eprintln!("{} file not found: {}", ui::failure("Error:"), file),
352
384
  std::io::ErrorKind::PermissionDenied => {
353
- eprintln!("{RED}Error:{RESET} permission denied: {}", file)
385
+ eprintln!("{} permission denied: {}", ui::failure("Error:"), file)
354
386
  }
355
- _ => eprintln!("{RED}Error:{RESET} reading {}: {}", file, io_error),
387
+ _ => eprintln!("{} reading {}: {}", ui::failure("Error:"), file, io_error),
356
388
  },
357
- _ => eprintln!("{RED}Error:{RESET} parsing {}: {}", file, error),
389
+ _ => eprintln!("{} parsing {}: {}", ui::failure("Error:"), file, error),
358
390
  }
359
391
 
360
392
  process::exit(1);
@@ -366,21 +398,19 @@ pub(crate) fn run_op(display_file: &str, document: &yerba::Document, result: Res
366
398
  }
367
399
 
368
400
  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
401
  if let Err(error) = result {
372
402
  if let yerba::YerbaError::SelectorNotFound(selector) = &error {
373
- eprintln!("{RED}Error:{RESET} selector \"{selector}\" not found in {display_file}");
403
+ eprintln!("{} selector \"{selector}\" not found in {display_file}", ui::failure("Error:"));
374
404
 
375
405
  show_similar_selectors(display_file, document, selector);
376
406
  } else {
377
- eprintln!("{RED}Error:{RESET} {}", error);
407
+ eprintln!("{} {}", ui::failure("Error:"), error);
378
408
  }
379
409
 
380
410
  if let Some(hint) = hint {
381
411
  if matches!(error, yerba::YerbaError::SelectorNotFound(_)) {
382
412
  eprintln!();
383
- eprintln!(" {DIM}Hint: {hint}{RESET}");
413
+ eprintln!(" {}", ui::subtle(format!("Hint: {}", hint)));
384
414
  }
385
415
  }
386
416
 
@@ -389,7 +419,6 @@ pub(crate) fn run_op_with_hint(display_file: &str, document: &yerba::Document, r
389
419
  }
390
420
 
391
421
  pub(crate) fn show_similar_selectors(file: &str, document: &yerba::Document, invalid_path: &str) {
392
- use color::*;
393
422
  use yerba::didyoumean::didyoumean_ranked;
394
423
 
395
424
  let selectors = document.selectors();
@@ -405,19 +434,19 @@ pub(crate) fn show_similar_selectors(file: &str, document: &yerba::Document, inv
405
434
  eprintln!();
406
435
 
407
436
  if close.is_empty() {
408
- eprintln!(" {BOLD}Available selectors in {file}:{RESET}");
437
+ eprintln!(" {}", ui::strong(format!("Available selectors in {}:", file)));
409
438
 
410
439
  for selector in selectors.iter().take(10) {
411
- eprintln!(" {DIM}{selector}{RESET}");
440
+ eprintln!(" {}", ui::subtle(selector));
412
441
  }
413
442
 
414
443
  if selectors.len() > 10 {
415
- eprintln!(" {DIM}... and {} more{RESET}", selectors.len() - 10);
444
+ eprintln!(" {}", ui::subtle(format!("... and {} more", selectors.len() - 10)));
416
445
  }
417
446
  } else if close.len() == 1 {
418
- eprintln!(" {BOLD}Did you mean this selector?{RESET} {}", close[0]);
447
+ eprintln!(" {} {}", ui::strong("Did you mean this selector?"), close[0]);
419
448
  } else {
420
- eprintln!(" {BOLD}Did you mean one of these selectors?{RESET}");
449
+ eprintln!(" {}", ui::strong("Did you mean one of these selectors?"));
421
450
 
422
451
  for selector in close.iter().take(5) {
423
452
  eprintln!(" {selector}");
@@ -425,8 +454,8 @@ pub(crate) fn show_similar_selectors(file: &str, document: &yerba::Document, inv
425
454
  }
426
455
 
427
456
  eprintln!();
428
- eprintln!(" {BOLD}To see all valid selectors, run:{RESET}");
429
- eprintln!(" yerba selectors \"{file}\"{RESET}");
457
+ eprintln!(" {}", ui::strong("To see all valid selectors, run:"));
458
+ eprintln!(" yerba selectors \"{file}\"");
430
459
  }
431
460
 
432
461
  pub(crate) fn output(file: &str, document: &yerba::Document, dry_run: bool) {