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