yerba 0.3.0-aarch64-linux-gnu → 0.4.1-aarch64-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/README.md +93 -8
- data/exe/aarch64-linux-gnu/yerba +0 -0
- data/ext/yerba/extconf.rb +22 -3
- data/ext/yerba/include/yerba.h +32 -9
- data/ext/yerba/yerba.c +133 -25
- 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/collection.rb +35 -0
- data/lib/yerba/document.rb +45 -3
- data/lib/yerba/query_result.rb +50 -0
- data/lib/yerba/sequence.rb +187 -1
- data/lib/yerba/version.rb +1 -1
- data/lib/yerba/yerbafile.rb +45 -0
- data/lib/yerba.rb +2 -0
- data/rust/Cargo.lock +3 -1
- data/rust/Cargo.toml +3 -2
- data/rust/cbindgen.toml +1 -0
- data/rust/rustfmt.toml +1 -1
- data/rust/src/commands/apply.rs +11 -2
- data/rust/src/commands/blank_lines.rs +1 -4
- data/rust/src/commands/check.rs +11 -2
- data/rust/src/commands/directives.rs +61 -0
- data/rust/src/commands/get.rs +5 -22
- data/rust/src/commands/mod.rs +16 -18
- data/rust/src/commands/quote_style.rs +12 -6
- data/rust/src/commands/selectors.rs +3 -19
- data/rust/src/commands/sort.rs +22 -157
- data/rust/src/didyoumean.rs +2 -4
- data/rust/src/document/condition.rs +139 -0
- data/rust/src/document/delete.rs +91 -0
- data/rust/src/document/get.rs +262 -0
- data/rust/src/document/insert.rs +384 -0
- data/rust/src/document/mod.rs +784 -0
- data/rust/src/document/set.rs +100 -0
- data/rust/src/document/sort.rs +639 -0
- data/rust/src/document/style.rs +473 -0
- data/rust/src/error.rs +24 -6
- data/rust/src/ffi.rs +272 -518
- data/rust/src/json.rs +1 -7
- data/rust/src/lib.rs +88 -2
- data/rust/src/main.rs +2 -0
- data/rust/src/quote_style.rs +83 -7
- data/rust/src/selector.rs +2 -7
- data/rust/src/syntax.rs +41 -21
- data/rust/src/yerbafile.rs +86 -19
- metadata +13 -3
- data/rust/src/document.rs +0 -2304
data/rust/src/commands/mod.rs
CHANGED
|
@@ -2,6 +2,7 @@ pub mod apply;
|
|
|
2
2
|
pub mod blank_lines;
|
|
3
3
|
pub mod check;
|
|
4
4
|
pub mod delete;
|
|
5
|
+
pub mod directives;
|
|
5
6
|
pub mod get;
|
|
6
7
|
pub mod init;
|
|
7
8
|
pub mod insert;
|
|
@@ -126,10 +127,7 @@ pub(crate) fn colorize_help(input: &str) -> String {
|
|
|
126
127
|
}
|
|
127
128
|
|
|
128
129
|
if columns.len() == 3 {
|
|
129
|
-
output.push_str(&format!(
|
|
130
|
-
" \x1b[36m{:<20}{RESET} {:<21} {DIM}{}{RESET}\n",
|
|
131
|
-
columns[0], columns[1], columns[2]
|
|
132
|
-
));
|
|
130
|
+
output.push_str(&format!(" \x1b[36m{:<20}{RESET} {:<21} {DIM}{}{RESET}\n", columns[0], columns[1], columns[2]));
|
|
133
131
|
} else if columns.len() == 2 {
|
|
134
132
|
output.push_str(&format!(" \x1b[36m{:<20}{RESET} {}\n", columns[0], columns[1]));
|
|
135
133
|
} else {
|
|
@@ -154,13 +152,12 @@ pub enum Command {
|
|
|
154
152
|
Sort(sort::Args),
|
|
155
153
|
QuoteStyle(quote_style::Args),
|
|
156
154
|
BlankLines(blank_lines::Args),
|
|
155
|
+
Directives(directives::Args),
|
|
157
156
|
Selectors(selectors::Args),
|
|
158
157
|
#[command(about = "Create a new Yerbafile in the current directory")]
|
|
159
158
|
Init,
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
#[command(about = "Check if all files match Yerbafile rules (exits 1 if not)")]
|
|
163
|
-
Check,
|
|
159
|
+
Apply(apply::Args),
|
|
160
|
+
Check(check::Args),
|
|
164
161
|
#[command(about = "Print the yerba version")]
|
|
165
162
|
Version,
|
|
166
163
|
#[command(about = "\u{1f9c9}")]
|
|
@@ -182,17 +179,18 @@ impl Command {
|
|
|
182
179
|
Command::Sort(args) => args.run(),
|
|
183
180
|
Command::QuoteStyle(args) => args.run(),
|
|
184
181
|
Command::BlankLines(args) => args.run(),
|
|
182
|
+
Command::Directives(args) => args.run(),
|
|
185
183
|
Command::Selectors(args) => args.run(),
|
|
186
184
|
Command::Init => init::run(),
|
|
187
|
-
Command::Apply =>
|
|
188
|
-
Command::Check =>
|
|
185
|
+
Command::Apply(args) => args.run(),
|
|
186
|
+
Command::Check(args) => args.run(),
|
|
189
187
|
Command::Version => version::run(),
|
|
190
188
|
Command::Mate => mate::run(),
|
|
191
189
|
}
|
|
192
190
|
}
|
|
193
191
|
}
|
|
194
192
|
|
|
195
|
-
pub(crate) fn run_yerbafile(write: bool) {
|
|
193
|
+
pub(crate) fn run_yerbafile(write: bool, files: Vec<String>) {
|
|
196
194
|
use color::*;
|
|
197
195
|
|
|
198
196
|
let yerbafile_path = yerba::Yerbafile::find().unwrap_or_else(|| {
|
|
@@ -207,7 +205,12 @@ pub(crate) fn run_yerbafile(write: bool) {
|
|
|
207
205
|
|
|
208
206
|
eprintln!("🧉 {BOLD}Using{RESET} {}", yerbafile_path.display());
|
|
209
207
|
|
|
210
|
-
let results =
|
|
208
|
+
let results = if files.is_empty() {
|
|
209
|
+
yerbafile.apply(write)
|
|
210
|
+
} else {
|
|
211
|
+
files.iter().flat_map(|file| yerbafile.apply_file(file, write)).collect()
|
|
212
|
+
};
|
|
213
|
+
|
|
211
214
|
let mut has_changes = false;
|
|
212
215
|
let mut has_errors = false;
|
|
213
216
|
|
|
@@ -334,12 +337,7 @@ pub(crate) fn run_op(file: &str, document: &yerba::Document, result: Result<(),
|
|
|
334
337
|
run_op_with_hint(file, document, result, None);
|
|
335
338
|
}
|
|
336
339
|
|
|
337
|
-
pub(crate) fn run_op_with_hint(
|
|
338
|
-
file: &str,
|
|
339
|
-
document: &yerba::Document,
|
|
340
|
-
result: Result<(), yerba::YerbaError>,
|
|
341
|
-
hint: Option<&str>,
|
|
342
|
-
) {
|
|
340
|
+
pub(crate) fn run_op_with_hint(file: &str, document: &yerba::Document, result: Result<(), yerba::YerbaError>, hint: Option<&str>) {
|
|
343
341
|
use color::*;
|
|
344
342
|
|
|
345
343
|
if let Err(error) = result {
|
|
@@ -11,7 +11,7 @@ static EXAMPLES: LazyLock<String> = LazyLock::new(|| {
|
|
|
11
11
|
yerba quote-style config.yml --keys plain
|
|
12
12
|
yerba quote-style config.yml --keys plain --values double
|
|
13
13
|
yerba quote-style config.yml "[].speakers" --values plain
|
|
14
|
-
yerba quote-style "data/**/*.yml" --keys plain --values double
|
|
14
|
+
yerba quote-style "data/**/*.yml" --keys plain --values double
|
|
15
15
|
"#})
|
|
16
16
|
});
|
|
17
17
|
|
|
@@ -25,12 +25,12 @@ pub struct Args {
|
|
|
25
25
|
file: String,
|
|
26
26
|
/// Selector to scope the operation (optional — omit for whole file)
|
|
27
27
|
selector: Option<String>,
|
|
28
|
-
/// Quote style for
|
|
28
|
+
/// Quote style for keys
|
|
29
29
|
#[arg(long)]
|
|
30
|
-
|
|
31
|
-
/// Quote style for
|
|
30
|
+
keys: Option<yerba::KeyStyle>,
|
|
31
|
+
/// Quote style for values
|
|
32
32
|
#[arg(long)]
|
|
33
|
-
|
|
33
|
+
values: Option<yerba::QuoteStyle>,
|
|
34
34
|
#[arg(long)]
|
|
35
35
|
dry_run: bool,
|
|
36
36
|
}
|
|
@@ -53,7 +53,13 @@ impl Args {
|
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
if let Some(style) = &self.values {
|
|
56
|
-
let
|
|
56
|
+
if let Ok(warnings) = document.enforce_quotes_at(style, selector) {
|
|
57
|
+
for warning in warnings {
|
|
58
|
+
use super::color::*;
|
|
59
|
+
|
|
60
|
+
eprintln!("{YELLOW}Warning:{RESET} {} — {}", resolved_file, warning);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
57
63
|
}
|
|
58
64
|
|
|
59
65
|
output(&resolved_file, &document, self.dry_run);
|
|
@@ -81,11 +81,7 @@ impl Args {
|
|
|
81
81
|
|
|
82
82
|
for resolved_file in resolve_files(&self.file) {
|
|
83
83
|
let document = parse_file(&resolved_file);
|
|
84
|
-
let prefix = if selector.is_empty() {
|
|
85
|
-
String::new()
|
|
86
|
-
} else {
|
|
87
|
-
selector.to_string()
|
|
88
|
-
};
|
|
84
|
+
let prefix = if selector.is_empty() { String::new() } else { selector.to_string() };
|
|
89
85
|
|
|
90
86
|
let values = if selector.is_empty() {
|
|
91
87
|
document.get_value("").into_iter().collect::<Vec<_>>()
|
|
@@ -112,14 +108,7 @@ impl Args {
|
|
|
112
108
|
if let Some(label) = info.count_label() {
|
|
113
109
|
let padding = max_selector_len - selector.len() + 2;
|
|
114
110
|
|
|
115
|
-
println!(
|
|
116
|
-
"{}{}{}{}{}",
|
|
117
|
-
selector,
|
|
118
|
-
" ".repeat(padding),
|
|
119
|
-
color::DIM,
|
|
120
|
-
label,
|
|
121
|
-
color::RESET
|
|
122
|
-
);
|
|
111
|
+
println!("{}{}{}{}{}", selector, " ".repeat(padding), color::DIM, label, color::RESET);
|
|
123
112
|
} else {
|
|
124
113
|
println!("{}", selector);
|
|
125
114
|
}
|
|
@@ -127,12 +116,7 @@ impl Args {
|
|
|
127
116
|
}
|
|
128
117
|
}
|
|
129
118
|
|
|
130
|
-
fn collect_selectors(
|
|
131
|
-
value: &serde_yaml::Value,
|
|
132
|
-
prefix: &str,
|
|
133
|
-
selectors: &mut BTreeMap<String, SelectorInfo>,
|
|
134
|
-
counter: &mut usize,
|
|
135
|
-
) {
|
|
119
|
+
fn collect_selectors(value: &serde_yaml::Value, prefix: &str, selectors: &mut BTreeMap<String, SelectorInfo>, counter: &mut usize) {
|
|
136
120
|
match value {
|
|
137
121
|
serde_yaml::Value::Mapping(map) => {
|
|
138
122
|
for (key, child) in map {
|
data/rust/src/commands/sort.rs
CHANGED
|
@@ -89,13 +89,7 @@ impl Args {
|
|
|
89
89
|
|
|
90
90
|
let by = &self.by[0];
|
|
91
91
|
let selector = self.selector.as_deref().unwrap_or("");
|
|
92
|
-
|
|
93
|
-
let items_selector = if selector.is_empty() {
|
|
94
|
-
"[]".to_string()
|
|
95
|
-
} else {
|
|
96
|
-
format!("{}[]", selector)
|
|
97
|
-
};
|
|
98
|
-
|
|
92
|
+
let items_selector = if selector.is_empty() { "[]".to_string() } else { format!("{}[]", selector) };
|
|
99
93
|
let document = parse_file(&self.file);
|
|
100
94
|
let (labels, context_values, selector_display) = self.resolve_labels(&document, by, selector, &items_selector);
|
|
101
95
|
|
|
@@ -111,6 +105,7 @@ impl Args {
|
|
|
111
105
|
|
|
112
106
|
for (index, label) in labels.iter().enumerate() {
|
|
113
107
|
let context = context_values.get(index).map(|c| c.as_slice()).unwrap_or(&[]);
|
|
108
|
+
|
|
114
109
|
eprintln!(" {}", self.format_label_line(index, label, context));
|
|
115
110
|
}
|
|
116
111
|
|
|
@@ -119,26 +114,14 @@ impl Args {
|
|
|
119
114
|
eprintln!("{context_hint}");
|
|
120
115
|
eprintln!();
|
|
121
116
|
eprintln!(" {BOLD}To sort alphabetically:{RESET}");
|
|
122
|
-
eprintln!(
|
|
123
|
-
|
|
124
|
-
self.file
|
|
125
|
-
);
|
|
126
|
-
eprintln!(
|
|
127
|
-
" yerba sort \"{}\"{selector_display} --by \"{by}\" --order desc",
|
|
128
|
-
self.file
|
|
129
|
-
);
|
|
117
|
+
eprintln!(" yerba sort \"{}\"{selector_display} --by \"{by}\" --order asc", self.file);
|
|
118
|
+
eprintln!(" yerba sort \"{}\"{selector_display} --by \"{by}\" --order desc", self.file);
|
|
130
119
|
eprintln!();
|
|
131
120
|
eprintln!(" {BOLD}To reorder explicitly:{RESET}");
|
|
132
|
-
eprintln!(
|
|
133
|
-
" yerba sort \"{}\"{selector_display} --by \"{by}\" --order \"{csv}\"",
|
|
134
|
-
self.file
|
|
135
|
-
);
|
|
121
|
+
eprintln!(" yerba sort \"{}\"{selector_display} --by \"{by}\" --order \"{csv}\"", self.file);
|
|
136
122
|
eprintln!();
|
|
137
123
|
eprintln!(" {BOLD}To move individual items:{RESET}");
|
|
138
|
-
eprintln!(
|
|
139
|
-
" yerba move \"{}\"{selector_display} <item> --before/--after <target>",
|
|
140
|
-
self.file
|
|
141
|
-
);
|
|
124
|
+
eprintln!(" yerba move \"{}\"{selector_display} <item> --before/--after <target>", self.file);
|
|
142
125
|
|
|
143
126
|
process::exit(1);
|
|
144
127
|
}
|
|
@@ -174,11 +157,7 @@ impl Args {
|
|
|
174
157
|
let mut document = parse_file(&resolved_file);
|
|
175
158
|
|
|
176
159
|
if sort_fields.is_empty() {
|
|
177
|
-
let first_item_selector = if selector.is_empty() {
|
|
178
|
-
"[0]".to_string()
|
|
179
|
-
} else {
|
|
180
|
-
format!("{}[0]", selector)
|
|
181
|
-
};
|
|
160
|
+
let first_item_selector = if selector.is_empty() { "[0]".to_string() } else { format!("{}[0]", selector) };
|
|
182
161
|
|
|
183
162
|
match document.get_value(&first_item_selector) {
|
|
184
163
|
Some(first) if first.is_mapping() => {
|
|
@@ -190,11 +169,7 @@ impl Args {
|
|
|
190
169
|
let fields: Vec<&String> = selectors
|
|
191
170
|
.iter()
|
|
192
171
|
.filter(|s| {
|
|
193
|
-
let check = if prefix.is_empty() {
|
|
194
|
-
format!("{}[].", selector)
|
|
195
|
-
} else {
|
|
196
|
-
prefix.to_string()
|
|
197
|
-
};
|
|
172
|
+
let check = if prefix.is_empty() { format!("{}[].", selector) } else { prefix.to_string() };
|
|
198
173
|
s.starts_with(&check) && !s[check.len()..].contains('.') && !s[check.len()..].contains('[')
|
|
199
174
|
})
|
|
200
175
|
.collect();
|
|
@@ -212,10 +187,7 @@ impl Args {
|
|
|
212
187
|
}
|
|
213
188
|
|
|
214
189
|
None if selector.is_empty() => {
|
|
215
|
-
eprintln!(
|
|
216
|
-
"{RED}Error:{RESET} no sequence found at root level in {}",
|
|
217
|
-
resolved_file
|
|
218
|
-
);
|
|
190
|
+
eprintln!("{RED}Error:{RESET} no sequence found at root level in {}", resolved_file);
|
|
219
191
|
eprintln!();
|
|
220
192
|
eprintln!(" {DIM}Specify a selector for the sequence to sort:{RESET}");
|
|
221
193
|
eprintln!(" yerba sort \"{}\" \"<selector>\"", self.file);
|
|
@@ -255,11 +227,7 @@ impl Args {
|
|
|
255
227
|
let order = &self.order[0];
|
|
256
228
|
let selector = self.selector.as_deref().unwrap_or("");
|
|
257
229
|
|
|
258
|
-
let items_selector = if selector.is_empty() {
|
|
259
|
-
"[]".to_string()
|
|
260
|
-
} else {
|
|
261
|
-
format!("{}[]", selector)
|
|
262
|
-
};
|
|
230
|
+
let items_selector = if selector.is_empty() { "[]".to_string() } else { format!("{}[]", selector) };
|
|
263
231
|
|
|
264
232
|
let mut document = parse_file(&self.file);
|
|
265
233
|
let mut seen = std::collections::HashSet::new();
|
|
@@ -286,11 +254,7 @@ impl Args {
|
|
|
286
254
|
let values_with_commas: Vec<&String> = labels.iter().filter(|l| l.contains(',')).collect();
|
|
287
255
|
|
|
288
256
|
if !values_with_commas.is_empty() {
|
|
289
|
-
let selector_display = if selector.is_empty() {
|
|
290
|
-
String::new()
|
|
291
|
-
} else {
|
|
292
|
-
format!(" \"{selector}\"")
|
|
293
|
-
};
|
|
257
|
+
let selector_display = if selector.is_empty() { String::new() } else { format!(" \"{selector}\"") };
|
|
294
258
|
|
|
295
259
|
eprintln!("{RED}Error:{RESET} some values for {by} contain commas, which conflicts with --order parsing");
|
|
296
260
|
eprintln!();
|
|
@@ -302,108 +266,24 @@ impl Args {
|
|
|
302
266
|
|
|
303
267
|
eprintln!();
|
|
304
268
|
eprintln!(" {BOLD}Use yerba move to reorder individual items instead:{RESET}");
|
|
305
|
-
eprintln!(
|
|
306
|
-
" yerba move \"{}\"{selector_display} <item> --before/--after <target>",
|
|
307
|
-
self.file
|
|
308
|
-
);
|
|
269
|
+
eprintln!(" yerba move \"{}\"{selector_display} <item> --before/--after <target>", self.file);
|
|
309
270
|
|
|
310
271
|
process::exit(1);
|
|
311
272
|
}
|
|
312
273
|
|
|
313
274
|
let desired_order: Vec<&str> = order.split(',').map(|s| s.trim()).collect();
|
|
314
|
-
|
|
315
|
-
let mut used = vec![false; labels.len()];
|
|
316
|
-
let mut moves: Vec<usize> = Vec::new();
|
|
317
|
-
|
|
318
|
-
for desired in &desired_order {
|
|
319
|
-
let found = labels
|
|
320
|
-
.iter()
|
|
321
|
-
.enumerate()
|
|
322
|
-
.find(|(index, label)| label.as_str() == *desired && !used[*index]);
|
|
323
|
-
|
|
324
|
-
if let Some((index, _)) = found {
|
|
325
|
-
moves.push(index);
|
|
326
|
-
used[index] = true;
|
|
327
|
-
} else {
|
|
328
|
-
eprintln!("{RED}Error:{RESET} no item found with {by} == \"{desired}\"");
|
|
329
|
-
eprintln!();
|
|
330
|
-
eprintln!(" {BOLD}Available values:{RESET}");
|
|
331
|
-
|
|
332
|
-
for (index, label) in labels.iter().enumerate() {
|
|
333
|
-
eprintln!(" {DIM}{index}:{RESET} {label}");
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
process::exit(1);
|
|
337
|
-
}
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
let missing: Vec<&String> = labels
|
|
341
|
-
.iter()
|
|
342
|
-
.enumerate()
|
|
343
|
-
.filter(|(index, _)| !used[*index])
|
|
344
|
-
.map(|(_, label)| label)
|
|
345
|
-
.collect();
|
|
346
|
-
|
|
347
|
-
if !missing.is_empty() {
|
|
348
|
-
eprintln!(
|
|
349
|
-
"{RED}Error:{RESET} --order must specify all {} items, but {} are missing",
|
|
350
|
-
labels.len(),
|
|
351
|
-
missing.len()
|
|
352
|
-
);
|
|
353
|
-
eprintln!();
|
|
354
|
-
eprintln!(" {BOLD}Missing values:{RESET}");
|
|
355
|
-
|
|
356
|
-
for label in &missing {
|
|
357
|
-
eprintln!(" {label}");
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
eprintln!();
|
|
361
|
-
eprintln!(" {BOLD}All values (by {by}):{RESET}");
|
|
362
|
-
|
|
363
|
-
for label in &labels {
|
|
364
|
-
eprintln!(" {label}");
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
eprintln!();
|
|
368
|
-
eprintln!(" {BOLD}To move individual items, use:{RESET}");
|
|
369
|
-
eprintln!(" yerba move <file> <selector> <item> --before/--after <target>");
|
|
370
|
-
|
|
371
|
-
process::exit(1);
|
|
372
|
-
}
|
|
373
|
-
|
|
374
275
|
let container = if selector.is_empty() { "" } else { selector };
|
|
375
276
|
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
if let Err(error) = result {
|
|
383
|
-
eprintln!("{RED}Error:{RESET} {}", error);
|
|
384
|
-
process::exit(1);
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
for item in moves.iter_mut().skip(target + 1) {
|
|
388
|
-
if *item >= target && *item < source {
|
|
389
|
-
*item += 1;
|
|
390
|
-
} else if *item == source {
|
|
391
|
-
*item = target;
|
|
392
|
-
}
|
|
393
|
-
}
|
|
277
|
+
match document.reorder_items(container, by, &desired_order) {
|
|
278
|
+
Ok(()) => output(&self.file, &document, self.dry_run),
|
|
279
|
+
Err(error) => {
|
|
280
|
+
eprintln!("{RED}Error:{RESET} {}", error);
|
|
281
|
+
process::exit(1);
|
|
394
282
|
}
|
|
395
283
|
}
|
|
396
|
-
|
|
397
|
-
output(&self.file, &document, self.dry_run);
|
|
398
284
|
}
|
|
399
285
|
|
|
400
|
-
fn resolve_labels(
|
|
401
|
-
&self,
|
|
402
|
-
document: &yerba::Document,
|
|
403
|
-
by: &str,
|
|
404
|
-
selector: &str,
|
|
405
|
-
items_selector: &str,
|
|
406
|
-
) -> (Vec<String>, Vec<Vec<String>>, String) {
|
|
286
|
+
fn resolve_labels(&self, document: &yerba::Document, by: &str, selector: &str, items_selector: &str) -> (Vec<String>, Vec<Vec<String>>, String) {
|
|
407
287
|
use super::color::*;
|
|
408
288
|
|
|
409
289
|
let items = document.get_values(items_selector);
|
|
@@ -413,10 +293,7 @@ impl Args {
|
|
|
413
293
|
eprintln!("{RED}Error:{RESET} no sequence found at root level");
|
|
414
294
|
eprintln!();
|
|
415
295
|
eprintln!(" {DIM}If the file is a map, specify which sequence to sort:{RESET}");
|
|
416
|
-
eprintln!(
|
|
417
|
-
" yerba sort \"{}\" \"<selector>\" --by \"{by}\" --order asc",
|
|
418
|
-
self.file
|
|
419
|
-
);
|
|
296
|
+
eprintln!(" yerba sort \"{}\" \"<selector>\" --by \"{by}\" --order asc", self.file);
|
|
420
297
|
eprintln!();
|
|
421
298
|
|
|
422
299
|
super::show_similar_selectors(&self.file, document, "[]");
|
|
@@ -458,10 +335,7 @@ impl Args {
|
|
|
458
335
|
} else {
|
|
459
336
|
let field = by.strip_prefix('.').unwrap_or(by);
|
|
460
337
|
|
|
461
|
-
yerba::json::resolve_select_field(item, field)
|
|
462
|
-
.as_str()
|
|
463
|
-
.unwrap_or("")
|
|
464
|
-
.to_string()
|
|
338
|
+
yerba::json::resolve_select_field(item, field).as_str().unwrap_or("").to_string()
|
|
465
339
|
}
|
|
466
340
|
})
|
|
467
341
|
.collect();
|
|
@@ -487,11 +361,7 @@ impl Args {
|
|
|
487
361
|
})
|
|
488
362
|
.collect();
|
|
489
363
|
|
|
490
|
-
let selector_display = if selector.is_empty() {
|
|
491
|
-
String::new()
|
|
492
|
-
} else {
|
|
493
|
-
format!(" \"{selector}\"")
|
|
494
|
-
};
|
|
364
|
+
let selector_display = if selector.is_empty() { String::new() } else { format!(" \"{selector}\"") };
|
|
495
365
|
|
|
496
366
|
(labels, context_values, selector_display)
|
|
497
367
|
}
|
|
@@ -502,12 +372,7 @@ impl Args {
|
|
|
502
372
|
if context.is_empty() {
|
|
503
373
|
format!("{DIM}[{index}]{RESET} {label}")
|
|
504
374
|
} else {
|
|
505
|
-
let context = context
|
|
506
|
-
.iter()
|
|
507
|
-
.filter(|c| !c.is_empty())
|
|
508
|
-
.cloned()
|
|
509
|
-
.collect::<Vec<_>>()
|
|
510
|
-
.join(", ");
|
|
375
|
+
let context = context.iter().filter(|c| !c.is_empty()).cloned().collect::<Vec<_>>().join(", ");
|
|
511
376
|
|
|
512
377
|
if context.is_empty() {
|
|
513
378
|
format!("{DIM}[{index}]{RESET} {label}")
|
data/rust/src/didyoumean.rs
CHANGED
|
@@ -10,8 +10,7 @@ pub fn didyoumean(input: &str, list: &[String]) -> Option<String> {
|
|
|
10
10
|
.map(|item| (item.clone(), levenshtein(&input_lower, &item.to_lowercase())))
|
|
11
11
|
.collect();
|
|
12
12
|
|
|
13
|
-
scored
|
|
14
|
-
.sort_by(|(a_item, a_distance), (b_item, b_distance)| a_distance.cmp(b_distance).then_with(|| a_item.cmp(b_item)));
|
|
13
|
+
scored.sort_by(|(a_item, a_distance), (b_item, b_distance)| a_distance.cmp(b_distance).then_with(|| a_item.cmp(b_item)));
|
|
15
14
|
|
|
16
15
|
scored.into_iter().next().map(|(item, _)| item)
|
|
17
16
|
}
|
|
@@ -29,8 +28,7 @@ pub fn didyoumean_ranked(input: &str, list: &[String], threshold: usize) -> Vec<
|
|
|
29
28
|
.filter(|(_, distance)| *distance <= threshold)
|
|
30
29
|
.collect();
|
|
31
30
|
|
|
32
|
-
scored
|
|
33
|
-
.sort_by(|(a_item, a_distance), (b_item, b_distance)| a_distance.cmp(b_distance).then_with(|| a_item.cmp(b_item)));
|
|
31
|
+
scored.sort_by(|(a_item, a_distance), (b_item, b_distance)| a_distance.cmp(b_distance).then_with(|| a_item.cmp(b_item)));
|
|
34
32
|
|
|
35
33
|
scored.into_iter().map(|(item, _)| item).collect()
|
|
36
34
|
}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
use super::*;
|
|
2
|
+
|
|
3
|
+
impl Document {
|
|
4
|
+
pub fn filter(&self, dot_path: &str, condition: &str) -> Vec<serde_yaml::Value> {
|
|
5
|
+
self
|
|
6
|
+
.navigate_all(dot_path)
|
|
7
|
+
.iter()
|
|
8
|
+
.filter(|node| self.evaluate_condition_on_node(node, condition))
|
|
9
|
+
.map(node_to_yaml_value)
|
|
10
|
+
.collect()
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
pub(super) fn evaluate_condition_on_node(&self, node: &SyntaxNode, condition: &str) -> bool {
|
|
14
|
+
let condition = condition.trim();
|
|
15
|
+
|
|
16
|
+
let (left, operator, right) = match parse_condition(condition) {
|
|
17
|
+
Some(parts) => parts,
|
|
18
|
+
None => return false,
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
let path = crate::selector::Selector::parse(&left);
|
|
22
|
+
|
|
23
|
+
if !path.is_relative() {
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
let target_nodes = navigate_from_node(node, &path.to_selector_string());
|
|
28
|
+
let values: Vec<String> = target_nodes.iter().filter_map(extract_scalar_text).collect();
|
|
29
|
+
|
|
30
|
+
match operator {
|
|
31
|
+
"==" => values.iter().any(|value| value == &right),
|
|
32
|
+
"!=" => values.iter().all(|value| value != &right),
|
|
33
|
+
"contains" => {
|
|
34
|
+
if values.iter().any(|value| value == &right || value.contains(&right)) {
|
|
35
|
+
return true;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
for node in &target_nodes {
|
|
39
|
+
if let Some(sequence) = node.descendants().find_map(BlockSeq::cast) {
|
|
40
|
+
for entry in sequence.entries() {
|
|
41
|
+
if let Some(text) = entry.flow().and_then(|flow| extract_scalar_text(flow.syntax())) {
|
|
42
|
+
if text == right {
|
|
43
|
+
return true;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
false
|
|
51
|
+
}
|
|
52
|
+
"not_contains" => {
|
|
53
|
+
for node in &target_nodes {
|
|
54
|
+
if let Some(sequence) = node.descendants().find_map(BlockSeq::cast) {
|
|
55
|
+
for entry in sequence.entries() {
|
|
56
|
+
if let Some(text) = entry.flow().and_then(|flow| extract_scalar_text(flow.syntax())) {
|
|
57
|
+
if text == right {
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
!values.iter().any(|value| value == &right || value.contains(&right))
|
|
66
|
+
}
|
|
67
|
+
_ => false,
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
pub fn evaluate_condition(&self, parent_path: &str, condition: &str) -> bool {
|
|
72
|
+
let condition = condition.trim();
|
|
73
|
+
|
|
74
|
+
let (left, operator, right) = match parse_condition(condition) {
|
|
75
|
+
Some(parts) => parts,
|
|
76
|
+
None => return false,
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
let path = crate::selector::Selector::parse(&left);
|
|
80
|
+
|
|
81
|
+
let full_path = if path.is_relative() {
|
|
82
|
+
let path_string = path.to_selector_string();
|
|
83
|
+
|
|
84
|
+
if parent_path.is_empty() {
|
|
85
|
+
path_string
|
|
86
|
+
} else {
|
|
87
|
+
format!("{}.{}", parent_path, path_string)
|
|
88
|
+
}
|
|
89
|
+
} else {
|
|
90
|
+
path.to_selector_string()
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
let has_brackets = crate::selector::Selector::parse(&full_path).has_brackets();
|
|
94
|
+
|
|
95
|
+
match operator {
|
|
96
|
+
"==" => {
|
|
97
|
+
if has_brackets {
|
|
98
|
+
self.get_all(&full_path).iter().any(|value| value == &right)
|
|
99
|
+
} else {
|
|
100
|
+
self.get(&full_path).unwrap_or_default() == right
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
"!=" => {
|
|
104
|
+
if has_brackets {
|
|
105
|
+
self.get_all(&full_path).iter().all(|value| value != &right)
|
|
106
|
+
} else {
|
|
107
|
+
self.get(&full_path).unwrap_or_default() != right
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
"contains" => {
|
|
111
|
+
if has_brackets {
|
|
112
|
+
self.get_all(&full_path).iter().any(|value| value == &right || value.contains(&right))
|
|
113
|
+
} else {
|
|
114
|
+
let items = self.get_sequence_values(&full_path);
|
|
115
|
+
|
|
116
|
+
if !items.is_empty() {
|
|
117
|
+
items.iter().any(|item| item == &right)
|
|
118
|
+
} else {
|
|
119
|
+
self.get(&full_path).map(|value| value.contains(&right)).unwrap_or(false)
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
"not_contains" => {
|
|
124
|
+
if has_brackets {
|
|
125
|
+
self.get_all(&full_path).iter().all(|value| value != &right && !value.contains(&right))
|
|
126
|
+
} else {
|
|
127
|
+
let items = self.get_sequence_values(&full_path);
|
|
128
|
+
|
|
129
|
+
if !items.is_empty() {
|
|
130
|
+
!items.iter().any(|item| item == &right)
|
|
131
|
+
} else {
|
|
132
|
+
self.get(&full_path).map(|value| !value.contains(&right)).unwrap_or(true)
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
_ => false,
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|