yerba 0.2.2 → 0.3.0
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 +78 -5
- data/ext/yerba/extconf.rb +17 -9
- data/ext/yerba/include/yerba.h +2 -1
- data/ext/yerba/yerba.c +8 -1
- data/lib/yerba/version.rb +1 -1
- data/lib/yerba.rb +7 -2
- data/rust/Cargo.lock +1 -1
- data/rust/Cargo.toml +2 -2
- data/rust/src/commands/delete.rs +9 -4
- data/rust/src/commands/get.rs +56 -13
- data/rust/src/commands/insert.rs +8 -4
- data/rust/src/commands/mod.rs +72 -5
- data/rust/src/commands/move_item.rs +2 -1
- data/rust/src/commands/move_key.rs +2 -1
- data/rust/src/commands/remove.rs +8 -4
- data/rust/src/commands/rename.rs +8 -4
- data/rust/src/commands/selectors.rs +174 -0
- data/rust/src/commands/set.rs +33 -16
- data/rust/src/commands/sort.rs +477 -10
- data/rust/src/didyoumean.rs +55 -0
- data/rust/src/document.rs +118 -51
- data/rust/src/error.rs +12 -2
- data/rust/src/ffi.rs +8 -3
- data/rust/src/lib.rs +2 -1
- data/rust/src/main.rs +2 -0
- metadata +3 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 477663fa314d01cbe3c98e8a5a8bdc08c0e26d287bff36d23ae1eea519e81609
|
|
4
|
+
data.tar.gz: 3a9ce13df82bbebd5cbbfc7590d0d334e841c4314bb059f31047ba707bd9bb1e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: be2932fad743bdf806070d5898f73533e8225994dc8d6581b0f47c5ee79519a8cfc83fd4f88cae4607fe5e36ee85f2cc165f288bbeca0184a9756fe18e7b3b27
|
|
7
|
+
data.tar.gz: 6251a8c8d261c458781e48a2adc8364172bd999c4ecbcc8e38ba625cf9a2434285efabd96dbfb64eabf78791f5a6a187cb76114bfc37facce78d48aa782696cf
|
data/README.md
CHANGED
|
@@ -47,7 +47,7 @@ Use `yerba` as a library in your Rust project:
|
|
|
47
47
|
|
|
48
48
|
```toml
|
|
49
49
|
[dependencies]
|
|
50
|
-
yerba = "0.
|
|
50
|
+
yerba = "0.3"
|
|
51
51
|
```
|
|
52
52
|
|
|
53
53
|
```rust
|
|
@@ -162,6 +162,12 @@ Use `--condition` to only apply the change when a sibling field matches:
|
|
|
162
162
|
yerba set config.yml "database.host" "0.0.0.0" --condition ".port == 5432"
|
|
163
163
|
```
|
|
164
164
|
|
|
165
|
+
Use `--all` to update all nodes matching a wildcard selector:
|
|
166
|
+
|
|
167
|
+
```bash
|
|
168
|
+
yerba set videos.yml "[].description" "" --all
|
|
169
|
+
```
|
|
170
|
+
|
|
165
171
|
### `insert`
|
|
166
172
|
|
|
167
173
|
Insert a new key into a map or a new item into a sequence. By default, new items are appended at the end.
|
|
@@ -249,15 +255,25 @@ yerba move-key config.yml "database.pool" --after "database.name"
|
|
|
249
255
|
|
|
250
256
|
### `sort`
|
|
251
257
|
|
|
252
|
-
Sort items in a sequence. For simple scalar sequences, no options are needed. For sequences of maps, use `--by` to specify sort
|
|
258
|
+
Sort items in a sequence. For simple scalar sequences, no options are needed. For sequences of maps, use `--by` to specify the sort field. Use `--order desc` for descending. Repeat `--by` and `--order` for tie-breakers:
|
|
253
259
|
|
|
254
260
|
```bash
|
|
255
261
|
yerba sort config.yml "tags"
|
|
256
|
-
yerba sort videos.yml --by "title"
|
|
257
|
-
yerba sort videos.yml --by "date
|
|
258
|
-
yerba sort videos.yml "[].speakers" --by "name"
|
|
262
|
+
yerba sort videos.yml --by ".title"
|
|
263
|
+
yerba sort videos.yml --by ".date" --order desc --by ".title"
|
|
264
|
+
yerba sort videos.yml "[].speakers" --by ".name"
|
|
259
265
|
```
|
|
260
266
|
|
|
267
|
+
Use `--order` with a comma-separated list to specify an explicit custom order. All items must be listed:
|
|
268
|
+
|
|
269
|
+
```bash
|
|
270
|
+
yerba sort videos.yml "[]" --by ".id" --order "talk-c,talk-a,talk-b"
|
|
271
|
+
yerba sort speakers.yml "[]" --by ".name" --order "Charlie,Alice,Bob"
|
|
272
|
+
yerba sort config.yml "tags" --by "." --order "yaml,ruby,rust"
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
This is useful for reordering items in a specific sequence (e.g., conference schedule order, priority lists) or when an LLM agent needs to rearrange items programmatically.
|
|
276
|
+
|
|
261
277
|
### `sort-keys`
|
|
262
278
|
|
|
263
279
|
Reorder the keys in a map to match a predefined order. If any key in the document is not present in the order list, the command aborts with an error, this ensures you account for every field:
|
|
@@ -295,6 +311,57 @@ yerba blank-lines videos.yml "[]" 1
|
|
|
295
311
|
yerba blank-lines config.yml "tags" 0
|
|
296
312
|
```
|
|
297
313
|
|
|
314
|
+
### `selectors`
|
|
315
|
+
|
|
316
|
+
Show all valid selectors for a YAML file. Useful for discovering the structure of a file and knowing which selectors you can use with other commands:
|
|
317
|
+
|
|
318
|
+
```bash
|
|
319
|
+
yerba selectors config.yml
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
Output:
|
|
323
|
+
```
|
|
324
|
+
database
|
|
325
|
+
database.host
|
|
326
|
+
database.port
|
|
327
|
+
tags
|
|
328
|
+
tags[]
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
For sequences of objects:
|
|
332
|
+
|
|
333
|
+
```bash
|
|
334
|
+
yerba selectors videos.yml
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
Output:
|
|
338
|
+
```
|
|
339
|
+
[]
|
|
340
|
+
[].id
|
|
341
|
+
[].title
|
|
342
|
+
[].speakers
|
|
343
|
+
[].speakers[]
|
|
344
|
+
[].speakers[].name
|
|
345
|
+
[].speakers[].slug
|
|
346
|
+
[].video_id
|
|
347
|
+
[].video_provider
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
Pass a selector to scope the output to a specific subtree:
|
|
351
|
+
|
|
352
|
+
```bash
|
|
353
|
+
yerba selectors config.yml "database"
|
|
354
|
+
yerba selectors videos.yml "[]"
|
|
355
|
+
yerba selectors videos.yml "[].speakers"
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
Works with glob patterns to show the union of selectors across multiple files:
|
|
359
|
+
|
|
360
|
+
```bash
|
|
361
|
+
yerba selectors "data/**/videos.yml"
|
|
362
|
+
yerba selectors "data/**/videos.yml" "[]"
|
|
363
|
+
```
|
|
364
|
+
|
|
298
365
|
## `Yerbafile`
|
|
299
366
|
|
|
300
367
|
A `Yerbafile` is a YAML configuration file that defines formatting and editing rules as pipelines of operations that are applied to your files across your project.
|
|
@@ -428,6 +495,12 @@ document["database"]["host"].value = "0.0.0.0"
|
|
|
428
495
|
document.set("database.port", 3306)
|
|
429
496
|
```
|
|
430
497
|
|
|
498
|
+
Set all matching nodes at once with `all: true`:
|
|
499
|
+
|
|
500
|
+
```ruby
|
|
501
|
+
document.set("[].description", "", all: true)
|
|
502
|
+
```
|
|
503
|
+
|
|
431
504
|
Insert new keys with positional control:
|
|
432
505
|
|
|
433
506
|
```ruby
|
data/ext/yerba/extconf.rb
CHANGED
|
@@ -93,19 +93,27 @@ if target_platform
|
|
|
93
93
|
end
|
|
94
94
|
end
|
|
95
95
|
|
|
96
|
-
|
|
96
|
+
static_lib = File.join(lib_dir, "libyerba.a")
|
|
97
97
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
98
|
+
if File.exist?(static_lib)
|
|
99
|
+
puts "yerba: Static library found at #{static_lib}"
|
|
100
|
+
$LDFLAGS << " #{static_lib}"
|
|
101
|
+
else
|
|
102
|
+
lib_path = File.join(lib_dir, lib_name)
|
|
101
103
|
|
|
102
|
-
|
|
104
|
+
unless File.exist?(lib_path)
|
|
105
|
+
abort "ERROR: Shared library not found at #{lib_path}"
|
|
106
|
+
end
|
|
103
107
|
|
|
104
|
-
|
|
105
|
-
$CFLAGS << " -I#{File.join(__dir__, "include")}"
|
|
108
|
+
puts "yerba: Shared library found at #{lib_path} (dynamic)"
|
|
106
109
|
|
|
107
|
-
|
|
108
|
-
|
|
110
|
+
$LDFLAGS << " -L#{lib_dir} -lyerba"
|
|
111
|
+
|
|
112
|
+
if RbConfig::CONFIG["host_os"].match?(/darwin|linux/)
|
|
113
|
+
$LDFLAGS << " -Wl,-rpath,#{lib_dir}"
|
|
114
|
+
end
|
|
109
115
|
end
|
|
110
116
|
|
|
117
|
+
$CFLAGS << " -I#{File.join(__dir__, "include")}"
|
|
118
|
+
|
|
111
119
|
create_makefile("yerba/yerba")
|
data/ext/yerba/include/yerba.h
CHANGED
|
@@ -102,7 +102,8 @@ char *yerba_document_find(const struct Document *document,
|
|
|
102
102
|
struct YerbaResult yerba_document_set(struct Document *document,
|
|
103
103
|
const char *path,
|
|
104
104
|
const char *value,
|
|
105
|
-
enum YerbaValueType value_type
|
|
105
|
+
enum YerbaValueType value_type,
|
|
106
|
+
bool all);
|
|
106
107
|
|
|
107
108
|
struct YerbaResult yerba_document_insert(struct Document *document,
|
|
108
109
|
const char *path,
|
data/ext/yerba/yerba.c
CHANGED
|
@@ -399,6 +399,7 @@ static VALUE document_set(int argc, VALUE *argv, VALUE self) {
|
|
|
399
399
|
const char *c_value;
|
|
400
400
|
YerbaValueType value_type;
|
|
401
401
|
char number_buffer[64];
|
|
402
|
+
bool all = false;
|
|
402
403
|
|
|
403
404
|
if (value == Qnil) {
|
|
404
405
|
c_value = "null";
|
|
@@ -422,7 +423,13 @@ static VALUE document_set(int argc, VALUE *argv, VALUE self) {
|
|
|
422
423
|
value_type = YERBA_VALUE_TYPE_STRING;
|
|
423
424
|
}
|
|
424
425
|
|
|
425
|
-
|
|
426
|
+
if (!NIL_P(opts)) {
|
|
427
|
+
VALUE v_all = rb_hash_aref(opts, ID2SYM(rb_intern("all")));
|
|
428
|
+
|
|
429
|
+
if (RTEST(v_all)) all = true;
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
YerbaResult result = yerba_document_set(document, StringValueCStr(path), c_value, value_type, all);
|
|
426
433
|
check_result(result);
|
|
427
434
|
|
|
428
435
|
return self;
|
data/lib/yerba/version.rb
CHANGED
data/lib/yerba.rb
CHANGED
|
@@ -12,9 +12,14 @@ require_relative "yerba/document"
|
|
|
12
12
|
require_relative "yerba/collection"
|
|
13
13
|
|
|
14
14
|
begin
|
|
15
|
-
|
|
15
|
+
major, minor, = RUBY_VERSION.split(".")
|
|
16
|
+
require "yerba/#{major}.#{minor}/yerba"
|
|
16
17
|
rescue LoadError
|
|
17
|
-
|
|
18
|
+
begin
|
|
19
|
+
require "yerba/yerba"
|
|
20
|
+
rescue LoadError
|
|
21
|
+
# C extension not available, fall back to CLI mode
|
|
22
|
+
end
|
|
18
23
|
end
|
|
19
24
|
|
|
20
25
|
module Yerba
|
data/rust/Cargo.lock
CHANGED
data/rust/Cargo.toml
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[package]
|
|
2
2
|
name = "yerba"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.3.0"
|
|
4
4
|
edition = "2021"
|
|
5
5
|
authors = ["Marco Roth <marco.roth@intergga.ch>"]
|
|
6
6
|
description = "YAML Editing and Refactoring with Better Accuracy"
|
|
@@ -13,7 +13,7 @@ categories = ["parser-implementations", "development-tools"]
|
|
|
13
13
|
[lib]
|
|
14
14
|
name = "yerba"
|
|
15
15
|
path = "src/lib.rs"
|
|
16
|
-
crate-type = ["cdylib", "rlib"]
|
|
16
|
+
crate-type = ["cdylib", "staticlib", "rlib"]
|
|
17
17
|
|
|
18
18
|
[[bin]]
|
|
19
19
|
name = "yerba"
|
data/rust/src/commands/delete.rs
CHANGED
|
@@ -3,12 +3,13 @@ use std::sync::LazyLock;
|
|
|
3
3
|
use indoc::indoc;
|
|
4
4
|
|
|
5
5
|
use super::colorize_examples;
|
|
6
|
-
use super::{output, parse_file, run_op};
|
|
6
|
+
use super::{output, parse_file, resolve_files, run_op};
|
|
7
7
|
|
|
8
8
|
static EXAMPLES: LazyLock<String> = LazyLock::new(|| {
|
|
9
9
|
colorize_examples(indoc! {r#"
|
|
10
10
|
yerba delete config.yml "database.pool"
|
|
11
11
|
yerba delete videos.yml "[0].description"
|
|
12
|
+
yerba delete "data/**/event.yml" "date_precision"
|
|
12
13
|
yerba delete config.yml "database.pool" --dry-run
|
|
13
14
|
"#})
|
|
14
15
|
});
|
|
@@ -28,8 +29,12 @@ pub struct Args {
|
|
|
28
29
|
|
|
29
30
|
impl Args {
|
|
30
31
|
pub fn run(self) {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
32
|
+
for resolved_file in resolve_files(&self.file) {
|
|
33
|
+
let mut document = parse_file(&resolved_file);
|
|
34
|
+
let result = document.delete(&self.selector);
|
|
35
|
+
|
|
36
|
+
run_op(&resolved_file, &document, result);
|
|
37
|
+
output(&resolved_file, &document, self.dry_run);
|
|
38
|
+
}
|
|
34
39
|
}
|
|
35
40
|
}
|
data/rust/src/commands/get.rs
CHANGED
|
@@ -3,7 +3,7 @@ use std::sync::LazyLock;
|
|
|
3
3
|
|
|
4
4
|
use indoc::indoc;
|
|
5
5
|
|
|
6
|
-
use super::{colorize_examples, parse_file, resolve_files};
|
|
6
|
+
use super::{colorize_examples, parse_file, resolve_files, show_similar_selectors};
|
|
7
7
|
|
|
8
8
|
static EXAMPLES: LazyLock<String> = LazyLock::new(|| {
|
|
9
9
|
colorize_examples(indoc! {r#"
|
|
@@ -64,9 +64,49 @@ impl Args {
|
|
|
64
64
|
|
|
65
65
|
let search_path_string = search_path.to_selector_string();
|
|
66
66
|
let mut all_results: Vec<serde_json::Value> = Vec::new();
|
|
67
|
+
let files = resolve_files(&self.file);
|
|
68
|
+
let is_glob = files.len() > 1;
|
|
67
69
|
|
|
68
|
-
for resolved_file in
|
|
69
|
-
let document = parse_file(
|
|
70
|
+
for resolved_file in &files {
|
|
71
|
+
let document = parse_file(resolved_file);
|
|
72
|
+
|
|
73
|
+
if normalized_condition.is_none() && !document.exists(&self.selector) {
|
|
74
|
+
if is_glob {
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
use super::color::*;
|
|
79
|
+
|
|
80
|
+
eprintln!(
|
|
81
|
+
"{RED}Error:{RESET} selector \"{}\" not found in {}",
|
|
82
|
+
self.selector, resolved_file
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
show_similar_selectors(resolved_file, &document, &self.selector);
|
|
86
|
+
process::exit(1);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if let Some(fields) = &select_fields {
|
|
90
|
+
for field in fields {
|
|
91
|
+
let field_trimmed = field.trim().trim_start_matches('.');
|
|
92
|
+
let full_selector = if search_path_string.is_empty() {
|
|
93
|
+
field_trimmed.to_string()
|
|
94
|
+
} else {
|
|
95
|
+
format!("{}.{}", search_path_string, field_trimmed)
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
if !document.exists(&full_selector) {
|
|
99
|
+
use super::color::*;
|
|
100
|
+
eprintln!(
|
|
101
|
+
"{RED}Error:{RESET} select field \"{}\" not found in {}",
|
|
102
|
+
field.trim(),
|
|
103
|
+
resolved_file
|
|
104
|
+
);
|
|
105
|
+
show_similar_selectors(resolved_file, &document, &full_selector);
|
|
106
|
+
process::exit(1);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
70
110
|
|
|
71
111
|
let values: Vec<serde_yaml::Value> = if let Some(condition) = &normalized_condition {
|
|
72
112
|
document.filter(&search_path_string, condition)
|
|
@@ -74,16 +114,6 @@ impl Args {
|
|
|
74
114
|
document.get_values(&search_path_string)
|
|
75
115
|
};
|
|
76
116
|
|
|
77
|
-
if values.is_empty()
|
|
78
|
-
&& !selector.has_brackets()
|
|
79
|
-
&& normalized_condition.is_none()
|
|
80
|
-
&& !document.exists(&self.selector)
|
|
81
|
-
{
|
|
82
|
-
use super::color::*;
|
|
83
|
-
eprintln!("{RED}Error:{RESET} path not found: {}", self.selector);
|
|
84
|
-
process::exit(1);
|
|
85
|
-
}
|
|
86
|
-
|
|
87
117
|
for value in values {
|
|
88
118
|
if let Some(field) = &extract_field {
|
|
89
119
|
let field_string = field.to_selector_string();
|
|
@@ -121,6 +151,19 @@ impl Args {
|
|
|
121
151
|
}
|
|
122
152
|
}
|
|
123
153
|
|
|
154
|
+
if is_glob && all_results.is_empty() && normalized_condition.is_none() {
|
|
155
|
+
use super::color::*;
|
|
156
|
+
|
|
157
|
+
eprintln!(
|
|
158
|
+
"{RED}Error:{RESET} selector \"{}\" not found in any of the {} files matching \"{}\"",
|
|
159
|
+
self.selector,
|
|
160
|
+
files.len(),
|
|
161
|
+
self.file
|
|
162
|
+
);
|
|
163
|
+
|
|
164
|
+
process::exit(1);
|
|
165
|
+
}
|
|
166
|
+
|
|
124
167
|
if self.raw {
|
|
125
168
|
for value in &all_results {
|
|
126
169
|
match value {
|
data/rust/src/commands/insert.rs
CHANGED
|
@@ -3,7 +3,7 @@ use std::sync::LazyLock;
|
|
|
3
3
|
use indoc::indoc;
|
|
4
4
|
|
|
5
5
|
use super::colorize_examples;
|
|
6
|
-
use super::{output, parse_file, run_op};
|
|
6
|
+
use super::{output, parse_file, resolve_files, run_op};
|
|
7
7
|
|
|
8
8
|
static EXAMPLES: LazyLock<String> = LazyLock::new(|| {
|
|
9
9
|
colorize_examples(indoc! {r#"
|
|
@@ -99,8 +99,12 @@ impl Args {
|
|
|
99
99
|
.unwrap_or(yerba::InsertPosition::Last)
|
|
100
100
|
};
|
|
101
101
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
102
|
+
for resolved_file in resolve_files(&self.file) {
|
|
103
|
+
let mut document = parse_file(&resolved_file);
|
|
104
|
+
let result = document.insert_into(&self.selector, &resolved_value, position.clone());
|
|
105
|
+
|
|
106
|
+
run_op(&resolved_file, &document, result);
|
|
107
|
+
output(&resolved_file, &document, self.dry_run);
|
|
108
|
+
}
|
|
105
109
|
}
|
|
106
110
|
}
|
data/rust/src/commands/mod.rs
CHANGED
|
@@ -11,6 +11,7 @@ pub mod move_key;
|
|
|
11
11
|
pub mod quote_style;
|
|
12
12
|
pub mod remove;
|
|
13
13
|
pub mod rename;
|
|
14
|
+
pub mod selectors;
|
|
14
15
|
pub mod set;
|
|
15
16
|
pub mod sort;
|
|
16
17
|
pub mod sort_keys;
|
|
@@ -153,6 +154,7 @@ pub enum Command {
|
|
|
153
154
|
Sort(sort::Args),
|
|
154
155
|
QuoteStyle(quote_style::Args),
|
|
155
156
|
BlankLines(blank_lines::Args),
|
|
157
|
+
Selectors(selectors::Args),
|
|
156
158
|
#[command(about = "Create a new Yerbafile in the current directory")]
|
|
157
159
|
Init,
|
|
158
160
|
#[command(about = "Apply all rules from the Yerbafile and write changes")]
|
|
@@ -180,6 +182,7 @@ impl Command {
|
|
|
180
182
|
Command::Sort(args) => args.run(),
|
|
181
183
|
Command::QuoteStyle(args) => args.run(),
|
|
182
184
|
Command::BlankLines(args) => args.run(),
|
|
185
|
+
Command::Selectors(args) => args.run(),
|
|
183
186
|
Command::Init => init::run(),
|
|
184
187
|
Command::Apply => apply::run(),
|
|
185
188
|
Command::Check => check::run(),
|
|
@@ -224,7 +227,7 @@ pub(crate) fn run_yerbafile(write: bool) {
|
|
|
224
227
|
}
|
|
225
228
|
|
|
226
229
|
if !has_changes && !has_errors {
|
|
227
|
-
eprintln!("
|
|
230
|
+
eprintln!("\n{BOLD}{GREEN}All files match the rules.{RESET}");
|
|
228
231
|
}
|
|
229
232
|
|
|
230
233
|
if !write && has_changes {
|
|
@@ -327,13 +330,77 @@ pub(crate) fn parse_file(file: &str) -> yerba::Document {
|
|
|
327
330
|
})
|
|
328
331
|
}
|
|
329
332
|
|
|
330
|
-
pub(crate) fn run_op(
|
|
333
|
+
pub(crate) fn run_op(file: &str, document: &yerba::Document, result: Result<(), yerba::YerbaError>) {
|
|
334
|
+
run_op_with_hint(file, document, result, None);
|
|
335
|
+
}
|
|
336
|
+
|
|
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
|
+
) {
|
|
331
343
|
use color::*;
|
|
332
344
|
|
|
333
|
-
|
|
334
|
-
|
|
345
|
+
if let Err(error) = result {
|
|
346
|
+
if let yerba::YerbaError::SelectorNotFound(selector) = &error {
|
|
347
|
+
eprintln!("{RED}Error:{RESET} selector \"{selector}\" not found in {file}");
|
|
348
|
+
|
|
349
|
+
show_similar_selectors(file, document, selector);
|
|
350
|
+
} else {
|
|
351
|
+
eprintln!("{RED}Error:{RESET} {}", error);
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
if let Some(hint) = hint {
|
|
355
|
+
if matches!(error, yerba::YerbaError::SelectorNotFound(_)) {
|
|
356
|
+
eprintln!();
|
|
357
|
+
eprintln!(" {DIM}Hint: {hint}{RESET}");
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
335
361
|
process::exit(1);
|
|
336
|
-
}
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
pub(crate) fn show_similar_selectors(file: &str, document: &yerba::Document, invalid_path: &str) {
|
|
366
|
+
use color::*;
|
|
367
|
+
use yerba::didyoumean::didyoumean_ranked;
|
|
368
|
+
|
|
369
|
+
let selectors = document.selectors();
|
|
370
|
+
|
|
371
|
+
if selectors.is_empty() {
|
|
372
|
+
return;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
let query = invalid_path.split_whitespace().next().unwrap_or(invalid_path);
|
|
376
|
+
let threshold = query.len() / 2 + 3;
|
|
377
|
+
let close = didyoumean_ranked(query, &selectors, threshold);
|
|
378
|
+
|
|
379
|
+
eprintln!();
|
|
380
|
+
|
|
381
|
+
if close.is_empty() {
|
|
382
|
+
eprintln!(" {BOLD}Available selectors in {file}:{RESET}");
|
|
383
|
+
|
|
384
|
+
for selector in selectors.iter().take(10) {
|
|
385
|
+
eprintln!(" {DIM}{selector}{RESET}");
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
if selectors.len() > 10 {
|
|
389
|
+
eprintln!(" {DIM}... and {} more{RESET}", selectors.len() - 10);
|
|
390
|
+
}
|
|
391
|
+
} else if close.len() == 1 {
|
|
392
|
+
eprintln!(" {BOLD}Did you mean this selector?{RESET} {}", close[0]);
|
|
393
|
+
} else {
|
|
394
|
+
eprintln!(" {BOLD}Did you mean one of these selectors?{RESET}");
|
|
395
|
+
|
|
396
|
+
for selector in close.iter().take(5) {
|
|
397
|
+
eprintln!(" {selector}");
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
eprintln!();
|
|
402
|
+
eprintln!(" {BOLD}To see all valid selectors, run:{RESET}");
|
|
403
|
+
eprintln!(" yerba selectors \"{file}\"{RESET}");
|
|
337
404
|
}
|
|
338
405
|
|
|
339
406
|
pub(crate) fn output(file: &str, document: &yerba::Document, dry_run: bool) {
|
|
@@ -48,7 +48,8 @@ impl Args {
|
|
|
48
48
|
|document, path, reference| document.resolve_sequence_index(path, reference),
|
|
49
49
|
);
|
|
50
50
|
|
|
51
|
-
|
|
51
|
+
let result = document.move_item(&self.selector, from_index, to_index);
|
|
52
|
+
run_op(&self.file, &document, result);
|
|
52
53
|
output(&self.file, &document, self.dry_run);
|
|
53
54
|
}
|
|
54
55
|
}
|
|
@@ -81,7 +81,8 @@ impl Args {
|
|
|
81
81
|
|document, parent_path, reference| document.resolve_key_index(parent_path, reference),
|
|
82
82
|
);
|
|
83
83
|
|
|
84
|
-
|
|
84
|
+
let result = document.move_key(parent_path, from_index, to_index);
|
|
85
|
+
run_op(&self.file, &document, result);
|
|
85
86
|
output(&self.file, &document, self.dry_run);
|
|
86
87
|
}
|
|
87
88
|
}
|
data/rust/src/commands/remove.rs
CHANGED
|
@@ -3,7 +3,7 @@ use std::sync::LazyLock;
|
|
|
3
3
|
use indoc::indoc;
|
|
4
4
|
|
|
5
5
|
use super::colorize_examples;
|
|
6
|
-
use super::{output, parse_file, run_op};
|
|
6
|
+
use super::{output, parse_file, resolve_files, run_op};
|
|
7
7
|
|
|
8
8
|
static EXAMPLES: LazyLock<String> = LazyLock::new(|| {
|
|
9
9
|
colorize_examples(indoc! {r#"
|
|
@@ -28,8 +28,12 @@ pub struct Args {
|
|
|
28
28
|
|
|
29
29
|
impl Args {
|
|
30
30
|
pub fn run(self) {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
31
|
+
for resolved_file in resolve_files(&self.file) {
|
|
32
|
+
let mut document = parse_file(&resolved_file);
|
|
33
|
+
let result = document.remove(&self.selector, &self.value);
|
|
34
|
+
|
|
35
|
+
run_op(&resolved_file, &document, result);
|
|
36
|
+
output(&resolved_file, &document, self.dry_run);
|
|
37
|
+
}
|
|
34
38
|
}
|
|
35
39
|
}
|
data/rust/src/commands/rename.rs
CHANGED
|
@@ -3,7 +3,7 @@ use std::sync::LazyLock;
|
|
|
3
3
|
use indoc::indoc;
|
|
4
4
|
|
|
5
5
|
use super::colorize_examples;
|
|
6
|
-
use super::{output, parse_file, run_op};
|
|
6
|
+
use super::{output, parse_file, resolve_files, run_op};
|
|
7
7
|
|
|
8
8
|
static EXAMPLES: LazyLock<String> = LazyLock::new(|| {
|
|
9
9
|
colorize_examples(indoc! {r#"
|
|
@@ -30,8 +30,12 @@ pub struct Args {
|
|
|
30
30
|
|
|
31
31
|
impl Args {
|
|
32
32
|
pub fn run(self) {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
33
|
+
for resolved_file in resolve_files(&self.file) {
|
|
34
|
+
let mut document = parse_file(&resolved_file);
|
|
35
|
+
let result = document.rename(&self.source, &self.destination);
|
|
36
|
+
|
|
37
|
+
run_op(&resolved_file, &document, result);
|
|
38
|
+
output(&resolved_file, &document, self.dry_run);
|
|
39
|
+
}
|
|
36
40
|
}
|
|
37
41
|
}
|