yerba 0.2.2-arm-linux-gnu → 0.4.0-arm-linux-gnu
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +167 -12
- data/exe/arm-linux-gnu/yerba +0 -0
- data/ext/yerba/extconf.rb +39 -12
- data/ext/yerba/include/yerba.h +21 -10
- data/ext/yerba/yerba.c +91 -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 +16 -0
- data/lib/yerba/sequence.rb +169 -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/cbindgen.toml +1 -0
- data/rust/rustfmt.toml +1 -1
- data/rust/src/commands/blank_lines.rs +1 -4
- data/rust/src/commands/delete.rs +9 -4
- data/rust/src/commands/directives.rs +61 -0
- data/rust/src/commands/get.rs +52 -26
- data/rust/src/commands/insert.rs +8 -4
- data/rust/src/commands/mod.rs +71 -9
- data/rust/src/commands/move_item.rs +2 -1
- data/rust/src/commands/move_key.rs +2 -1
- data/rust/src/commands/quote_style.rs +12 -6
- data/rust/src/commands/remove.rs +8 -4
- data/rust/src/commands/rename.rs +8 -4
- data/rust/src/commands/selectors.rs +158 -0
- data/rust/src/commands/set.rs +33 -16
- data/rust/src/commands/sort.rs +342 -10
- data/rust/src/didyoumean.rs +53 -0
- 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 +314 -0
- data/rust/src/document/mod.rs +784 -0
- data/rust/src/document/set.rs +90 -0
- data/rust/src/document/sort.rs +607 -0
- data/rust/src/document/style.rs +473 -0
- data/rust/src/error.rs +35 -7
- data/rust/src/ffi.rs +213 -520
- data/rust/src/json.rs +1 -7
- data/rust/src/lib.rs +89 -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 +39 -18
- metadata +13 -3
- data/rust/src/document.rs +0 -2237
data/lib/yerba/document.rb
CHANGED
|
@@ -48,6 +48,22 @@ module Yerba
|
|
|
48
48
|
end
|
|
49
49
|
end
|
|
50
50
|
|
|
51
|
+
def find_by(...)
|
|
52
|
+
root.find_by(...)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def where(...)
|
|
56
|
+
root.where(...)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def pluck(...)
|
|
60
|
+
root.pluck(...)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def <<(item)
|
|
64
|
+
root << item
|
|
65
|
+
end
|
|
66
|
+
|
|
51
67
|
def inspect
|
|
52
68
|
if path
|
|
53
69
|
"#<Yerba::Document path=#{path.inspect}>"
|
data/lib/yerba/sequence.rb
CHANGED
|
@@ -58,7 +58,100 @@ module Yerba
|
|
|
58
58
|
def each
|
|
59
59
|
return enum_for(:each) unless block_given?
|
|
60
60
|
|
|
61
|
-
length.times { |
|
|
61
|
+
length.times { |index| yield self[index] }
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def find_by(selector = nil, value = nil, **criteria)
|
|
65
|
+
index = index_of(selector, value, **criteria)
|
|
66
|
+
|
|
67
|
+
self[index] if index
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def where(selector = nil, value = nil, **criteria)
|
|
71
|
+
indices_of(selector, value, **criteria).map { |index| self[index] }
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def pluck(*fields)
|
|
75
|
+
return [] unless @document
|
|
76
|
+
|
|
77
|
+
all_values = @document.get_value(@selector)
|
|
78
|
+
return [] unless all_values.is_a?(Array)
|
|
79
|
+
|
|
80
|
+
if fields.length == 1
|
|
81
|
+
|
|
82
|
+
all_values.map { |item| item.is_a?(Hash) ? item[fields.first.to_s] : item }
|
|
83
|
+
else
|
|
84
|
+
|
|
85
|
+
all_values.map { |item| fields.map(&:to_s).map { |field| item.is_a?(Hash) ? item[field] : nil } }
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def index_of(selector = nil, value = nil, **criteria)
|
|
90
|
+
if selector && value.nil? && criteria.empty?
|
|
91
|
+
values = @document&.get("#{@selector}[]")
|
|
92
|
+
|
|
93
|
+
return values.index(selector) if values.is_a?(Array)
|
|
94
|
+
|
|
95
|
+
return nil
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
criteria[selector] = value if selector && value
|
|
99
|
+
pairs = expand_nested_criteria(criteria)
|
|
100
|
+
|
|
101
|
+
indices = nil
|
|
102
|
+
|
|
103
|
+
pairs.each do |field, expected|
|
|
104
|
+
field_string = field.to_s
|
|
105
|
+
|
|
106
|
+
if field_string.include?("[]")
|
|
107
|
+
matching = nested_indices_for(field_string, expected)
|
|
108
|
+
else
|
|
109
|
+
values = @document&.get("#{@selector}[].#{field_string}")
|
|
110
|
+
|
|
111
|
+
next unless values.is_a?(Array)
|
|
112
|
+
|
|
113
|
+
matching = values.each_with_index.filter_map { |actual, index| index if actual == expected }
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
indices = indices ? indices & matching : matching
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
indices&.first
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def indices_of(selector = nil, value = nil, **criteria)
|
|
123
|
+
if selector && value.nil? && criteria.empty?
|
|
124
|
+
values = @document&.get("#{@selector}[]")
|
|
125
|
+
|
|
126
|
+
if values.is_a?(Array)
|
|
127
|
+
return values.each_with_index.filter_map { |actual, index| index if actual == selector }
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
return []
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
criteria[selector] = value if selector && value
|
|
134
|
+
pairs = expand_nested_criteria(criteria)
|
|
135
|
+
|
|
136
|
+
indices = nil
|
|
137
|
+
|
|
138
|
+
pairs.each do |field, expected|
|
|
139
|
+
field_string = field.to_s
|
|
140
|
+
|
|
141
|
+
if field_string.include?("[]")
|
|
142
|
+
matching = nested_indices_for(field_string, expected)
|
|
143
|
+
else
|
|
144
|
+
values = @document&.get("#{@selector}[].#{field_string}")
|
|
145
|
+
|
|
146
|
+
next unless values.is_a?(Array)
|
|
147
|
+
|
|
148
|
+
matching = values.each_with_index.filter_map { |actual, index| index if actual == expected }
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
indices = indices ? indices & matching : matching
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
indices || []
|
|
62
155
|
end
|
|
63
156
|
|
|
64
157
|
def length
|
|
@@ -69,6 +162,7 @@ module Yerba
|
|
|
69
162
|
scalar_items.length
|
|
70
163
|
else
|
|
71
164
|
data = @document.get_value(@selector)
|
|
165
|
+
|
|
72
166
|
data.is_a?(Array) ? data.length : 0
|
|
73
167
|
end
|
|
74
168
|
else
|
|
@@ -156,6 +250,79 @@ module Yerba
|
|
|
156
250
|
|
|
157
251
|
private
|
|
158
252
|
|
|
253
|
+
def expand_nested_criteria(criteria)
|
|
254
|
+
expanded = []
|
|
255
|
+
|
|
256
|
+
criteria.each do |field, value|
|
|
257
|
+
if value.is_a?(Hash)
|
|
258
|
+
flatten_hash("#{field}[]", value).each do |path, leaf_value|
|
|
259
|
+
expanded << [path, leaf_value]
|
|
260
|
+
end
|
|
261
|
+
elsif value.is_a?(Array)
|
|
262
|
+
value.each do |item|
|
|
263
|
+
expanded << ["#{field}[]", item]
|
|
264
|
+
end
|
|
265
|
+
else
|
|
266
|
+
expanded << [field, value]
|
|
267
|
+
end
|
|
268
|
+
end
|
|
269
|
+
|
|
270
|
+
expanded
|
|
271
|
+
end
|
|
272
|
+
|
|
273
|
+
def flatten_hash(prefix, hash)
|
|
274
|
+
result = {}
|
|
275
|
+
|
|
276
|
+
hash.each do |key, value|
|
|
277
|
+
path = "#{prefix}.#{key}"
|
|
278
|
+
|
|
279
|
+
if value.is_a?(Hash)
|
|
280
|
+
flatten_hash("#{path}[]", value).each { |nested_path, leaf| result[nested_path] = leaf }
|
|
281
|
+
else
|
|
282
|
+
result[path] = value
|
|
283
|
+
end
|
|
284
|
+
end
|
|
285
|
+
|
|
286
|
+
result
|
|
287
|
+
end
|
|
288
|
+
|
|
289
|
+
def nested_indices_for(field, expected)
|
|
290
|
+
all_values = @document&.get_value(@selector)
|
|
291
|
+
return [] unless all_values.is_a?(Array)
|
|
292
|
+
|
|
293
|
+
all_values.each_with_index.filter_map do |item, index|
|
|
294
|
+
next unless item.is_a?(Hash)
|
|
295
|
+
|
|
296
|
+
nested_values = dig_values(item, field)
|
|
297
|
+
index if nested_values.include?(expected)
|
|
298
|
+
end
|
|
299
|
+
end
|
|
300
|
+
|
|
301
|
+
def dig_values(hash, path)
|
|
302
|
+
parts = path.split(".")
|
|
303
|
+
current = [hash]
|
|
304
|
+
|
|
305
|
+
parts.each do |part|
|
|
306
|
+
next_values = []
|
|
307
|
+
|
|
308
|
+
current.each do |value|
|
|
309
|
+
if part == "[]" && value.is_a?(Array)
|
|
310
|
+
next_values.concat(value)
|
|
311
|
+
elsif part.end_with?("[]")
|
|
312
|
+
key = part.chomp("[]")
|
|
313
|
+
child = value.is_a?(Hash) ? value[key] : nil
|
|
314
|
+
next_values.concat(child) if child.is_a?(Array)
|
|
315
|
+
elsif value.is_a?(Hash)
|
|
316
|
+
next_values << value[part] if value.key?(part)
|
|
317
|
+
end
|
|
318
|
+
end
|
|
319
|
+
|
|
320
|
+
current = next_values
|
|
321
|
+
end
|
|
322
|
+
|
|
323
|
+
current
|
|
324
|
+
end
|
|
325
|
+
|
|
159
326
|
def format_for_insert(value)
|
|
160
327
|
Formatting.quote(value, detect_quote_style)
|
|
161
328
|
end
|
|
@@ -172,6 +339,7 @@ module Yerba
|
|
|
172
339
|
result
|
|
173
340
|
else
|
|
174
341
|
data = @document.get_value(@selector)
|
|
342
|
+
|
|
175
343
|
data.is_a?(Array) ? data : []
|
|
176
344
|
end
|
|
177
345
|
else
|
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.4.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/cbindgen.toml
CHANGED
data/rust/rustfmt.toml
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
tab_spaces = 2
|
|
2
|
-
max_width =
|
|
2
|
+
max_width = 160
|
|
@@ -39,10 +39,7 @@ impl Args {
|
|
|
39
39
|
} else {
|
|
40
40
|
use super::color::*;
|
|
41
41
|
|
|
42
|
-
eprintln!(
|
|
43
|
-
"{RED}Error:{RESET} expected a number for blank line count, got '{}'",
|
|
44
|
-
self.first
|
|
45
|
-
);
|
|
42
|
+
eprintln!("{RED}Error:{RESET} expected a number for blank line count, got '{}'", self.first);
|
|
46
43
|
|
|
47
44
|
std::process::exit(1);
|
|
48
45
|
};
|
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
|
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
use std::sync::LazyLock;
|
|
2
|
+
|
|
3
|
+
use indoc::indoc;
|
|
4
|
+
|
|
5
|
+
use super::colorize_examples;
|
|
6
|
+
use super::{output, parse_file, resolve_files};
|
|
7
|
+
|
|
8
|
+
static EXAMPLES: LazyLock<String> = LazyLock::new(|| {
|
|
9
|
+
colorize_examples(indoc! {r#"
|
|
10
|
+
yerba directives config.yml --ensure
|
|
11
|
+
yerba directives config.yml --remove
|
|
12
|
+
yerba directives "data/**/*.yml" --ensure
|
|
13
|
+
yerba directives config.yml --ensure --dry-run
|
|
14
|
+
"#})
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
#[derive(clap::Args)]
|
|
18
|
+
#[command(
|
|
19
|
+
about = "Add or remove the document start marker (---)",
|
|
20
|
+
arg_required_else_help = true,
|
|
21
|
+
after_help = EXAMPLES.as_str()
|
|
22
|
+
)]
|
|
23
|
+
pub struct Args {
|
|
24
|
+
file: String,
|
|
25
|
+
/// Add --- if missing
|
|
26
|
+
#[arg(long)]
|
|
27
|
+
ensure: bool,
|
|
28
|
+
/// Remove --- if present
|
|
29
|
+
#[arg(long)]
|
|
30
|
+
remove: bool,
|
|
31
|
+
#[arg(long)]
|
|
32
|
+
dry_run: bool,
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
impl Args {
|
|
36
|
+
pub fn run(self) {
|
|
37
|
+
if !self.ensure && !self.remove {
|
|
38
|
+
use super::color::*;
|
|
39
|
+
eprintln!("{RED}Error:{RESET} specify --ensure or --remove");
|
|
40
|
+
std::process::exit(1);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if self.ensure && self.remove {
|
|
44
|
+
use super::color::*;
|
|
45
|
+
eprintln!("{RED}Error:{RESET} --ensure and --remove are mutually exclusive");
|
|
46
|
+
std::process::exit(1);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
for resolved_file in resolve_files(&self.file) {
|
|
50
|
+
let mut document = parse_file(&resolved_file);
|
|
51
|
+
|
|
52
|
+
if self.ensure {
|
|
53
|
+
let _ = document.ensure_directives();
|
|
54
|
+
} else if self.remove {
|
|
55
|
+
let _ = document.remove_directives();
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
output(&resolved_file, &document, self.dry_run);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
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,42 @@ 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!("{RED}Error:{RESET} selector \"{}\" not found in {}", self.selector, resolved_file);
|
|
81
|
+
|
|
82
|
+
show_similar_selectors(resolved_file, &document, &self.selector);
|
|
83
|
+
process::exit(1);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if let Some(fields) = &select_fields {
|
|
87
|
+
for field in fields {
|
|
88
|
+
let field_trimmed = field.trim().trim_start_matches('.');
|
|
89
|
+
let full_selector = if search_path_string.is_empty() {
|
|
90
|
+
field_trimmed.to_string()
|
|
91
|
+
} else {
|
|
92
|
+
format!("{}.{}", search_path_string, field_trimmed)
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
if !document.exists(&full_selector) {
|
|
96
|
+
use super::color::*;
|
|
97
|
+
eprintln!("{RED}Error:{RESET} select field \"{}\" not found in {}", field.trim(), resolved_file);
|
|
98
|
+
show_similar_selectors(resolved_file, &document, &full_selector);
|
|
99
|
+
process::exit(1);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
70
103
|
|
|
71
104
|
let values: Vec<serde_yaml::Value> = if let Some(condition) = &normalized_condition {
|
|
72
105
|
document.filter(&search_path_string, condition)
|
|
@@ -74,16 +107,6 @@ impl Args {
|
|
|
74
107
|
document.get_values(&search_path_string)
|
|
75
108
|
};
|
|
76
109
|
|
|
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
110
|
for value in values {
|
|
88
111
|
if let Some(field) = &extract_field {
|
|
89
112
|
let field_string = field.to_selector_string();
|
|
@@ -121,6 +144,19 @@ impl Args {
|
|
|
121
144
|
}
|
|
122
145
|
}
|
|
123
146
|
|
|
147
|
+
if is_glob && all_results.is_empty() && normalized_condition.is_none() {
|
|
148
|
+
use super::color::*;
|
|
149
|
+
|
|
150
|
+
eprintln!(
|
|
151
|
+
"{RED}Error:{RESET} selector \"{}\" not found in any of the {} files matching \"{}\"",
|
|
152
|
+
self.selector,
|
|
153
|
+
files.len(),
|
|
154
|
+
self.file
|
|
155
|
+
);
|
|
156
|
+
|
|
157
|
+
process::exit(1);
|
|
158
|
+
}
|
|
159
|
+
|
|
124
160
|
if self.raw {
|
|
125
161
|
for value in &all_results {
|
|
126
162
|
match value {
|
|
@@ -132,23 +168,13 @@ impl Args {
|
|
|
132
168
|
}
|
|
133
169
|
}
|
|
134
170
|
} else if all_results.len() == 1 {
|
|
135
|
-
println!(
|
|
136
|
-
"{}",
|
|
137
|
-
serde_json::to_string_pretty(&all_results[0]).unwrap_or_else(|_| "null".to_string())
|
|
138
|
-
);
|
|
171
|
+
println!("{}", serde_json::to_string_pretty(&all_results[0]).unwrap_or_else(|_| "null".to_string()));
|
|
139
172
|
} else {
|
|
140
|
-
println!(
|
|
141
|
-
"{}",
|
|
142
|
-
serde_json::to_string_pretty(&all_results).unwrap_or_else(|_| "[]".to_string())
|
|
143
|
-
);
|
|
173
|
+
println!("{}", serde_json::to_string_pretty(&all_results).unwrap_or_else(|_| "[]".to_string()));
|
|
144
174
|
}
|
|
145
175
|
}
|
|
146
176
|
|
|
147
|
-
fn resolve_search_scope(
|
|
148
|
-
&self,
|
|
149
|
-
selector: &yerba::Selector,
|
|
150
|
-
condition_path: Option<&yerba::Selector>,
|
|
151
|
-
) -> (yerba::Selector, Option<yerba::Selector>) {
|
|
177
|
+
fn resolve_search_scope(&self, selector: &yerba::Selector, condition_path: Option<&yerba::Selector>) -> (yerba::Selector, Option<yerba::Selector>) {
|
|
152
178
|
if let Some(condition) = condition_path {
|
|
153
179
|
if condition.is_relative() {
|
|
154
180
|
let (container, field) = selector.split_at_last_bracket();
|
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
|
@@ -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;
|
|
@@ -11,6 +12,7 @@ pub mod move_key;
|
|
|
11
12
|
pub mod quote_style;
|
|
12
13
|
pub mod remove;
|
|
13
14
|
pub mod rename;
|
|
15
|
+
pub mod selectors;
|
|
14
16
|
pub mod set;
|
|
15
17
|
pub mod sort;
|
|
16
18
|
pub mod sort_keys;
|
|
@@ -125,10 +127,7 @@ pub(crate) fn colorize_help(input: &str) -> String {
|
|
|
125
127
|
}
|
|
126
128
|
|
|
127
129
|
if columns.len() == 3 {
|
|
128
|
-
output.push_str(&format!(
|
|
129
|
-
" \x1b[36m{:<20}{RESET} {:<21} {DIM}{}{RESET}\n",
|
|
130
|
-
columns[0], columns[1], columns[2]
|
|
131
|
-
));
|
|
130
|
+
output.push_str(&format!(" \x1b[36m{:<20}{RESET} {:<21} {DIM}{}{RESET}\n", columns[0], columns[1], columns[2]));
|
|
132
131
|
} else if columns.len() == 2 {
|
|
133
132
|
output.push_str(&format!(" \x1b[36m{:<20}{RESET} {}\n", columns[0], columns[1]));
|
|
134
133
|
} else {
|
|
@@ -153,6 +152,8 @@ pub enum Command {
|
|
|
153
152
|
Sort(sort::Args),
|
|
154
153
|
QuoteStyle(quote_style::Args),
|
|
155
154
|
BlankLines(blank_lines::Args),
|
|
155
|
+
Directives(directives::Args),
|
|
156
|
+
Selectors(selectors::Args),
|
|
156
157
|
#[command(about = "Create a new Yerbafile in the current directory")]
|
|
157
158
|
Init,
|
|
158
159
|
#[command(about = "Apply all rules from the Yerbafile and write changes")]
|
|
@@ -180,6 +181,8 @@ impl Command {
|
|
|
180
181
|
Command::Sort(args) => args.run(),
|
|
181
182
|
Command::QuoteStyle(args) => args.run(),
|
|
182
183
|
Command::BlankLines(args) => args.run(),
|
|
184
|
+
Command::Directives(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,72 @@ 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(file: &str, document: &yerba::Document, result: Result<(), yerba::YerbaError>, hint: Option<&str>) {
|
|
331
338
|
use color::*;
|
|
332
339
|
|
|
333
|
-
|
|
334
|
-
|
|
340
|
+
if let Err(error) = result {
|
|
341
|
+
if let yerba::YerbaError::SelectorNotFound(selector) = &error {
|
|
342
|
+
eprintln!("{RED}Error:{RESET} selector \"{selector}\" not found in {file}");
|
|
343
|
+
|
|
344
|
+
show_similar_selectors(file, document, selector);
|
|
345
|
+
} else {
|
|
346
|
+
eprintln!("{RED}Error:{RESET} {}", error);
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
if let Some(hint) = hint {
|
|
350
|
+
if matches!(error, yerba::YerbaError::SelectorNotFound(_)) {
|
|
351
|
+
eprintln!();
|
|
352
|
+
eprintln!(" {DIM}Hint: {hint}{RESET}");
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
|
|
335
356
|
process::exit(1);
|
|
336
|
-
}
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
pub(crate) fn show_similar_selectors(file: &str, document: &yerba::Document, invalid_path: &str) {
|
|
361
|
+
use color::*;
|
|
362
|
+
use yerba::didyoumean::didyoumean_ranked;
|
|
363
|
+
|
|
364
|
+
let selectors = document.selectors();
|
|
365
|
+
|
|
366
|
+
if selectors.is_empty() {
|
|
367
|
+
return;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
let query = invalid_path.split_whitespace().next().unwrap_or(invalid_path);
|
|
371
|
+
let threshold = query.len() / 2 + 3;
|
|
372
|
+
let close = didyoumean_ranked(query, &selectors, threshold);
|
|
373
|
+
|
|
374
|
+
eprintln!();
|
|
375
|
+
|
|
376
|
+
if close.is_empty() {
|
|
377
|
+
eprintln!(" {BOLD}Available selectors in {file}:{RESET}");
|
|
378
|
+
|
|
379
|
+
for selector in selectors.iter().take(10) {
|
|
380
|
+
eprintln!(" {DIM}{selector}{RESET}");
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
if selectors.len() > 10 {
|
|
384
|
+
eprintln!(" {DIM}... and {} more{RESET}", selectors.len() - 10);
|
|
385
|
+
}
|
|
386
|
+
} else if close.len() == 1 {
|
|
387
|
+
eprintln!(" {BOLD}Did you mean this selector?{RESET} {}", close[0]);
|
|
388
|
+
} else {
|
|
389
|
+
eprintln!(" {BOLD}Did you mean one of these selectors?{RESET}");
|
|
390
|
+
|
|
391
|
+
for selector in close.iter().take(5) {
|
|
392
|
+
eprintln!(" {selector}");
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
eprintln!();
|
|
397
|
+
eprintln!(" {BOLD}To see all valid selectors, run:{RESET}");
|
|
398
|
+
eprintln!(" yerba selectors \"{file}\"{RESET}");
|
|
337
399
|
}
|
|
338
400
|
|
|
339
401
|
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
|
}
|