yerba 0.9.2-aarch64-linux-gnu → 0.10.0-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 +34 -17
- data/exe/aarch64-linux-gnu/yerba +0 -0
- data/ext/yerba/include/yerba.h +56 -34
- data/ext/yerba/yerba.c +268 -186
- 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 +4 -4
- data/lib/yerba/document.rb +134 -0
- data/lib/yerba/formatting.rb +1 -0
- data/lib/yerba/location.rb +8 -1
- data/lib/yerba/map.rb +16 -6
- data/lib/yerba/node.rb +41 -0
- data/lib/yerba/query_result.rb +33 -10
- data/lib/yerba/scalar.rb +29 -0
- data/lib/yerba/selector.rb +3 -1
- data/lib/yerba/sequence.rb +38 -33
- data/lib/yerba/version.rb +1 -1
- data/rust/Cargo.lock +1 -1
- data/rust/Cargo.toml +1 -1
- data/rust/src/commands/get.rs +12 -12
- data/rust/src/commands/init.rs +8 -8
- data/rust/src/commands/insert.rs +4 -18
- data/rust/src/commands/location.rs +2 -0
- data/rust/src/commands/mod.rs +34 -6
- data/rust/src/commands/move_item.rs +1 -1
- data/rust/src/commands/move_key.rs +8 -8
- data/rust/src/commands/rename.rs +1 -1
- data/rust/src/commands/selectors.rs +1 -1
- data/rust/src/commands/set.rs +14 -6
- data/rust/src/document/condition.rs +25 -25
- data/rust/src/document/delete.rs +57 -43
- data/rust/src/document/get.rs +91 -82
- data/rust/src/document/insert.rs +77 -77
- data/rust/src/document/mod.rs +138 -64
- data/rust/src/document/nodes.rs +162 -0
- data/rust/src/document/schema.rs +8 -8
- data/rust/src/document/set.rs +49 -26
- data/rust/src/document/sort.rs +54 -54
- data/rust/src/document/style.rs +50 -49
- data/rust/src/document/unique.rs +5 -5
- data/rust/src/error.rs +8 -4
- data/rust/src/ffi.rs +218 -106
- data/rust/src/json.rs +62 -2
- data/rust/src/lib.rs +2 -1
- data/rust/src/main.rs +2 -1
- data/rust/src/selector.rs +48 -7
- data/rust/src/syntax.rs +449 -56
- data/rust/src/validation.rs +5 -3
- data/rust/src/yerbafile.rs +124 -76
- metadata +3 -2
data/rust/src/commands/get.rs
CHANGED
|
@@ -45,9 +45,9 @@ pub struct Args {
|
|
|
45
45
|
impl Args {
|
|
46
46
|
pub fn run(self) {
|
|
47
47
|
let selector = yerba::Selector::parse(&self.selector);
|
|
48
|
-
let
|
|
48
|
+
let condition_selector = self.condition.as_deref().map(yerba::Selector::parse);
|
|
49
49
|
let select_fields: Option<Vec<&str>> = self.select.as_deref().map(|fields| fields.split(',').collect());
|
|
50
|
-
let (
|
|
50
|
+
let (search_selector, extract_field) = self.resolve_search_scope(&selector, condition_selector.as_ref());
|
|
51
51
|
|
|
52
52
|
let normalized_condition = self.condition.as_ref().map(|condition| {
|
|
53
53
|
if !condition.starts_with('.') {
|
|
@@ -73,7 +73,7 @@ impl Args {
|
|
|
73
73
|
}
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
-
let
|
|
76
|
+
let search_selector_string = search_selector.to_selector_string();
|
|
77
77
|
let mut all_results: Vec<serde_json::Value> = Vec::new();
|
|
78
78
|
let files = resolve_files(&self.file);
|
|
79
79
|
let is_glob = files.len() > 1;
|
|
@@ -97,10 +97,10 @@ impl Args {
|
|
|
97
97
|
|
|
98
98
|
for field in fields {
|
|
99
99
|
let field_trimmed = field.trim().trim_start_matches('.');
|
|
100
|
-
let full_selector = if
|
|
100
|
+
let full_selector = if search_selector_string.is_empty() {
|
|
101
101
|
field_trimmed.to_string()
|
|
102
102
|
} else {
|
|
103
|
-
format!("{}.{}",
|
|
103
|
+
format!("{}.{}", search_selector_string, field_trimmed)
|
|
104
104
|
};
|
|
105
105
|
|
|
106
106
|
if !document.exists(&full_selector) {
|
|
@@ -122,24 +122,24 @@ impl Args {
|
|
|
122
122
|
|
|
123
123
|
let (values, selectors, lines): (Vec<yaml_serde::Value>, Vec<String>, Vec<usize>) = if select_fields.is_some() {
|
|
124
124
|
if let Some(condition) = &normalized_condition {
|
|
125
|
-
let triples = document.filter_with_selectors(&
|
|
125
|
+
let triples = document.filter_with_selectors(&search_selector_string, condition).unwrap_or_default();
|
|
126
126
|
let (values, rest): (Vec<_>, Vec<_>) = triples.into_iter().map(|(v, s, l)| (v, (s, l))).unzip();
|
|
127
127
|
let (selectors, lines): (Vec<_>, Vec<_>) = rest.into_iter().unzip();
|
|
128
128
|
|
|
129
129
|
(values, selectors, lines)
|
|
130
130
|
} else {
|
|
131
|
-
let located = document.get_all_located(&
|
|
131
|
+
let located = document.get_all_located(&search_selector_string);
|
|
132
132
|
let selectors = located.iter().map(|n| n.selector.clone()).collect();
|
|
133
133
|
let lines = located.iter().map(|n| n.line).collect();
|
|
134
|
-
let values = document.get_values(&
|
|
134
|
+
let values = document.get_values(&search_selector_string);
|
|
135
135
|
let values: Vec<_> = values.into_iter().filter(|v| !v.is_null()).collect();
|
|
136
136
|
|
|
137
137
|
(values, selectors, lines)
|
|
138
138
|
}
|
|
139
139
|
} else if let Some(condition) = &normalized_condition {
|
|
140
|
-
(document.filter(&
|
|
140
|
+
(document.filter(&search_selector_string, condition).unwrap_or_default(), Vec::new(), Vec::new())
|
|
141
141
|
} else {
|
|
142
|
-
(document.get_values(&
|
|
142
|
+
(document.get_values(&search_selector_string), Vec::new(), Vec::new())
|
|
143
143
|
};
|
|
144
144
|
|
|
145
145
|
for (index, value) in values.iter().enumerate() {
|
|
@@ -216,8 +216,8 @@ impl Args {
|
|
|
216
216
|
}
|
|
217
217
|
}
|
|
218
218
|
|
|
219
|
-
fn resolve_search_scope(&self, selector: &yerba::Selector,
|
|
220
|
-
if let Some(condition) =
|
|
219
|
+
fn resolve_search_scope(&self, selector: &yerba::Selector, condition_selector: Option<&yerba::Selector>) -> (yerba::Selector, Option<yerba::Selector>) {
|
|
220
|
+
if let Some(condition) = condition_selector {
|
|
221
221
|
if condition.is_relative() {
|
|
222
222
|
let (container, field) = selector.split_at_last_bracket();
|
|
223
223
|
|
data/rust/src/commands/init.rs
CHANGED
|
@@ -25,12 +25,12 @@ pub fn run() {
|
|
|
25
25
|
#
|
|
26
26
|
# # Override quote style for a specific selector
|
|
27
27
|
# - quote_style:
|
|
28
|
-
#
|
|
28
|
+
# selector: "[].speakers"
|
|
29
29
|
# value_style: plain
|
|
30
30
|
#
|
|
31
31
|
# # Sort keys in a predefined order (aborts on unknown keys)
|
|
32
32
|
# - sort_keys:
|
|
33
|
-
#
|
|
33
|
+
# selector: "[]"
|
|
34
34
|
# order:
|
|
35
35
|
# - id
|
|
36
36
|
# - title
|
|
@@ -38,7 +38,7 @@ pub fn run() {
|
|
|
38
38
|
#
|
|
39
39
|
# # Sort items in a sequence by field(s)
|
|
40
40
|
# - sort:
|
|
41
|
-
#
|
|
41
|
+
# selector: ""
|
|
42
42
|
# by: name
|
|
43
43
|
# # case_sensitive: false
|
|
44
44
|
#
|
|
@@ -54,19 +54,19 @@ pub fn run() {
|
|
|
54
54
|
#
|
|
55
55
|
# # Set a value (with optional condition)
|
|
56
56
|
# - set:
|
|
57
|
-
#
|
|
57
|
+
# selector: "status"
|
|
58
58
|
# value: "published"
|
|
59
59
|
# # condition: ".draft == true"
|
|
60
60
|
#
|
|
61
61
|
# # Insert a new key or sequence item
|
|
62
62
|
# - insert:
|
|
63
|
-
#
|
|
63
|
+
# selector: "tags"
|
|
64
64
|
# value: "yaml"
|
|
65
65
|
# # condition: ".tags not_contains yaml"
|
|
66
66
|
#
|
|
67
67
|
# # Delete a key (with optional condition)
|
|
68
68
|
# - delete:
|
|
69
|
-
#
|
|
69
|
+
# selector: "deprecated_field"
|
|
70
70
|
# # condition: ".deprecated_field == true"
|
|
71
71
|
#
|
|
72
72
|
# # Rename a key
|
|
@@ -76,12 +76,12 @@ pub fn run() {
|
|
|
76
76
|
#
|
|
77
77
|
# # Remove an item from a sequence
|
|
78
78
|
# - remove:
|
|
79
|
-
#
|
|
79
|
+
# selector: "tags"
|
|
80
80
|
# value: "obsolete"
|
|
81
81
|
#
|
|
82
82
|
# # Get a value and store it as a variable
|
|
83
83
|
# - get:
|
|
84
|
-
#
|
|
84
|
+
# selector: "[].name"
|
|
85
85
|
# as: "names"
|
|
86
86
|
# # file: "other_file.yml"
|
|
87
87
|
"#};
|
data/rust/src/commands/insert.rs
CHANGED
|
@@ -4,7 +4,7 @@ use std::sync::LazyLock;
|
|
|
4
4
|
use indoc::indoc;
|
|
5
5
|
|
|
6
6
|
use super::colorize_examples;
|
|
7
|
-
use super::{output, parse_file, resolve_files, run_op};
|
|
7
|
+
use super::{default_insert_position, output, parse_file, resolve_files, run_op};
|
|
8
8
|
|
|
9
9
|
static EXAMPLES: LazyLock<String> = LazyLock::new(|| {
|
|
10
10
|
colorize_examples(indoc! {r#"
|
|
@@ -81,28 +81,14 @@ impl Args {
|
|
|
81
81
|
std::process::exit(1);
|
|
82
82
|
};
|
|
83
83
|
|
|
84
|
-
let parent_path = self.selector.rsplit_once('.').map(|(parent, _)| parent).unwrap_or("");
|
|
85
|
-
|
|
86
84
|
let position = if let Some(index) = self.at {
|
|
87
85
|
yerba::InsertPosition::At(index)
|
|
88
86
|
} else if let Some(target) = self.before {
|
|
89
|
-
|
|
90
|
-
yerba::InsertPosition::BeforeCondition(target)
|
|
91
|
-
} else {
|
|
92
|
-
yerba::InsertPosition::Before(target)
|
|
93
|
-
}
|
|
87
|
+
yerba::InsertPosition::before(target)
|
|
94
88
|
} else if let Some(target) = self.after {
|
|
95
|
-
|
|
96
|
-
yerba::InsertPosition::AfterCondition(target)
|
|
97
|
-
} else {
|
|
98
|
-
yerba::InsertPosition::After(target)
|
|
99
|
-
}
|
|
89
|
+
yerba::InsertPosition::after(target)
|
|
100
90
|
} else {
|
|
101
|
-
|
|
102
|
-
.and_then(|yerbafile_path| yerba::Yerbafile::load(&yerbafile_path).ok())
|
|
103
|
-
.and_then(|yerbafile| yerbafile.sort_order_for(&self.file, parent_path))
|
|
104
|
-
.map(yerba::InsertPosition::FromSortOrder)
|
|
105
|
-
.unwrap_or(yerba::InsertPosition::Last)
|
|
91
|
+
default_insert_position(&self.file, &self.selector)
|
|
106
92
|
};
|
|
107
93
|
|
|
108
94
|
for resolved_file in resolve_files(&self.file) {
|
|
@@ -49,6 +49,8 @@ impl Args {
|
|
|
49
49
|
"end_column": info.location.end_column,
|
|
50
50
|
"start_offset": info.location.start_offset,
|
|
51
51
|
"end_offset": info.location.end_offset,
|
|
52
|
+
"start_character_column": info.location.start_character_column,
|
|
53
|
+
"end_character_column": info.location.end_character_column,
|
|
52
54
|
});
|
|
53
55
|
|
|
54
56
|
println!("{}", serde_json::to_string_pretty(&json).unwrap_or_default());
|
data/rust/src/commands/mod.rs
CHANGED
|
@@ -196,6 +196,24 @@ pub(crate) fn run_yerbafile(write: bool, files: Vec<String>) {
|
|
|
196
196
|
ui::subtle(format!("(from {})", yerbafile_path.display()))
|
|
197
197
|
);
|
|
198
198
|
|
|
199
|
+
if !yerbafile.deprecated_path_keys.is_empty() {
|
|
200
|
+
let keys = &yerbafile.deprecated_path_keys;
|
|
201
|
+
let mut places = keys.iter().take(3).cloned().collect::<Vec<_>>().join(", ");
|
|
202
|
+
|
|
203
|
+
if keys.len() > 3 {
|
|
204
|
+
places.push_str(&format!(" and {} more", keys.len() - 3));
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
eprintln!(
|
|
208
|
+
"{} {} is deprecated, use {} instead ({}: {})\n",
|
|
209
|
+
ui::pending("Warning:"),
|
|
210
|
+
ui::strong("path:"),
|
|
211
|
+
ui::strong("selector:"),
|
|
212
|
+
pluralize(keys.len(), "place", "places"),
|
|
213
|
+
places
|
|
214
|
+
);
|
|
215
|
+
}
|
|
216
|
+
|
|
199
217
|
let started = std::time::Instant::now();
|
|
200
218
|
|
|
201
219
|
let results = if files.is_empty() {
|
|
@@ -309,14 +327,14 @@ fn format_duration(elapsed: std::time::Duration) -> String {
|
|
|
309
327
|
|
|
310
328
|
pub(crate) fn resolve_move_indexes(
|
|
311
329
|
document: &yerba::Document,
|
|
312
|
-
|
|
330
|
+
selector: &str,
|
|
313
331
|
item: &str,
|
|
314
332
|
before: Option<String>,
|
|
315
333
|
after: Option<String>,
|
|
316
334
|
to: Option<usize>,
|
|
317
335
|
resolve: impl Fn(&yerba::Document, &str, &str) -> Result<usize, yerba::YerbaError>,
|
|
318
336
|
) -> (usize, usize) {
|
|
319
|
-
let from_index = resolve(document,
|
|
337
|
+
let from_index = resolve(document, selector, item).unwrap_or_else(|error| {
|
|
320
338
|
eprintln!("{} {}", ui::failure("Error:"), error);
|
|
321
339
|
process::exit(1);
|
|
322
340
|
});
|
|
@@ -324,7 +342,7 @@ pub(crate) fn resolve_move_indexes(
|
|
|
324
342
|
let to_index = if let Some(index) = to {
|
|
325
343
|
index
|
|
326
344
|
} else if let Some(target) = &before {
|
|
327
|
-
let target_index = resolve(document,
|
|
345
|
+
let target_index = resolve(document, selector, target).unwrap_or_else(|error| {
|
|
328
346
|
eprintln!("{} {}", ui::failure("Error:"), error);
|
|
329
347
|
process::exit(1);
|
|
330
348
|
});
|
|
@@ -335,7 +353,7 @@ pub(crate) fn resolve_move_indexes(
|
|
|
335
353
|
target_index
|
|
336
354
|
}
|
|
337
355
|
} else if let Some(target) = &after {
|
|
338
|
-
let target_index = resolve(document,
|
|
356
|
+
let target_index = resolve(document, selector, target).unwrap_or_else(|error| {
|
|
339
357
|
eprintln!("{} {}", ui::failure("Error:"), error);
|
|
340
358
|
process::exit(1);
|
|
341
359
|
});
|
|
@@ -417,7 +435,7 @@ pub(crate) fn run_op_with_hint(display_file: &str, document: &yerba::Document, r
|
|
|
417
435
|
}
|
|
418
436
|
}
|
|
419
437
|
|
|
420
|
-
pub(crate) fn show_similar_selectors(file: &str, document: &yerba::Document,
|
|
438
|
+
pub(crate) fn show_similar_selectors(file: &str, document: &yerba::Document, invalid_selector: &str) {
|
|
421
439
|
use yerba::didyoumean::didyoumean_ranked;
|
|
422
440
|
|
|
423
441
|
let selectors = document.selectors();
|
|
@@ -426,7 +444,7 @@ pub(crate) fn show_similar_selectors(file: &str, document: &yerba::Document, inv
|
|
|
426
444
|
return;
|
|
427
445
|
}
|
|
428
446
|
|
|
429
|
-
let query =
|
|
447
|
+
let query = invalid_selector.split_whitespace().next().unwrap_or(invalid_selector);
|
|
430
448
|
let threshold = query.len() / 2 + 3;
|
|
431
449
|
let close = didyoumean_ranked(query, &selectors, threshold);
|
|
432
450
|
|
|
@@ -468,3 +486,13 @@ pub(crate) fn output(file: &str, document: &yerba::Document, dry_run: bool) {
|
|
|
468
486
|
});
|
|
469
487
|
}
|
|
470
488
|
}
|
|
489
|
+
|
|
490
|
+
pub(crate) fn default_insert_position(file: &str, selector: &str) -> yerba::InsertPosition {
|
|
491
|
+
let parent_selector = selector.rsplit_once('.').map(|(parent, _)| parent).unwrap_or("");
|
|
492
|
+
|
|
493
|
+
yerba::Yerbafile::find()
|
|
494
|
+
.and_then(|yerbafile_path| yerba::Yerbafile::load(&yerbafile_path).ok())
|
|
495
|
+
.and_then(|yerbafile| yerbafile.sort_order_for(file, parent_selector))
|
|
496
|
+
.map(yerba::InsertPosition::FromSortOrder)
|
|
497
|
+
.unwrap_or(yerba::InsertPosition::Last)
|
|
498
|
+
}
|
|
@@ -45,7 +45,7 @@ impl Args {
|
|
|
45
45
|
self.before,
|
|
46
46
|
self.after,
|
|
47
47
|
self.to,
|
|
48
|
-
|document,
|
|
48
|
+
|document, selector, reference| document.resolve_sequence_index(selector, reference),
|
|
49
49
|
);
|
|
50
50
|
|
|
51
51
|
let result = document.move_item(&self.selector, from_index, to_index);
|
|
@@ -36,16 +36,16 @@ pub struct Args {
|
|
|
36
36
|
|
|
37
37
|
impl Args {
|
|
38
38
|
pub fn run(self) {
|
|
39
|
-
let (
|
|
39
|
+
let (parent_selector, key) = self.selector.rsplit_once('.').unwrap_or(("", &self.selector));
|
|
40
40
|
|
|
41
41
|
let mut document = parse_file(&self.file);
|
|
42
42
|
|
|
43
43
|
let before_key = self.before.map(|target| {
|
|
44
44
|
let (target_parent, target_key) = target.rsplit_once('.').unwrap_or(("", &target));
|
|
45
45
|
|
|
46
|
-
if target_parent !=
|
|
46
|
+
if target_parent != parent_selector {
|
|
47
47
|
eprintln!(
|
|
48
|
-
"{} cannot move key across different maps ({} \u{2192} {})\n\n Use 'yerba rename' to relocate keys to a different
|
|
48
|
+
"{} cannot move key across different maps ({} \u{2192} {})\n\n Use 'yerba rename' to relocate keys to a different map.",
|
|
49
49
|
ui::failure("Error:"),
|
|
50
50
|
self.selector,
|
|
51
51
|
target
|
|
@@ -60,9 +60,9 @@ impl Args {
|
|
|
60
60
|
let after_key = self.after.map(|target| {
|
|
61
61
|
let (target_parent, target_key) = target.rsplit_once('.').unwrap_or(("", &target));
|
|
62
62
|
|
|
63
|
-
if target_parent !=
|
|
63
|
+
if target_parent != parent_selector {
|
|
64
64
|
eprintln!(
|
|
65
|
-
"{} cannot move key across different maps ({} \u{2192} {})\n\n Use 'yerba rename' to relocate keys to a different
|
|
65
|
+
"{} cannot move key across different maps ({} \u{2192} {})\n\n Use 'yerba rename' to relocate keys to a different map.",
|
|
66
66
|
ui::failure("Error:"),
|
|
67
67
|
self.selector,
|
|
68
68
|
target
|
|
@@ -76,15 +76,15 @@ impl Args {
|
|
|
76
76
|
|
|
77
77
|
let (from_index, to_index) = resolve_move_indexes(
|
|
78
78
|
&document,
|
|
79
|
-
|
|
79
|
+
parent_selector,
|
|
80
80
|
key,
|
|
81
81
|
before_key,
|
|
82
82
|
after_key,
|
|
83
83
|
self.to,
|
|
84
|
-
|document,
|
|
84
|
+
|document, parent_selector, reference| document.resolve_key_index(parent_selector, reference),
|
|
85
85
|
);
|
|
86
86
|
|
|
87
|
-
let result = document.move_key(
|
|
87
|
+
let result = document.move_key(parent_selector, from_index, to_index);
|
|
88
88
|
run_op(&self.file, &document, result);
|
|
89
89
|
output(&self.file, &document, self.dry_run);
|
|
90
90
|
}
|
data/rust/src/commands/rename.rs
CHANGED
|
@@ -16,7 +16,7 @@ static EXAMPLES: LazyLock<String> = LazyLock::new(|| {
|
|
|
16
16
|
|
|
17
17
|
#[derive(clap::Args)]
|
|
18
18
|
#[command(
|
|
19
|
-
about = "Rename a key, or move it to another map (the destination is a full selector
|
|
19
|
+
about = "Rename a key, or move it to another map (the destination is a full selector)",
|
|
20
20
|
arg_required_else_help = true,
|
|
21
21
|
after_help = EXAMPLES.as_str()
|
|
22
22
|
)]
|
|
@@ -25,7 +25,7 @@ static EXAMPLES: LazyLock<String> = LazyLock::new(|| {
|
|
|
25
25
|
)]
|
|
26
26
|
pub struct Args {
|
|
27
27
|
file: String,
|
|
28
|
-
/// Show selectors starting from this
|
|
28
|
+
/// Show selectors starting from this selector
|
|
29
29
|
selector: Option<String>,
|
|
30
30
|
/// Sort selectors alphabetically (default: document order)
|
|
31
31
|
#[arg(long)]
|
data/rust/src/commands/set.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, resolve_files, run_op_with_hint};
|
|
6
|
+
use super::{default_insert_position, output, parse_file, resolve_files, run_op_with_hint};
|
|
7
7
|
|
|
8
8
|
static EXAMPLES: LazyLock<String> = LazyLock::new(|| {
|
|
9
9
|
colorize_examples(indoc! {r#"
|
|
10
10
|
yerba set config.yml "database.host" "0.0.0.0"
|
|
11
11
|
yerba set config.yml "database.host" "0.0.0.0" --if-exists
|
|
12
|
+
yerba set config.yml "database.pool" 5 --plain --if-missing
|
|
12
13
|
yerba set config.yml "database.host" "0.0.0.0" --condition ".port == 5432"
|
|
13
14
|
yerba set videos.yml "[0].title" "New Title"
|
|
14
15
|
yerba set videos.yml "[].title" "New Title" --condition ".id == talk-1"
|
|
@@ -21,7 +22,7 @@ static EXAMPLES: LazyLock<String> = LazyLock::new(|| {
|
|
|
21
22
|
|
|
22
23
|
#[derive(clap::Args)]
|
|
23
24
|
#[command(
|
|
24
|
-
about = "Update an existing value at a
|
|
25
|
+
about = "Update an existing value at a selector (preserves quote style)",
|
|
25
26
|
arg_required_else_help = true,
|
|
26
27
|
after_help = EXAMPLES.as_str()
|
|
27
28
|
)]
|
|
@@ -46,7 +47,7 @@ pub struct Args {
|
|
|
46
47
|
|
|
47
48
|
impl Args {
|
|
48
49
|
pub fn run(self) {
|
|
49
|
-
let
|
|
50
|
+
let parent_selector = self.selector.rsplit_once('.').map(|(parent, _)| parent).unwrap_or("");
|
|
50
51
|
|
|
51
52
|
if let Some(condition) = &self.condition {
|
|
52
53
|
if let Err(error) = yerba::validate_condition(condition) {
|
|
@@ -59,7 +60,7 @@ impl Args {
|
|
|
59
60
|
}
|
|
60
61
|
|
|
61
62
|
let leaf = self.selector.rsplit_once('.').map(|(_, leaf)| leaf).unwrap_or(&self.selector);
|
|
62
|
-
let per_item = yerba::Selector::parse(
|
|
63
|
+
let per_item = yerba::Selector::parse(parent_selector).has_brackets();
|
|
63
64
|
|
|
64
65
|
for resolved_file in resolve_files(&self.file) {
|
|
65
66
|
let mut document = parse_file(&resolved_file);
|
|
@@ -69,14 +70,21 @@ impl Args {
|
|
|
69
70
|
} else if self.if_missing {
|
|
70
71
|
!document.exists(&self.selector)
|
|
71
72
|
} else if let Some(condition) = &self.condition {
|
|
72
|
-
per_item || document.evaluate_condition(
|
|
73
|
+
per_item || document.evaluate_condition(parent_selector, condition).unwrap_or(false)
|
|
73
74
|
} else {
|
|
74
75
|
true
|
|
75
76
|
};
|
|
76
77
|
|
|
77
78
|
if should_set {
|
|
78
79
|
let result = match &self.condition {
|
|
79
|
-
|
|
80
|
+
_ if self.if_missing => {
|
|
81
|
+
let value = if self.plain { self.value.clone() } else { yerba::quote_scalar(&self.value) };
|
|
82
|
+
|
|
83
|
+
document.insert_into(&self.selector, &value, default_insert_position(&self.file, &self.selector))
|
|
84
|
+
}
|
|
85
|
+
Some(condition) if per_item => document
|
|
86
|
+
.set_where(parent_selector, leaf, &self.value, condition, self.all, self.plain)
|
|
87
|
+
.map(|_| ()),
|
|
80
88
|
_ if self.all && self.plain => document.set_all_plain(&self.selector, &self.value),
|
|
81
89
|
_ if self.all => document.set_all(&self.selector, &self.value),
|
|
82
90
|
_ if self.plain => document.set_plain(&self.selector, &self.value),
|
|
@@ -47,12 +47,12 @@ pub fn validate_item_condition(condition: &str) -> Result<(), YerbaError> {
|
|
|
47
47
|
}
|
|
48
48
|
|
|
49
49
|
impl Document {
|
|
50
|
-
pub fn filter(&self,
|
|
50
|
+
pub fn filter(&self, selector: &str, condition: &str) -> Result<Vec<yaml_serde::Value>, YerbaError> {
|
|
51
51
|
validate_item_condition(condition)?;
|
|
52
52
|
|
|
53
53
|
Ok(
|
|
54
54
|
self
|
|
55
|
-
.navigate_all_compact(
|
|
55
|
+
.navigate_all_compact(selector)
|
|
56
56
|
.iter()
|
|
57
57
|
.filter(|node| self.evaluate_condition_on_node(node, condition))
|
|
58
58
|
.map(node_to_yaml_value)
|
|
@@ -60,14 +60,14 @@ impl Document {
|
|
|
60
60
|
)
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
-
pub fn filter_with_selectors(&self,
|
|
63
|
+
pub fn filter_with_selectors(&self, selector: &str, condition: &str) -> Result<Vec<(yaml_serde::Value, String, usize)>, YerbaError> {
|
|
64
64
|
validate_item_condition(condition)?;
|
|
65
65
|
|
|
66
66
|
let source = self.source_text();
|
|
67
67
|
|
|
68
68
|
Ok(
|
|
69
69
|
self
|
|
70
|
-
.navigate_all_compact(
|
|
70
|
+
.navigate_all_compact(selector)
|
|
71
71
|
.iter()
|
|
72
72
|
.filter(|node| self.evaluate_condition_on_node(node, condition))
|
|
73
73
|
.map(|node| {
|
|
@@ -88,13 +88,13 @@ impl Document {
|
|
|
88
88
|
None => return false,
|
|
89
89
|
};
|
|
90
90
|
|
|
91
|
-
let
|
|
91
|
+
let parsed = crate::selector::Selector::parse(&left);
|
|
92
92
|
|
|
93
|
-
if !
|
|
93
|
+
if !parsed.is_relative() {
|
|
94
94
|
return false;
|
|
95
95
|
}
|
|
96
96
|
|
|
97
|
-
let target_nodes = navigate_from_node(node, &
|
|
97
|
+
let target_nodes = navigate_from_node(node, &parsed.to_selector_string());
|
|
98
98
|
let values: Vec<String> = target_nodes.iter().filter_map(extract_scalar_text).collect();
|
|
99
99
|
|
|
100
100
|
match operator {
|
|
@@ -138,8 +138,8 @@ impl Document {
|
|
|
138
138
|
}
|
|
139
139
|
}
|
|
140
140
|
|
|
141
|
-
pub fn evaluate_condition(&self,
|
|
142
|
-
if
|
|
141
|
+
pub fn evaluate_condition(&self, parent_selector: &str, condition: &str) -> Result<bool, YerbaError> {
|
|
142
|
+
if parent_selector.is_empty() {
|
|
143
143
|
validate_condition(condition)?;
|
|
144
144
|
} else {
|
|
145
145
|
validate_item_condition(condition)?;
|
|
@@ -152,55 +152,55 @@ impl Document {
|
|
|
152
152
|
None => return Ok(false),
|
|
153
153
|
};
|
|
154
154
|
|
|
155
|
-
let
|
|
156
|
-
let
|
|
155
|
+
let parsed = crate::selector::Selector::parse(&left);
|
|
156
|
+
let selector_string = parsed.to_selector_string();
|
|
157
157
|
|
|
158
|
-
let
|
|
159
|
-
|
|
158
|
+
let full_selector = if parent_selector.is_empty() {
|
|
159
|
+
selector_string
|
|
160
160
|
} else {
|
|
161
|
-
format!("{}.{}",
|
|
161
|
+
format!("{}.{}", parent_selector, selector_string)
|
|
162
162
|
};
|
|
163
163
|
|
|
164
|
-
let has_brackets = crate::selector::Selector::parse(&
|
|
164
|
+
let has_brackets = crate::selector::Selector::parse(&full_selector).has_brackets();
|
|
165
165
|
|
|
166
166
|
let result = match operator {
|
|
167
167
|
"==" => {
|
|
168
168
|
if has_brackets {
|
|
169
|
-
self.get_all(&
|
|
169
|
+
self.get_all(&full_selector).iter().any(|value| value == &right)
|
|
170
170
|
} else {
|
|
171
|
-
self.get(&
|
|
171
|
+
self.get(&full_selector).unwrap_or_default() == right
|
|
172
172
|
}
|
|
173
173
|
}
|
|
174
174
|
"!=" => {
|
|
175
175
|
if has_brackets {
|
|
176
|
-
self.get_all(&
|
|
176
|
+
self.get_all(&full_selector).iter().all(|value| value != &right)
|
|
177
177
|
} else {
|
|
178
|
-
self.get(&
|
|
178
|
+
self.get(&full_selector).unwrap_or_default() != right
|
|
179
179
|
}
|
|
180
180
|
}
|
|
181
181
|
"contains" => {
|
|
182
182
|
if has_brackets {
|
|
183
|
-
self.get_all(&
|
|
183
|
+
self.get_all(&full_selector).iter().any(|value| value == &right || value.contains(&right))
|
|
184
184
|
} else {
|
|
185
|
-
let items = self.get_sequence_values(&
|
|
185
|
+
let items = self.get_sequence_values(&full_selector);
|
|
186
186
|
|
|
187
187
|
if !items.is_empty() {
|
|
188
188
|
items.iter().any(|item| item == &right)
|
|
189
189
|
} else {
|
|
190
|
-
self.get(&
|
|
190
|
+
self.get(&full_selector).map(|value| value.contains(&right)).unwrap_or(false)
|
|
191
191
|
}
|
|
192
192
|
}
|
|
193
193
|
}
|
|
194
194
|
"not_contains" => {
|
|
195
195
|
if has_brackets {
|
|
196
|
-
self.get_all(&
|
|
196
|
+
self.get_all(&full_selector).iter().all(|value| value != &right && !value.contains(&right))
|
|
197
197
|
} else {
|
|
198
|
-
let items = self.get_sequence_values(&
|
|
198
|
+
let items = self.get_sequence_values(&full_selector);
|
|
199
199
|
|
|
200
200
|
if !items.is_empty() {
|
|
201
201
|
!items.iter().any(|item| item == &right)
|
|
202
202
|
} else {
|
|
203
|
-
self.get(&
|
|
203
|
+
self.get(&full_selector).map(|value| !value.contains(&right)).unwrap_or(true)
|
|
204
204
|
}
|
|
205
205
|
}
|
|
206
206
|
}
|