yerba 0.8.0-arm-linux-gnu → 0.8.1-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.
- checksums.yaml +4 -4
- data/exe/arm-linux-gnu/yerba +0 -0
- data/lib/yerba/3.2/yerba.so +0 -0
- data/lib/yerba/3.3/yerba.so +0 -0
- data/lib/yerba/3.4/yerba.so +0 -0
- data/lib/yerba/4.0/yerba.so +0 -0
- data/lib/yerba/version.rb +1 -1
- data/rust/Cargo.lock +2 -342
- data/rust/Cargo.toml +15 -6
- data/rust/src/commands/blank_lines.rs +2 -2
- data/rust/src/commands/directives.rs +4 -4
- data/rust/src/commands/get.rs +7 -10
- data/rust/src/commands/init.rs +4 -4
- data/rust/src/commands/insert.rs +6 -6
- data/rust/src/commands/location.rs +2 -2
- data/rust/src/commands/mate.rs +7 -7
- data/rust/src/commands/mod.rs +119 -90
- data/rust/src/commands/move_key.rs +9 -6
- data/rust/src/commands/quote_style.rs +4 -5
- data/rust/src/commands/schema.rs +6 -6
- data/rust/src/commands/selectors.rs +2 -2
- data/rust/src/commands/set.rs +3 -2
- data/rust/src/commands/sort.rs +36 -42
- data/rust/src/commands/sort_keys.rs +2 -2
- data/rust/src/commands/ui.rs +157 -0
- data/rust/src/commands/unique.rs +7 -7
- data/rust/src/commands/version.rs +13 -3
- data/rust/src/document/insert.rs +4 -0
- data/rust/src/document/mod.rs +7 -3
- data/rust/src/document/set.rs +10 -2
- data/rust/src/error.rs +3 -0
- data/rust/src/ffi.rs +5 -0
- data/rust/src/lib.rs +9 -1
- data/rust/src/quote_style.rs +12 -11
- data/rust/src/syntax.rs +132 -1
- data/rust/src/yaml_writer.rs +4 -4
- metadata +3 -2
data/rust/src/commands/sort.rs
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
use super::ui;
|
|
1
2
|
use std::process;
|
|
2
3
|
use std::sync::LazyLock;
|
|
3
4
|
|
|
@@ -53,14 +54,14 @@ fn is_explicit_reorder(orders: &[String]) -> bool {
|
|
|
53
54
|
|
|
54
55
|
impl Args {
|
|
55
56
|
pub fn run(self) {
|
|
56
|
-
use super::
|
|
57
|
+
use super::ui;
|
|
57
58
|
|
|
58
59
|
if self.by.is_empty() && self.order.is_empty() && self.selector.is_none() {
|
|
59
60
|
let document = parse_file(&self.file);
|
|
60
61
|
|
|
61
|
-
eprintln!("{
|
|
62
|
+
eprintln!("{} specify a selector, --by, or --order", ui::failure("Error:"));
|
|
62
63
|
eprintln!();
|
|
63
|
-
eprintln!(" {
|
|
64
|
+
eprintln!(" {}", ui::strong("Examples:"));
|
|
64
65
|
eprintln!(" yerba sort \"{}\" \"tags\"", self.file);
|
|
65
66
|
eprintln!(" yerba sort \"{}\" --by \".title\" --order asc", self.file);
|
|
66
67
|
eprintln!();
|
|
@@ -80,10 +81,8 @@ impl Args {
|
|
|
80
81
|
}
|
|
81
82
|
|
|
82
83
|
fn show_values(self) {
|
|
83
|
-
use super::color::*;
|
|
84
|
-
|
|
85
84
|
if self.by.len() != 1 {
|
|
86
|
-
eprintln!("{
|
|
85
|
+
eprintln!("{} --order is required when using --by", ui::failure("Error:"));
|
|
87
86
|
process::exit(1);
|
|
88
87
|
}
|
|
89
88
|
|
|
@@ -94,14 +93,14 @@ impl Args {
|
|
|
94
93
|
let (labels, context_values, selector_display) = self.resolve_labels(&document, by, selector, &items_selector);
|
|
95
94
|
|
|
96
95
|
let context_hint = if self.context.is_empty() {
|
|
97
|
-
format!("\n\n {
|
|
96
|
+
format!("\n\n {}", ui::subtle("Add --context \".field\" to show additional fields alongside values"))
|
|
98
97
|
} else {
|
|
99
98
|
String::new()
|
|
100
99
|
};
|
|
101
100
|
|
|
102
|
-
eprintln!("{
|
|
101
|
+
eprintln!("{} --order is required when using --by", ui::failure("Error:"));
|
|
103
102
|
eprintln!();
|
|
104
|
-
eprintln!(" {
|
|
103
|
+
eprintln!(" {}", ui::strong(format!("Current values (by {}):", by)));
|
|
105
104
|
|
|
106
105
|
for (index, label) in labels.iter().enumerate() {
|
|
107
106
|
let context = context_values.get(index).map(|c| c.as_slice()).unwrap_or(&[]);
|
|
@@ -113,22 +112,20 @@ impl Args {
|
|
|
113
112
|
|
|
114
113
|
eprintln!("{context_hint}");
|
|
115
114
|
eprintln!();
|
|
116
|
-
eprintln!(" {
|
|
115
|
+
eprintln!(" {}", ui::strong("To sort alphabetically:"));
|
|
117
116
|
eprintln!(" yerba sort \"{}\"{selector_display} --by \"{by}\" --order asc", self.file);
|
|
118
117
|
eprintln!(" yerba sort \"{}\"{selector_display} --by \"{by}\" --order desc", self.file);
|
|
119
118
|
eprintln!();
|
|
120
|
-
eprintln!(" {
|
|
119
|
+
eprintln!(" {}", ui::strong("To reorder explicitly:"));
|
|
121
120
|
eprintln!(" yerba sort \"{}\"{selector_display} --by \"{by}\" --order \"{csv}\"", self.file);
|
|
122
121
|
eprintln!();
|
|
123
|
-
eprintln!(" {
|
|
122
|
+
eprintln!(" {}", ui::strong("To move individual items:"));
|
|
124
123
|
eprintln!(" yerba move \"{}\"{selector_display} <item> --before/--after <target>", self.file);
|
|
125
124
|
|
|
126
125
|
process::exit(1);
|
|
127
126
|
}
|
|
128
127
|
|
|
129
128
|
fn run_sort(self) {
|
|
130
|
-
use super::color::*;
|
|
131
|
-
|
|
132
129
|
let selector = self.selector.as_deref().unwrap_or("");
|
|
133
130
|
|
|
134
131
|
let sort_fields: Vec<yerba::SortField> = if self.by.is_empty() {
|
|
@@ -145,7 +142,7 @@ impl Args {
|
|
|
145
142
|
Some("desc" | "descending") => yerba::SortField::desc(field),
|
|
146
143
|
Some("asc" | "ascending") | None => yerba::SortField::asc(field),
|
|
147
144
|
Some(other) => {
|
|
148
|
-
eprintln!("{
|
|
145
|
+
eprintln!("{} invalid sort direction \"{other}\". Use \"asc\" or \"desc\"", ui::failure("Error:"));
|
|
149
146
|
process::exit(1);
|
|
150
147
|
}
|
|
151
148
|
}
|
|
@@ -161,7 +158,7 @@ impl Args {
|
|
|
161
158
|
|
|
162
159
|
match document.get_value(&first_item_selector) {
|
|
163
160
|
Some(first) if first.is_mapping() => {
|
|
164
|
-
eprintln!("{
|
|
161
|
+
eprintln!("{} --by is required to sort a sequence of maps", ui::failure("Error:"));
|
|
165
162
|
eprintln!();
|
|
166
163
|
|
|
167
164
|
let selectors = document.selectors();
|
|
@@ -175,7 +172,7 @@ impl Args {
|
|
|
175
172
|
.collect();
|
|
176
173
|
|
|
177
174
|
if !fields.is_empty() {
|
|
178
|
-
eprintln!(" {
|
|
175
|
+
eprintln!(" {}", ui::strong("Available fields:"));
|
|
179
176
|
|
|
180
177
|
for field in &fields {
|
|
181
178
|
let short = field.rsplit_once('.').map(|(_, f)| f).unwrap_or(field);
|
|
@@ -187,9 +184,9 @@ impl Args {
|
|
|
187
184
|
}
|
|
188
185
|
|
|
189
186
|
None if selector.is_empty() => {
|
|
190
|
-
eprintln!("{
|
|
187
|
+
eprintln!("{} no sequence found at root level in {}", ui::failure("Error:"), resolved_file);
|
|
191
188
|
eprintln!();
|
|
192
|
-
eprintln!(" {
|
|
189
|
+
eprintln!(" {}", ui::subtle("Specify a selector for the sequence to sort:"));
|
|
193
190
|
eprintln!(" yerba sort \"{}\" \"<selector>\"", self.file);
|
|
194
191
|
eprintln!();
|
|
195
192
|
|
|
@@ -209,16 +206,14 @@ impl Args {
|
|
|
209
206
|
}
|
|
210
207
|
|
|
211
208
|
fn run_reorder(self) {
|
|
212
|
-
use super::color::*;
|
|
213
|
-
|
|
214
209
|
if self.by.len() != 1 {
|
|
215
|
-
eprintln!("{
|
|
210
|
+
eprintln!("{} explicit --order requires exactly one --by field", ui::failure("Error:"));
|
|
216
211
|
|
|
217
212
|
process::exit(1);
|
|
218
213
|
}
|
|
219
214
|
|
|
220
215
|
if self.order.len() != 1 {
|
|
221
|
-
eprintln!("{
|
|
216
|
+
eprintln!("{} explicit --order must be a single comma-separated list", ui::failure("Error:"));
|
|
222
217
|
|
|
223
218
|
process::exit(1);
|
|
224
219
|
}
|
|
@@ -236,17 +231,17 @@ impl Args {
|
|
|
236
231
|
let duplicates: Vec<&String> = labels.iter().filter(|label| !seen.insert(label.as_str())).collect();
|
|
237
232
|
|
|
238
233
|
if !duplicates.is_empty() {
|
|
239
|
-
eprintln!("{
|
|
234
|
+
eprintln!("{} --order requires unique values for {by}, but found duplicates", ui::failure("Error:"));
|
|
240
235
|
eprintln!();
|
|
241
|
-
eprintln!(" {
|
|
236
|
+
eprintln!(" {}", ui::strong("Duplicate values:"));
|
|
242
237
|
|
|
243
238
|
for label in &duplicates {
|
|
244
239
|
eprintln!(" {label}");
|
|
245
240
|
}
|
|
246
241
|
|
|
247
242
|
eprintln!();
|
|
248
|
-
eprintln!(" {
|
|
249
|
-
eprintln!(" choose a --by field with unique values (e.g. \".id\")
|
|
243
|
+
eprintln!(" {}", ui::subtle("Use \"yerba sort\" with --by instead to sort by field, or"));
|
|
244
|
+
eprintln!(" {}", ui::subtle("choose a --by field with unique values (e.g. \".id\")"));
|
|
250
245
|
|
|
251
246
|
process::exit(1);
|
|
252
247
|
}
|
|
@@ -256,16 +251,19 @@ impl Args {
|
|
|
256
251
|
if !values_with_commas.is_empty() {
|
|
257
252
|
let selector_display = if selector.is_empty() { String::new() } else { format!(" \"{selector}\"") };
|
|
258
253
|
|
|
259
|
-
eprintln!(
|
|
254
|
+
eprintln!(
|
|
255
|
+
"{} some values for {by} contain commas, which conflicts with --order parsing",
|
|
256
|
+
ui::failure("Error:")
|
|
257
|
+
);
|
|
260
258
|
eprintln!();
|
|
261
|
-
eprintln!(" {
|
|
259
|
+
eprintln!(" {}", ui::strong("Values with commas:"));
|
|
262
260
|
|
|
263
261
|
for label in &values_with_commas {
|
|
264
262
|
eprintln!(" {label}");
|
|
265
263
|
}
|
|
266
264
|
|
|
267
265
|
eprintln!();
|
|
268
|
-
eprintln!(" {
|
|
266
|
+
eprintln!(" {}", ui::strong("Use yerba move to reorder individual items instead:"));
|
|
269
267
|
eprintln!(" yerba move \"{}\"{selector_display} <item> --before/--after <target>", self.file);
|
|
270
268
|
|
|
271
269
|
process::exit(1);
|
|
@@ -277,28 +275,26 @@ impl Args {
|
|
|
277
275
|
match document.reorder_items(container, by, &desired_order) {
|
|
278
276
|
Ok(()) => output(&self.file, &document, self.dry_run),
|
|
279
277
|
Err(error) => {
|
|
280
|
-
eprintln!("{
|
|
278
|
+
eprintln!("{} {}", ui::failure("Error:"), error);
|
|
281
279
|
process::exit(1);
|
|
282
280
|
}
|
|
283
281
|
}
|
|
284
282
|
}
|
|
285
283
|
|
|
286
284
|
fn resolve_labels(&self, document: &yerba::Document, by: &str, selector: &str, items_selector: &str) -> (Vec<String>, Vec<Vec<String>>, String) {
|
|
287
|
-
use super::color::*;
|
|
288
|
-
|
|
289
285
|
let items = document.get_values(items_selector);
|
|
290
286
|
|
|
291
287
|
if items.is_empty() {
|
|
292
288
|
if selector.is_empty() {
|
|
293
|
-
eprintln!("{
|
|
289
|
+
eprintln!("{} no sequence found at root level", ui::failure("Error:"));
|
|
294
290
|
eprintln!();
|
|
295
|
-
eprintln!(" {
|
|
291
|
+
eprintln!(" {}", ui::subtle("If the file is a map, specify which sequence to sort:"));
|
|
296
292
|
eprintln!(" yerba sort \"{}\" \"<selector>\" --by \"{by}\" --order asc", self.file);
|
|
297
293
|
eprintln!();
|
|
298
294
|
|
|
299
295
|
super::show_similar_selectors(&self.file, document, "[]");
|
|
300
296
|
} else {
|
|
301
|
-
eprintln!("{
|
|
297
|
+
eprintln!("{} no sequence found at selector: {selector}", ui::failure("Error:"));
|
|
302
298
|
|
|
303
299
|
super::show_similar_selectors(&self.file, document, selector);
|
|
304
300
|
}
|
|
@@ -316,7 +312,7 @@ impl Args {
|
|
|
316
312
|
};
|
|
317
313
|
|
|
318
314
|
if !document.exists(&by_selector) {
|
|
319
|
-
eprintln!("{
|
|
315
|
+
eprintln!("{} field \"{by}\" not found in items", ui::failure("Error:"));
|
|
320
316
|
|
|
321
317
|
super::show_similar_selectors(&self.file, document, &by_selector);
|
|
322
318
|
|
|
@@ -367,17 +363,15 @@ impl Args {
|
|
|
367
363
|
}
|
|
368
364
|
|
|
369
365
|
fn format_label_line(&self, index: usize, label: &str, context: &[String]) -> String {
|
|
370
|
-
use super::color::*;
|
|
371
|
-
|
|
372
366
|
if context.is_empty() {
|
|
373
|
-
format!("{
|
|
367
|
+
format!("{} {}", ui::subtle(format!("[{}]", index)), label)
|
|
374
368
|
} else {
|
|
375
369
|
let context = context.iter().filter(|c| !c.is_empty()).cloned().collect::<Vec<_>>().join(", ");
|
|
376
370
|
|
|
377
371
|
if context.is_empty() {
|
|
378
|
-
format!("{
|
|
372
|
+
format!("{} {}", ui::subtle(format!("[{}]", index)), label)
|
|
379
373
|
} else {
|
|
380
|
-
format!("{
|
|
374
|
+
format!("{} {} {}", ui::subtle(format!("[{}]", index)), label, ui::subtle(context))
|
|
381
375
|
}
|
|
382
376
|
}
|
|
383
377
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
use super::ui;
|
|
1
2
|
use std::process;
|
|
2
3
|
use std::sync::LazyLock;
|
|
3
4
|
|
|
@@ -41,8 +42,7 @@ impl Args {
|
|
|
41
42
|
let document = parse_file(resolved_file);
|
|
42
43
|
|
|
43
44
|
if let Err(error) = document.validate_sort_keys(&self.selector, &key_order) {
|
|
44
|
-
|
|
45
|
-
eprintln!("{RED}Error in {}{RESET}: {}", resolved_file, error);
|
|
45
|
+
eprintln!("{}: {}", ui::failure(format!("Error in {}", resolved_file)), error);
|
|
46
46
|
has_errors = true;
|
|
47
47
|
}
|
|
48
48
|
}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
use std::io::IsTerminal;
|
|
2
|
+
use std::sync::OnceLock;
|
|
3
|
+
|
|
4
|
+
fn colored() -> bool {
|
|
5
|
+
static COLORED: OnceLock<bool> = OnceLock::new();
|
|
6
|
+
|
|
7
|
+
*COLORED.get_or_init(|| {
|
|
8
|
+
if std::env::var_os("NO_COLOR").is_some() {
|
|
9
|
+
return false;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
if std::env::var_os("CLICOLOR_FORCE").is_some_and(|value| value != "0") {
|
|
13
|
+
return true;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
std::io::stderr().is_terminal() || super::is_github_actions()
|
|
17
|
+
})
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
#[derive(Clone, Copy)]
|
|
21
|
+
pub struct Color(&'static str);
|
|
22
|
+
|
|
23
|
+
pub const GREEN: Color = Color("32");
|
|
24
|
+
pub const YELLOW: Color = Color("33");
|
|
25
|
+
pub const RED: Color = Color("31");
|
|
26
|
+
pub const CYAN: Color = Color("36");
|
|
27
|
+
|
|
28
|
+
pub struct Style {
|
|
29
|
+
color: Option<Color>,
|
|
30
|
+
bold: bool,
|
|
31
|
+
dim: bool,
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
impl Style {
|
|
35
|
+
pub const fn plain() -> Self {
|
|
36
|
+
Style {
|
|
37
|
+
color: None,
|
|
38
|
+
bold: false,
|
|
39
|
+
dim: false,
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
pub const fn fg(color: Color) -> Self {
|
|
44
|
+
Style {
|
|
45
|
+
color: Some(color),
|
|
46
|
+
bold: false,
|
|
47
|
+
dim: false,
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
pub const fn bold(mut self) -> Self {
|
|
52
|
+
self.bold = true;
|
|
53
|
+
self
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
pub const fn dim(mut self) -> Self {
|
|
57
|
+
self.dim = true;
|
|
58
|
+
self
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
pub fn paint(&self, text: impl std::fmt::Display) -> String {
|
|
62
|
+
if !colored() {
|
|
63
|
+
return text.to_string();
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
let mut codes: Vec<&str> = Vec::new();
|
|
67
|
+
|
|
68
|
+
if self.bold {
|
|
69
|
+
codes.push("1");
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if self.dim {
|
|
73
|
+
codes.push("2");
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if let Some(color) = &self.color {
|
|
77
|
+
codes.push(color.0);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if codes.is_empty() {
|
|
81
|
+
return text.to_string();
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
format!("\x1b[{}m{}\x1b[0m", codes.join(";"), text)
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
pub fn success(text: impl std::fmt::Display) -> String {
|
|
89
|
+
Style::fg(GREEN).bold().paint(text)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
pub fn pending(text: impl std::fmt::Display) -> String {
|
|
93
|
+
Style::fg(YELLOW).bold().paint(text)
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
pub fn failure(text: impl std::fmt::Display) -> String {
|
|
97
|
+
Style::fg(RED).bold().paint(text)
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
pub fn muted(text: impl std::fmt::Display) -> String {
|
|
101
|
+
Style::fg(CYAN).paint(text)
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
pub fn subtle(text: impl std::fmt::Display) -> String {
|
|
105
|
+
Style::plain().dim().paint(text)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
pub fn strong(text: impl std::fmt::Display) -> String {
|
|
109
|
+
Style::plain().bold().paint(text)
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
pub fn banner() -> String {
|
|
113
|
+
format!("🧉 {} {}", Style::fg(GREEN).bold().paint("yerba"), subtle(format!("v{}", yerba::version())))
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
pub const INDENT: &str = " ";
|
|
117
|
+
pub const STATUS_INDENT: &str = " ";
|
|
118
|
+
|
|
119
|
+
pub mod glyph {
|
|
120
|
+
pub const OK: &str = "✓";
|
|
121
|
+
pub const PENDING: &str = "~";
|
|
122
|
+
pub const FAIL: &str = "✗";
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
pub mod raw {
|
|
126
|
+
fn sequence(code: &'static str) -> &'static str {
|
|
127
|
+
if super::colored() {
|
|
128
|
+
code
|
|
129
|
+
} else {
|
|
130
|
+
""
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
pub fn green() -> &'static str {
|
|
135
|
+
sequence("\x1b[32m")
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
pub fn yellow() -> &'static str {
|
|
139
|
+
sequence("\x1b[33m")
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
pub fn bold() -> &'static str {
|
|
143
|
+
sequence("\x1b[1m")
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
pub fn dim() -> &'static str {
|
|
147
|
+
sequence("\x1b[2m")
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
pub fn italic() -> &'static str {
|
|
151
|
+
sequence("\x1b[3m")
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
pub fn reset() -> &'static str {
|
|
155
|
+
sequence("\x1b[0m")
|
|
156
|
+
}
|
|
157
|
+
}
|
data/rust/src/commands/unique.rs
CHANGED
|
@@ -37,7 +37,7 @@ pub struct Args {
|
|
|
37
37
|
|
|
38
38
|
impl Args {
|
|
39
39
|
pub fn run(self) {
|
|
40
|
-
use super::
|
|
40
|
+
use super::ui;
|
|
41
41
|
|
|
42
42
|
let selector = self.selector.as_deref().unwrap_or("");
|
|
43
43
|
let by = self.by.as_deref().unwrap_or(".");
|
|
@@ -50,20 +50,20 @@ impl Args {
|
|
|
50
50
|
let noun = if duplicates.len() == 1 { "duplicate" } else { "duplicates" };
|
|
51
51
|
|
|
52
52
|
if duplicates.is_empty() {
|
|
53
|
-
eprintln!("{
|
|
53
|
+
eprintln!("{} in {}", ui::success("No duplicates found"), resolved_file);
|
|
54
54
|
} else if self.remove {
|
|
55
|
-
eprintln!("{
|
|
55
|
+
eprintln!("{} from {}", ui::pending(format!("Removed {} {noun}", duplicates.len())), resolved_file);
|
|
56
56
|
|
|
57
57
|
for duplicate in &duplicates {
|
|
58
|
-
eprintln!(" {
|
|
58
|
+
eprintln!(" {}", ui::subtle(format!("line {}: {} == {}", duplicate.line, by, duplicate.value)));
|
|
59
59
|
}
|
|
60
60
|
|
|
61
61
|
output(&resolved_file, &document, self.dry_run);
|
|
62
62
|
} else {
|
|
63
|
-
eprintln!("{
|
|
63
|
+
eprintln!("{} in {}", ui::failure(format!("Found {} {noun}", duplicates.len())), resolved_file);
|
|
64
64
|
|
|
65
65
|
for duplicate in &duplicates {
|
|
66
|
-
eprintln!(" {
|
|
66
|
+
eprintln!(" {}", ui::subtle(format!("line {}: {} == {}", duplicate.line, by, duplicate.value)));
|
|
67
67
|
}
|
|
68
68
|
|
|
69
69
|
process::exit(1);
|
|
@@ -71,7 +71,7 @@ impl Args {
|
|
|
71
71
|
}
|
|
72
72
|
|
|
73
73
|
Err(error) => {
|
|
74
|
-
eprintln!("{
|
|
74
|
+
eprintln!("{} {}", ui::failure("Error:"), error);
|
|
75
75
|
process::exit(1);
|
|
76
76
|
}
|
|
77
77
|
}
|
|
@@ -1,8 +1,18 @@
|
|
|
1
|
-
use super::
|
|
1
|
+
use super::ui;
|
|
2
2
|
|
|
3
3
|
pub fn run() {
|
|
4
|
-
println!("
|
|
4
|
+
println!("{}", ui::banner());
|
|
5
5
|
println!(
|
|
6
|
-
" {
|
|
6
|
+
" {}{} {}{} {}{} {}{} {}{}",
|
|
7
|
+
ui::strong("Y"),
|
|
8
|
+
ui::subtle("AML"),
|
|
9
|
+
ui::strong("E"),
|
|
10
|
+
ui::subtle("diting and"),
|
|
11
|
+
ui::strong("R"),
|
|
12
|
+
ui::subtle("efactoring with"),
|
|
13
|
+
ui::strong("B"),
|
|
14
|
+
ui::subtle("etter"),
|
|
15
|
+
ui::strong("A"),
|
|
16
|
+
ui::subtle("ccuracy")
|
|
7
17
|
);
|
|
8
18
|
}
|
data/rust/src/document/insert.rs
CHANGED
|
@@ -570,6 +570,10 @@ impl Document {
|
|
|
570
570
|
let is_block_value = value.contains('\n') || value.starts_with("- ");
|
|
571
571
|
|
|
572
572
|
if !is_block_value {
|
|
573
|
+
if !crate::syntax::is_valid_inline_value(value) {
|
|
574
|
+
return format!("{}: {}", key, crate::syntax::quote_if_needed(value));
|
|
575
|
+
}
|
|
576
|
+
|
|
573
577
|
return format!("{}: {}", key, value);
|
|
574
578
|
}
|
|
575
579
|
|
data/rust/src/document/mod.rs
CHANGED
|
@@ -1,13 +1,17 @@
|
|
|
1
1
|
mod condition;
|
|
2
|
-
pub use condition::{validate_condition, validate_item_condition};
|
|
3
2
|
mod delete;
|
|
4
3
|
mod get;
|
|
5
4
|
mod insert;
|
|
6
|
-
mod schema;
|
|
7
5
|
mod set;
|
|
8
6
|
mod sort;
|
|
9
|
-
pub mod style;
|
|
10
7
|
mod unique;
|
|
8
|
+
|
|
9
|
+
#[cfg(feature = "schema")]
|
|
10
|
+
mod schema;
|
|
11
|
+
|
|
12
|
+
pub mod style;
|
|
13
|
+
|
|
14
|
+
pub use condition::{validate_condition, validate_item_condition};
|
|
11
15
|
pub use unique::DuplicateInfo;
|
|
12
16
|
|
|
13
17
|
use crate::syntax::YerbaValueType;
|
data/rust/src/document/set.rs
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
use super::*;
|
|
2
2
|
|
|
3
|
+
fn scalar_replacement_text(value: &str, kind: SyntaxKind) -> String {
|
|
4
|
+
if kind == SyntaxKind::PLAIN_SCALAR && !crate::syntax::is_inline_scalar_safe(value) {
|
|
5
|
+
return format_scalar_value(value, SyntaxKind::DOUBLE_QUOTED_SCALAR);
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
format_scalar_value(value, kind)
|
|
9
|
+
}
|
|
10
|
+
|
|
3
11
|
fn holds_collection(node: &SyntaxNode) -> bool {
|
|
4
12
|
node.descendants().any(|descendant| {
|
|
5
13
|
matches!(
|
|
@@ -84,7 +92,7 @@ impl Document {
|
|
|
84
92
|
|
|
85
93
|
let scalar_token = find_scalar_token(¤t_node).ok_or_else(|| YerbaError::SelectorNotFound(dot_path.to_string()))?;
|
|
86
94
|
|
|
87
|
-
let new_text =
|
|
95
|
+
let new_text = scalar_replacement_text(value, scalar_token.kind());
|
|
88
96
|
|
|
89
97
|
self.replace_token(&scalar_token, &new_text)
|
|
90
98
|
}
|
|
@@ -107,7 +115,7 @@ impl Document {
|
|
|
107
115
|
edits.push((span.range, replacement_text(&span, value)));
|
|
108
116
|
}
|
|
109
117
|
} else if let Some(scalar_token) = find_scalar_token(&node) {
|
|
110
|
-
edits.push((scalar_token.text_range(),
|
|
118
|
+
edits.push((scalar_token.text_range(), scalar_replacement_text(value, scalar_token.kind())));
|
|
111
119
|
}
|
|
112
120
|
}
|
|
113
121
|
|
data/rust/src/error.rs
CHANGED
|
@@ -10,6 +10,7 @@ pub enum YerbaError {
|
|
|
10
10
|
IndexOutOfBounds(usize, usize),
|
|
11
11
|
UnknownKeys(Vec<String>),
|
|
12
12
|
DuplicateValues(Vec<crate::DuplicateInfo>),
|
|
13
|
+
#[cfg(feature = "schema")]
|
|
13
14
|
SchemaValidation(Vec<crate::schema::ValidationError>),
|
|
14
15
|
DuplicateKey {
|
|
15
16
|
key: String,
|
|
@@ -78,6 +79,7 @@ impl std::fmt::Display for YerbaError {
|
|
|
78
79
|
write!(f, "index {} out of bounds (length {})", index, length)
|
|
79
80
|
}
|
|
80
81
|
|
|
82
|
+
#[cfg(feature = "schema")]
|
|
81
83
|
YerbaError::SchemaValidation(errors) => {
|
|
82
84
|
let details: Vec<String> = errors.iter().map(|error| error.to_string()).collect();
|
|
83
85
|
|
|
@@ -137,6 +139,7 @@ impl GitHubAnnotations for YerbaError {
|
|
|
137
139
|
})
|
|
138
140
|
.collect(),
|
|
139
141
|
|
|
142
|
+
#[cfg(feature = "schema")]
|
|
140
143
|
YerbaError::SchemaValidation(errors) => errors
|
|
141
144
|
.iter()
|
|
142
145
|
.map(|error| GitHubAnnotation {
|
data/rust/src/ffi.rs
CHANGED
|
@@ -746,6 +746,7 @@ pub unsafe extern "C" fn yerba_document_blank_lines(document: *mut Document, pat
|
|
|
746
746
|
}
|
|
747
747
|
|
|
748
748
|
/// Caller must free with yerba_string_free.
|
|
749
|
+
#[cfg(feature = "schema")]
|
|
749
750
|
#[no_mangle]
|
|
750
751
|
pub unsafe extern "C" fn yerba_document_validate_schema(document: *const Document, schema_json: *const c_char, selector: *const c_char) -> *mut c_char {
|
|
751
752
|
let document = &*document;
|
|
@@ -784,6 +785,7 @@ pub unsafe extern "C" fn yerba_document_validate_schema(document: *const Documen
|
|
|
784
785
|
}
|
|
785
786
|
|
|
786
787
|
/// Caller must free with yerba_string_free.
|
|
788
|
+
#[cfg(feature = "cli")]
|
|
787
789
|
#[no_mangle]
|
|
788
790
|
pub unsafe extern "C" fn yerba_yerbafile_find(directory: *const c_char) -> *mut c_char {
|
|
789
791
|
let start = if directory.is_null() {
|
|
@@ -803,6 +805,7 @@ pub unsafe extern "C" fn yerba_yerbafile_find(directory: *const c_char) -> *mut
|
|
|
803
805
|
}
|
|
804
806
|
}
|
|
805
807
|
|
|
808
|
+
#[cfg(feature = "cli")]
|
|
806
809
|
#[no_mangle]
|
|
807
810
|
pub unsafe extern "C" fn yerba_document_apply_yerbafile(document: *mut Document, file_path: *const c_char, yerbafile_path: *const c_char) -> YerbaResult {
|
|
808
811
|
let document = &mut *document;
|
|
@@ -924,6 +927,7 @@ fn located_nodes_to_list(nodes: &[crate::LocatedNode]) -> YerbaTypedList {
|
|
|
924
927
|
}
|
|
925
928
|
}
|
|
926
929
|
|
|
930
|
+
#[cfg(feature = "glob")]
|
|
927
931
|
#[no_mangle]
|
|
928
932
|
pub unsafe extern "C" fn yerba_glob_get(glob_pattern: *const c_char, path: *const c_char) -> YerbaTypedList {
|
|
929
933
|
let pattern = CStr::from_ptr(glob_pattern).to_str().unwrap_or("");
|
|
@@ -940,6 +944,7 @@ pub unsafe extern "C" fn yerba_document_get_all(document: *const Document, path:
|
|
|
940
944
|
located_nodes_to_list(&document.get_all_located(selector_string))
|
|
941
945
|
}
|
|
942
946
|
|
|
947
|
+
#[cfg(feature = "glob")]
|
|
943
948
|
#[no_mangle]
|
|
944
949
|
pub unsafe extern "C" fn yerba_glob_find(glob_pattern: *const c_char, path: *const c_char, condition: *const c_char, select: *const c_char) -> YerbaTypedList {
|
|
945
950
|
let pattern = CStr::from_ptr(glob_pattern).to_str().unwrap_or("");
|
data/rust/src/lib.rs
CHANGED
|
@@ -4,10 +4,12 @@ pub mod error;
|
|
|
4
4
|
pub mod ffi;
|
|
5
5
|
pub mod json;
|
|
6
6
|
mod quote_style;
|
|
7
|
+
#[cfg(feature = "schema")]
|
|
7
8
|
pub mod schema;
|
|
8
9
|
pub mod selector;
|
|
9
10
|
mod syntax;
|
|
10
11
|
mod yaml_writer;
|
|
12
|
+
#[cfg(feature = "cli")]
|
|
11
13
|
pub mod yerbafile;
|
|
12
14
|
|
|
13
15
|
pub use document::style::StyleEnforcement;
|
|
@@ -17,8 +19,12 @@ pub use document::{
|
|
|
17
19
|
pub use error::YerbaError;
|
|
18
20
|
pub use quote_style::{KeyStyle, QuoteStyle};
|
|
19
21
|
pub use selector::Selector;
|
|
20
|
-
pub use syntax::{
|
|
22
|
+
pub use syntax::{
|
|
23
|
+
detect_yaml_type, is_flow_collection, is_inline_scalar_safe, is_plain_safe, is_plain_safe_in_flow, is_quoted_scalar, is_raw_yaml_text, is_valid_inline_value,
|
|
24
|
+
needs_quoting, needs_quoting_in_flow, quote_if_needed, ScalarValue, YerbaValueType,
|
|
25
|
+
};
|
|
21
26
|
pub use yaml_writer::json_to_yaml_text;
|
|
27
|
+
#[cfg(feature = "cli")]
|
|
22
28
|
pub use yerbafile::Yerbafile;
|
|
23
29
|
|
|
24
30
|
pub fn version() -> &'static str {
|
|
@@ -33,6 +39,7 @@ pub fn parse_file(path: impl AsRef<std::path::Path>) -> Result<Document, YerbaEr
|
|
|
33
39
|
Document::parse_file(path)
|
|
34
40
|
}
|
|
35
41
|
|
|
42
|
+
#[cfg(feature = "glob")]
|
|
36
43
|
pub fn glob_get(pattern: &str, selector: &str) -> Vec<document::LocatedNode> {
|
|
37
44
|
use rayon::prelude::*;
|
|
38
45
|
|
|
@@ -55,6 +62,7 @@ pub fn glob_get(pattern: &str, selector: &str) -> Vec<document::LocatedNode> {
|
|
|
55
62
|
.collect()
|
|
56
63
|
}
|
|
57
64
|
|
|
65
|
+
#[cfg(feature = "glob")]
|
|
58
66
|
pub fn glob_find(pattern: &str, selector: &str, condition: Option<&str>, select: Option<&str>) -> Result<Vec<serde_json::Value>, YerbaError> {
|
|
59
67
|
use rayon::prelude::*;
|
|
60
68
|
|