yerba 0.5.0 → 0.6.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 +2 -26
- data/ext/yerba/include/yerba.h +12 -0
- data/ext/yerba/yerba.c +74 -0
- data/lib/yerba/formatting.rb +61 -0
- data/lib/yerba/map.rb +50 -6
- data/lib/yerba/sequence.rb +20 -0
- data/lib/yerba/version.rb +1 -1
- data/rust/Cargo.toml +2 -2
- data/rust/src/commands/get.rs +1 -1
- data/rust/src/commands/selectors.rs +4 -4
- data/rust/src/commands/sort.rs +1 -1
- data/rust/src/document/condition.rs +2 -2
- data/rust/src/document/delete.rs +65 -8
- data/rust/src/document/get.rs +55 -30
- data/rust/src/document/insert.rs +204 -9
- data/rust/src/document/mod.rs +76 -26
- data/rust/src/document/sort.rs +2 -2
- data/rust/src/document/style.rs +609 -2
- data/rust/src/ffi.rs +46 -0
- data/rust/src/json.rs +16 -16
- data/rust/src/lib.rs +3 -2
- data/rust/src/selector.rs +16 -0
- data/rust/src/yaml_writer.rs +98 -0
- data/rust/src/yerbafile.rs +164 -72
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 63abf64d4cc9319d72226b4c0a01757e2ef3770f3b5eb5d50691f9dba8d5541c
|
|
4
|
+
data.tar.gz: c809e6afbcf9dac67783853cab1b34017458204eadfc2a58bf82cf6b4d58afdd
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a6df761da07e009e2207f66d1fdc745733e3575e3faa5a1708d7d51ac37f03ac5cbd0f567962ed8e747e79396eed486794131915c1cf5350d1e82dc6451a90f7
|
|
7
|
+
data.tar.gz: ae74066e2fa54fc7e6eaa8defd68e786e18ab5bc871ba48996fb35536543fd7ef9f9e330d20855846a43d8e9718ba9d957a165c37c4391fcdb43984492f5d4b0
|
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.5"
|
|
51
51
|
```
|
|
52
52
|
|
|
53
53
|
```rust
|
|
@@ -843,31 +843,7 @@ document.to_s
|
|
|
843
843
|
|
|
844
844
|
## Development
|
|
845
845
|
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
### Building from source
|
|
849
|
-
|
|
850
|
-
The Rust core is in the `rust/` directory, with a workspace `Cargo.toml` at the root so all cargo commands work from the project root:
|
|
851
|
-
|
|
852
|
-
```bash
|
|
853
|
-
cargo build
|
|
854
|
-
cargo test
|
|
855
|
-
```
|
|
856
|
-
|
|
857
|
-
The C extension (for the Ruby API) is compiled via `ext/yerba/extconf.rb` which invokes `cargo build` and links against the resulting static library. Running `bundle exec rake compile` will build both the Rust library and the C extension.
|
|
858
|
-
|
|
859
|
-
### Running the CLI locally
|
|
860
|
-
|
|
861
|
-
```bash
|
|
862
|
-
cargo run -- get config.yml "database.host"
|
|
863
|
-
```
|
|
864
|
-
|
|
865
|
-
Or build a release binary:
|
|
866
|
-
|
|
867
|
-
```bash
|
|
868
|
-
cargo build --release
|
|
869
|
-
./target/release/yerba --help
|
|
870
|
-
```
|
|
846
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md) for details on how to set up the repo locally, run tests, and contribute.
|
|
871
847
|
|
|
872
848
|
## License
|
|
873
849
|
|
data/ext/yerba/include/yerba.h
CHANGED
|
@@ -90,6 +90,18 @@ struct YerbaResult yerba_document_set_quote_style(struct Document *document,
|
|
|
90
90
|
const char *path,
|
|
91
91
|
const char *style);
|
|
92
92
|
|
|
93
|
+
char *yerba_document_get_collection_style(const struct Document *document, const char *path);
|
|
94
|
+
|
|
95
|
+
struct YerbaResult yerba_document_set_collection_style(struct Document *document,
|
|
96
|
+
const char *path,
|
|
97
|
+
const char *style);
|
|
98
|
+
|
|
99
|
+
char *yerba_document_get_sequence_indent(const struct Document *document, const char *path);
|
|
100
|
+
|
|
101
|
+
struct YerbaResult yerba_document_set_sequence_indent(struct Document *document,
|
|
102
|
+
const char *path,
|
|
103
|
+
const char *style);
|
|
104
|
+
|
|
93
105
|
bool yerba_document_evaluate_condition(const struct Document *document,
|
|
94
106
|
const char *parent_path,
|
|
95
107
|
const char *condition);
|
data/ext/yerba/yerba.c
CHANGED
|
@@ -374,6 +374,76 @@ static VALUE document_set_quote_style(VALUE self, VALUE path, VALUE style) {
|
|
|
374
374
|
return self;
|
|
375
375
|
}
|
|
376
376
|
|
|
377
|
+
/* document.get_collection_style(path) → :flow, :block, or nil */
|
|
378
|
+
static VALUE document_get_collection_style(VALUE self, VALUE path) {
|
|
379
|
+
struct Document *document = get_document(self);
|
|
380
|
+
char *style = yerba_document_get_collection_style(document, StringValueCStr(path));
|
|
381
|
+
|
|
382
|
+
if (style == NULL) return Qnil;
|
|
383
|
+
|
|
384
|
+
VALUE symbol = ID2SYM(rb_intern(style));
|
|
385
|
+
|
|
386
|
+
yerba_string_free(style);
|
|
387
|
+
|
|
388
|
+
return symbol;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
/* document.set_collection_style(path, style) */
|
|
392
|
+
static VALUE document_set_collection_style(VALUE self, VALUE path, VALUE style) {
|
|
393
|
+
struct Document *document = get_document(self);
|
|
394
|
+
|
|
395
|
+
const char *style_string;
|
|
396
|
+
|
|
397
|
+
if (RB_TYPE_P(style, T_SYMBOL)) {
|
|
398
|
+
style_string = rb_id2name(SYM2ID(style));
|
|
399
|
+
} else if (RB_TYPE_P(style, T_STRING)) {
|
|
400
|
+
style_string = StringValueCStr(style);
|
|
401
|
+
} else {
|
|
402
|
+
rb_raise(rb_eError, "Invalid collection style (expected Symbol or String)");
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
YerbaResult result = yerba_document_set_collection_style(document, StringValueCStr(path), style_string);
|
|
406
|
+
|
|
407
|
+
check_result(result);
|
|
408
|
+
|
|
409
|
+
return self;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
/* document.get_sequence_indent(path) → :compact, :indented, or nil */
|
|
413
|
+
static VALUE document_get_sequence_indent(VALUE self, VALUE path) {
|
|
414
|
+
struct Document *document = get_document(self);
|
|
415
|
+
char *style = yerba_document_get_sequence_indent(document, StringValueCStr(path));
|
|
416
|
+
|
|
417
|
+
if (style == NULL) return Qnil;
|
|
418
|
+
|
|
419
|
+
VALUE symbol = ID2SYM(rb_intern(style));
|
|
420
|
+
|
|
421
|
+
yerba_string_free(style);
|
|
422
|
+
|
|
423
|
+
return symbol;
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
/* document.set_sequence_indent(path, style) */
|
|
427
|
+
static VALUE document_set_sequence_indent(VALUE self, VALUE path, VALUE style) {
|
|
428
|
+
struct Document *document = get_document(self);
|
|
429
|
+
|
|
430
|
+
const char *style_string;
|
|
431
|
+
|
|
432
|
+
if (RB_TYPE_P(style, T_SYMBOL)) {
|
|
433
|
+
style_string = rb_id2name(SYM2ID(style));
|
|
434
|
+
} else if (RB_TYPE_P(style, T_STRING)) {
|
|
435
|
+
style_string = StringValueCStr(style);
|
|
436
|
+
} else {
|
|
437
|
+
rb_raise(rb_eError, "Invalid sequence indent style (expected Symbol or String)");
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
YerbaResult result = yerba_document_set_sequence_indent(document, StringValueCStr(path), style_string);
|
|
441
|
+
|
|
442
|
+
check_result(result);
|
|
443
|
+
|
|
444
|
+
return self;
|
|
445
|
+
}
|
|
446
|
+
|
|
377
447
|
/* document.condition?(condition, path: "") */
|
|
378
448
|
static VALUE document_condition_p(int argc, VALUE *argv, VALUE self) {
|
|
379
449
|
VALUE condition, opts;
|
|
@@ -965,6 +1035,10 @@ void Init_yerba(void) {
|
|
|
965
1035
|
rb_define_method(rb_cDocument, "resolve_selectors", document_resolve_selectors, 1);
|
|
966
1036
|
rb_define_method(rb_cDocument, "get_quote_style", document_get_quote_style, 1);
|
|
967
1037
|
rb_define_method(rb_cDocument, "set_quote_style", document_set_quote_style, 2);
|
|
1038
|
+
rb_define_method(rb_cDocument, "get_collection_style", document_get_collection_style, 1);
|
|
1039
|
+
rb_define_method(rb_cDocument, "set_collection_style", document_set_collection_style, 2);
|
|
1040
|
+
rb_define_method(rb_cDocument, "get_sequence_indent", document_get_sequence_indent, 1);
|
|
1041
|
+
rb_define_method(rb_cDocument, "set_sequence_indent", document_set_sequence_indent, 2);
|
|
968
1042
|
rb_define_method(rb_cDocument, "exists?", document_exists_p, 1);
|
|
969
1043
|
rb_define_method(rb_cDocument, "valid_selector?", document_valid_selector_p, 1);
|
|
970
1044
|
rb_define_method(rb_cDocument, "condition?", document_condition_p, -1);
|
data/lib/yerba/formatting.rb
CHANGED
|
@@ -14,5 +14,66 @@ module Yerba
|
|
|
14
14
|
value.to_s
|
|
15
15
|
end
|
|
16
16
|
end
|
|
17
|
+
|
|
18
|
+
def self.to_yaml_value(value)
|
|
19
|
+
case value
|
|
20
|
+
when Array
|
|
21
|
+
return "[]" if value.empty?
|
|
22
|
+
|
|
23
|
+
items = value.map { |item| to_yaml_value(item) }
|
|
24
|
+
|
|
25
|
+
"[#{items.join(", ")}]"
|
|
26
|
+
when Hash
|
|
27
|
+
return "{}" if value.empty?
|
|
28
|
+
|
|
29
|
+
pairs = value.map { |key, value| "#{key}: #{to_yaml_value(value)}" }
|
|
30
|
+
|
|
31
|
+
"{#{pairs.join(", ")}}"
|
|
32
|
+
when true then "true"
|
|
33
|
+
when false then "false"
|
|
34
|
+
when nil then "null"
|
|
35
|
+
else value.to_s
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def self.to_block_yaml_value(value, indent = 0)
|
|
40
|
+
prefix = " " * indent
|
|
41
|
+
|
|
42
|
+
case value
|
|
43
|
+
when Array
|
|
44
|
+
return "[]" if value.empty?
|
|
45
|
+
|
|
46
|
+
value.map { |item|
|
|
47
|
+
if item.is_a?(Hash)
|
|
48
|
+
inner = to_block_yaml_value(item, indent + 1)
|
|
49
|
+
"#{prefix}- #{inner.lstrip}"
|
|
50
|
+
else
|
|
51
|
+
"#{prefix}- #{to_scalar_value(item)}"
|
|
52
|
+
end
|
|
53
|
+
}.join("\n")
|
|
54
|
+
when Hash
|
|
55
|
+
return "{}" if value.empty?
|
|
56
|
+
|
|
57
|
+
value.map { |key, value|
|
|
58
|
+
if value.is_a?(Hash) || value.is_a?(Array)
|
|
59
|
+
inner = to_block_yaml_value(value, indent + 1)
|
|
60
|
+
"#{prefix}#{key}:\n#{inner}"
|
|
61
|
+
else
|
|
62
|
+
"#{prefix}#{key}: #{to_scalar_value(value)}"
|
|
63
|
+
end
|
|
64
|
+
}.join("\n")
|
|
65
|
+
else
|
|
66
|
+
to_scalar_value(value)
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def self.to_scalar_value(value)
|
|
71
|
+
case value
|
|
72
|
+
when true then "true"
|
|
73
|
+
when false then "false"
|
|
74
|
+
when nil then "null"
|
|
75
|
+
else value.to_s
|
|
76
|
+
end
|
|
77
|
+
end
|
|
17
78
|
end
|
|
18
79
|
end
|
data/lib/yerba/map.rb
CHANGED
|
@@ -25,24 +25,44 @@ module Yerba
|
|
|
25
25
|
end
|
|
26
26
|
|
|
27
27
|
def []=(key, value)
|
|
28
|
+
set(key, value)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def set(key, value, style: nil)
|
|
28
32
|
if connected?
|
|
29
33
|
new_path = @selector.empty? ? key.to_s : "#{@selector}.#{key}"
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
34
|
+
coerced = coerce_value(value, style: style)
|
|
35
|
+
is_block_value = coerced.is_a?(String) && (coerced.include?("\n") || coerced.start_with?("- "))
|
|
36
|
+
|
|
37
|
+
if document.exists?(new_path) && is_block_value
|
|
38
|
+
# Block-style collections can't replace a scalar via set().
|
|
39
|
+
# Delete the key and re-insert at the same position.
|
|
40
|
+
all_keys = keys
|
|
41
|
+
key_index = all_keys.index(key.to_s)
|
|
42
|
+
after_key = key_index&.positive? ? all_keys[key_index - 1] : nil
|
|
43
|
+
|
|
44
|
+
document.delete(new_path)
|
|
45
|
+
|
|
46
|
+
if after_key
|
|
47
|
+
document.insert(new_path, coerced, after: after_key)
|
|
48
|
+
else
|
|
49
|
+
document.insert(new_path, coerced)
|
|
50
|
+
end
|
|
51
|
+
elsif document.exists?(new_path)
|
|
52
|
+
document.set(new_path, coerced)
|
|
33
53
|
else
|
|
34
|
-
document.insert(new_path,
|
|
54
|
+
document.insert(new_path, coerced)
|
|
35
55
|
end
|
|
36
56
|
else
|
|
37
57
|
@data[key] = value
|
|
38
58
|
end
|
|
39
59
|
end
|
|
40
60
|
|
|
41
|
-
def insert(key, value, before: nil, after: nil)
|
|
61
|
+
def insert(key, value, before: nil, after: nil, style: nil)
|
|
42
62
|
if connected?
|
|
43
63
|
new_path = @selector.empty? ? key.to_s : "#{@selector}.#{key}"
|
|
44
64
|
|
|
45
|
-
document.insert(new_path, value, before: before, after: after)
|
|
65
|
+
document.insert(new_path, coerce_value(value, style: style), before: before, after: after)
|
|
46
66
|
else
|
|
47
67
|
@data[key] = value
|
|
48
68
|
end
|
|
@@ -165,6 +185,16 @@ module Yerba
|
|
|
165
185
|
end
|
|
166
186
|
end
|
|
167
187
|
|
|
188
|
+
def collection_style
|
|
189
|
+
@collection_style || document&.get_collection_style(@selector)
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
def collection_style=(style)
|
|
193
|
+
document&.set_collection_style(@selector, style)
|
|
194
|
+
|
|
195
|
+
@collection_style = style
|
|
196
|
+
end
|
|
197
|
+
|
|
168
198
|
private
|
|
169
199
|
|
|
170
200
|
def format_value(value)
|
|
@@ -174,5 +204,19 @@ module Yerba
|
|
|
174
204
|
else value.to_s
|
|
175
205
|
end
|
|
176
206
|
end
|
|
207
|
+
|
|
208
|
+
def coerce_value(value, style: nil)
|
|
209
|
+
case value
|
|
210
|
+
when Array, Hash
|
|
211
|
+
resolved_style = style || :block
|
|
212
|
+
|
|
213
|
+
if resolved_style == :flow
|
|
214
|
+
Formatting.to_yaml_value(value)
|
|
215
|
+
else
|
|
216
|
+
Formatting.to_block_yaml_value(value)
|
|
217
|
+
end
|
|
218
|
+
else value
|
|
219
|
+
end
|
|
220
|
+
end
|
|
177
221
|
end
|
|
178
222
|
end
|
data/lib/yerba/sequence.rb
CHANGED
|
@@ -293,6 +293,26 @@ module Yerba
|
|
|
293
293
|
end
|
|
294
294
|
end
|
|
295
295
|
|
|
296
|
+
def collection_style
|
|
297
|
+
@collection_style || document&.get_collection_style(@selector)
|
|
298
|
+
end
|
|
299
|
+
|
|
300
|
+
def collection_style=(style)
|
|
301
|
+
document&.set_collection_style(@selector, style)
|
|
302
|
+
|
|
303
|
+
@collection_style = style
|
|
304
|
+
end
|
|
305
|
+
|
|
306
|
+
def sequence_indent
|
|
307
|
+
@sequence_indent || document&.get_sequence_indent(@selector)
|
|
308
|
+
end
|
|
309
|
+
|
|
310
|
+
def sequence_indent=(style)
|
|
311
|
+
document&.set_sequence_indent(@selector, style)
|
|
312
|
+
|
|
313
|
+
@sequence_indent = style
|
|
314
|
+
end
|
|
315
|
+
|
|
296
316
|
private
|
|
297
317
|
|
|
298
318
|
def dig_into(hash, path)
|
data/lib/yerba/version.rb
CHANGED
data/rust/Cargo.toml
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[package]
|
|
2
2
|
name = "yerba"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.6.0"
|
|
4
4
|
edition = "2021"
|
|
5
5
|
authors = ["Marco Roth <marco.roth@intergga.ch>"]
|
|
6
6
|
description = "YAML Editing and Refactoring with Better Accuracy"
|
|
@@ -24,7 +24,7 @@ yaml_parser = "0.3"
|
|
|
24
24
|
rowan = "0.16"
|
|
25
25
|
glob = "0.3"
|
|
26
26
|
serde = { version = "1", features = ["derive"] }
|
|
27
|
-
|
|
27
|
+
yaml_serde = "0.10"
|
|
28
28
|
serde_json = { version = "1", features = ["preserve_order"] }
|
|
29
29
|
clap = { version = "4", features = ["derive"] }
|
|
30
30
|
indoc = "2"
|
data/rust/src/commands/get.rs
CHANGED
|
@@ -112,7 +112,7 @@ impl Args {
|
|
|
112
112
|
}
|
|
113
113
|
}
|
|
114
114
|
|
|
115
|
-
let (values, selectors, lines): (Vec<
|
|
115
|
+
let (values, selectors, lines): (Vec<yaml_serde::Value>, Vec<String>, Vec<usize>) = if select_fields.is_some() {
|
|
116
116
|
if let Some(condition) = &normalized_condition {
|
|
117
117
|
let triples = document.filter_with_selectors(&search_path_string, condition);
|
|
118
118
|
let (values, rest): (Vec<_>, Vec<_>) = triples.into_iter().map(|(v, s, l)| (v, (s, l))).unzip();
|
|
@@ -116,11 +116,11 @@ impl Args {
|
|
|
116
116
|
}
|
|
117
117
|
}
|
|
118
118
|
|
|
119
|
-
fn collect_selectors(value: &
|
|
119
|
+
fn collect_selectors(value: &yaml_serde::Value, prefix: &str, selectors: &mut BTreeMap<String, SelectorInfo>, counter: &mut usize) {
|
|
120
120
|
match value {
|
|
121
|
-
|
|
121
|
+
yaml_serde::Value::Mapping(map) => {
|
|
122
122
|
for (key, child) in map {
|
|
123
|
-
if let
|
|
123
|
+
if let yaml_serde::Value::String(key_string) = key {
|
|
124
124
|
let selector = if prefix.is_empty() {
|
|
125
125
|
key_string.clone()
|
|
126
126
|
} else {
|
|
@@ -138,7 +138,7 @@ fn collect_selectors(value: &serde_yaml::Value, prefix: &str, selectors: &mut BT
|
|
|
138
138
|
}
|
|
139
139
|
}
|
|
140
140
|
|
|
141
|
-
|
|
141
|
+
yaml_serde::Value::Sequence(sequence) => {
|
|
142
142
|
let bracket_prefix = format!("{}[]", prefix);
|
|
143
143
|
|
|
144
144
|
let entry = selectors.entry(bracket_prefix.clone()).or_default();
|
data/rust/src/commands/sort.rs
CHANGED
|
@@ -329,7 +329,7 @@ impl Args {
|
|
|
329
329
|
.map(|item| {
|
|
330
330
|
if by_is_scalar {
|
|
331
331
|
match item {
|
|
332
|
-
|
|
332
|
+
yaml_serde::Value::String(string) => string.clone(),
|
|
333
333
|
_ => serde_json::to_string(&yerba::json::yaml_to_json(item)).unwrap_or_default(),
|
|
334
334
|
}
|
|
335
335
|
} else {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
use super::*;
|
|
2
2
|
|
|
3
3
|
impl Document {
|
|
4
|
-
pub fn filter(&self, dot_path: &str, condition: &str) -> Vec<
|
|
4
|
+
pub fn filter(&self, dot_path: &str, condition: &str) -> Vec<yaml_serde::Value> {
|
|
5
5
|
self
|
|
6
6
|
.navigate_all_compact(dot_path)
|
|
7
7
|
.iter()
|
|
@@ -10,7 +10,7 @@ impl Document {
|
|
|
10
10
|
.collect()
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
pub fn filter_with_selectors(&self, dot_path: &str, condition: &str) -> Vec<(
|
|
13
|
+
pub fn filter_with_selectors(&self, dot_path: &str, condition: &str) -> Vec<(yaml_serde::Value, String, usize)> {
|
|
14
14
|
let source = self.root.text().to_string();
|
|
15
15
|
|
|
16
16
|
self
|
data/rust/src/document/delete.rs
CHANGED
|
@@ -35,17 +35,74 @@ impl Document {
|
|
|
35
35
|
pub fn delete(&mut self, dot_path: &str) -> Result<(), YerbaError> {
|
|
36
36
|
Self::validate_path(dot_path)?;
|
|
37
37
|
|
|
38
|
-
let
|
|
39
|
-
let
|
|
38
|
+
let selector = crate::selector::Selector::parse(dot_path);
|
|
39
|
+
let segments = selector.segments();
|
|
40
|
+
let last_segment = segments.last().ok_or_else(|| YerbaError::SelectorNotFound(dot_path.to_string()))?;
|
|
41
|
+
let parent_path = selector.parent_path();
|
|
40
42
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
43
|
+
match last_segment {
|
|
44
|
+
crate::selector::SelectorSegment::Key(last_key) => {
|
|
45
|
+
let has_wildcard = selector.has_wildcard() || selector.has_brackets();
|
|
46
|
+
|
|
47
|
+
if parent_path.is_empty() && !has_wildcard {
|
|
48
|
+
let parent_node = self.navigate(&parent_path)?;
|
|
49
|
+
let map = parent_node
|
|
50
|
+
.descendants()
|
|
51
|
+
.find_map(BlockMap::cast)
|
|
52
|
+
.ok_or_else(|| YerbaError::SelectorNotFound(dot_path.to_string()))?;
|
|
53
|
+
|
|
54
|
+
let entry = find_entry_by_key(&map, last_key).ok_or_else(|| YerbaError::SelectorNotFound(dot_path.to_string()))?;
|
|
55
|
+
|
|
56
|
+
return self.remove_map_entry(&entry);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
let parent_nodes = self.navigate_all_compact(&parent_path);
|
|
60
|
+
|
|
61
|
+
if parent_nodes.is_empty() {
|
|
62
|
+
if has_wildcard {
|
|
63
|
+
return Ok(());
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return Err(YerbaError::SelectorNotFound(dot_path.to_string()));
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if parent_nodes.len() == 1 && !has_wildcard {
|
|
70
|
+
let map = parent_nodes[0]
|
|
71
|
+
.descendants()
|
|
72
|
+
.find_map(BlockMap::cast)
|
|
73
|
+
.ok_or_else(|| YerbaError::SelectorNotFound(dot_path.to_string()))?;
|
|
45
74
|
|
|
46
|
-
|
|
75
|
+
let entry = find_entry_by_key(&map, last_key).ok_or_else(|| YerbaError::SelectorNotFound(dot_path.to_string()))?;
|
|
47
76
|
|
|
48
|
-
|
|
77
|
+
return self.remove_map_entry(&entry);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
let mut ranges: Vec<TextRange> = Vec::new();
|
|
81
|
+
|
|
82
|
+
for parent_node in &parent_nodes {
|
|
83
|
+
if let Some(map) = parent_node.descendants().find_map(BlockMap::cast) {
|
|
84
|
+
if let Some(entry) = find_entry_by_key(&map, last_key) {
|
|
85
|
+
ranges.push(removal_range(entry.syntax()));
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if ranges.is_empty() {
|
|
91
|
+
return Ok(());
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
ranges.reverse();
|
|
95
|
+
|
|
96
|
+
for range in ranges {
|
|
97
|
+
self.apply_edit(range, "")?;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
Ok(())
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
crate::selector::SelectorSegment::Index(index) => self.remove_at(&parent_path, *index),
|
|
104
|
+
crate::selector::SelectorSegment::AllItems => Err(YerbaError::SelectorNotFound(dot_path.to_string())),
|
|
105
|
+
}
|
|
49
106
|
}
|
|
50
107
|
|
|
51
108
|
pub fn remove(&mut self, dot_path: &str, value: &str) -> Result<(), YerbaError> {
|
data/rust/src/document/get.rs
CHANGED
|
@@ -53,10 +53,12 @@ impl Document {
|
|
|
53
53
|
|
|
54
54
|
let current_node = self.navigate(dot_path).ok()?;
|
|
55
55
|
|
|
56
|
-
if current_node
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
56
|
+
if current_node.descendants().any(|child| {
|
|
57
|
+
matches!(
|
|
58
|
+
child.kind(),
|
|
59
|
+
SyntaxKind::BLOCK_MAP | SyntaxKind::BLOCK_SEQ | SyntaxKind::FLOW_SEQ | SyntaxKind::FLOW_MAP
|
|
60
|
+
)
|
|
61
|
+
}) {
|
|
60
62
|
return None;
|
|
61
63
|
}
|
|
62
64
|
|
|
@@ -130,24 +132,11 @@ impl Document {
|
|
|
130
132
|
}
|
|
131
133
|
|
|
132
134
|
pub fn node_type(&self, dot_path: &str) -> NodeType {
|
|
133
|
-
match self.
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
{
|
|
139
|
-
if BlockMap::can_cast(first_structural.kind()) {
|
|
140
|
-
NodeType::Map
|
|
141
|
-
} else {
|
|
142
|
-
NodeType::Sequence
|
|
143
|
-
}
|
|
144
|
-
} else if extract_scalar(&node).is_some() {
|
|
145
|
-
NodeType::Scalar
|
|
146
|
-
} else {
|
|
147
|
-
NodeType::NotFound
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
Err(_) => NodeType::NotFound,
|
|
135
|
+
match self.get_value(dot_path) {
|
|
136
|
+
Some(yaml_serde::Value::Mapping(_)) => NodeType::Map,
|
|
137
|
+
Some(yaml_serde::Value::Sequence(_)) => NodeType::Sequence,
|
|
138
|
+
Some(_) => NodeType::Scalar,
|
|
139
|
+
None => NodeType::NotFound,
|
|
151
140
|
}
|
|
152
141
|
}
|
|
153
142
|
|
|
@@ -278,7 +267,7 @@ impl Document {
|
|
|
278
267
|
.collect()
|
|
279
268
|
}
|
|
280
269
|
|
|
281
|
-
pub fn get_value(&self, dot_path: &str) -> Option<
|
|
270
|
+
pub fn get_value(&self, dot_path: &str) -> Option<yaml_serde::Value> {
|
|
282
271
|
if dot_path.is_empty() {
|
|
283
272
|
return Some(node_to_yaml_value(&self.root));
|
|
284
273
|
}
|
|
@@ -292,15 +281,15 @@ impl Document {
|
|
|
292
281
|
return None;
|
|
293
282
|
}
|
|
294
283
|
|
|
295
|
-
let values: Vec<
|
|
284
|
+
let values: Vec<yaml_serde::Value> = padded
|
|
296
285
|
.iter()
|
|
297
286
|
.map(|maybe_node| match maybe_node {
|
|
298
287
|
Some(node) => node_to_yaml_value(node),
|
|
299
|
-
None =>
|
|
288
|
+
None => yaml_serde::Value::Null,
|
|
300
289
|
})
|
|
301
290
|
.collect();
|
|
302
291
|
|
|
303
|
-
Some(
|
|
292
|
+
Some(yaml_serde::Value::Sequence(values))
|
|
304
293
|
} else {
|
|
305
294
|
let nodes = self.navigate_all_compact(dot_path);
|
|
306
295
|
|
|
@@ -312,13 +301,13 @@ impl Document {
|
|
|
312
301
|
return Some(node_to_yaml_value(&nodes[0]));
|
|
313
302
|
}
|
|
314
303
|
|
|
315
|
-
let values: Vec<
|
|
304
|
+
let values: Vec<yaml_serde::Value> = nodes.iter().map(node_to_yaml_value).collect();
|
|
316
305
|
|
|
317
|
-
Some(
|
|
306
|
+
Some(yaml_serde::Value::Sequence(values))
|
|
318
307
|
}
|
|
319
308
|
}
|
|
320
309
|
|
|
321
|
-
pub fn get_values(&self, dot_path: &str) -> Vec<
|
|
310
|
+
pub fn get_values(&self, dot_path: &str) -> Vec<yaml_serde::Value> {
|
|
322
311
|
let parsed = crate::selector::Selector::parse(dot_path);
|
|
323
312
|
|
|
324
313
|
if parsed.has_wildcard() {
|
|
@@ -327,7 +316,7 @@ impl Document {
|
|
|
327
316
|
.iter()
|
|
328
317
|
.map(|maybe_node| match maybe_node {
|
|
329
318
|
Some(node) => node_to_yaml_value(node),
|
|
330
|
-
None =>
|
|
319
|
+
None => yaml_serde::Value::Null,
|
|
331
320
|
})
|
|
332
321
|
.collect()
|
|
333
322
|
} else {
|
|
@@ -397,6 +386,42 @@ impl Document {
|
|
|
397
386
|
.collect()
|
|
398
387
|
}
|
|
399
388
|
|
|
389
|
+
pub fn get_collection_style(&self, dot_path: &str) -> Option<&'static str> {
|
|
390
|
+
let current_node = self.navigate(dot_path).ok()?;
|
|
391
|
+
|
|
392
|
+
for descendant in current_node.descendants() {
|
|
393
|
+
match descendant.kind() {
|
|
394
|
+
SyntaxKind::FLOW_SEQ | SyntaxKind::FLOW_MAP => return Some("flow"),
|
|
395
|
+
SyntaxKind::BLOCK_SEQ | SyntaxKind::BLOCK_MAP => return Some("block"),
|
|
396
|
+
_ => {}
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
None
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
pub fn get_sequence_indent(&self, dot_path: &str) -> Option<&'static str> {
|
|
404
|
+
let current_node = self.navigate(dot_path).ok()?;
|
|
405
|
+
|
|
406
|
+
let sequence = current_node.descendants().find_map(BlockSeq::cast)?;
|
|
407
|
+
let first_entry = sequence.entries().next()?;
|
|
408
|
+
|
|
409
|
+
let entry_indent = preceding_whitespace_indent(first_entry.syntax());
|
|
410
|
+
|
|
411
|
+
let entry_node = current_node.ancestors().find(|ancestor| ancestor.kind() == SyntaxKind::BLOCK_MAP_ENTRY)?;
|
|
412
|
+
|
|
413
|
+
let source = self.root.text().to_string();
|
|
414
|
+
let entry_start: usize = entry_node.text_range().start().into();
|
|
415
|
+
let line_start = source[..entry_start].rfind('\n').map(|position| position + 1).unwrap_or(0);
|
|
416
|
+
let key_indent = &source[line_start..entry_start];
|
|
417
|
+
|
|
418
|
+
if entry_indent.len() > key_indent.len() {
|
|
419
|
+
Some("indented")
|
|
420
|
+
} else {
|
|
421
|
+
Some("compact")
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
|
|
400
425
|
pub fn get_quote_style(&self, dot_path: &str) -> Option<&'static str> {
|
|
401
426
|
let current_node = self.navigate(dot_path).ok()?;
|
|
402
427
|
|