yerba 0.3.0-arm-linux-gnu → 0.4.1-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.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +93 -8
  3. data/exe/arm-linux-gnu/yerba +0 -0
  4. data/ext/yerba/extconf.rb +22 -3
  5. data/ext/yerba/include/yerba.h +32 -9
  6. data/ext/yerba/yerba.c +133 -25
  7. data/lib/yerba/3.2/yerba.so +0 -0
  8. data/lib/yerba/3.3/yerba.so +0 -0
  9. data/lib/yerba/3.4/yerba.so +0 -0
  10. data/lib/yerba/4.0/yerba.so +0 -0
  11. data/lib/yerba/collection.rb +35 -0
  12. data/lib/yerba/document.rb +45 -3
  13. data/lib/yerba/query_result.rb +50 -0
  14. data/lib/yerba/sequence.rb +187 -1
  15. data/lib/yerba/version.rb +1 -1
  16. data/lib/yerba/yerbafile.rb +45 -0
  17. data/lib/yerba.rb +2 -0
  18. data/rust/Cargo.lock +3 -1
  19. data/rust/Cargo.toml +3 -2
  20. data/rust/cbindgen.toml +1 -0
  21. data/rust/rustfmt.toml +1 -1
  22. data/rust/src/commands/apply.rs +11 -2
  23. data/rust/src/commands/blank_lines.rs +1 -4
  24. data/rust/src/commands/check.rs +11 -2
  25. data/rust/src/commands/directives.rs +61 -0
  26. data/rust/src/commands/get.rs +5 -22
  27. data/rust/src/commands/mod.rs +16 -18
  28. data/rust/src/commands/quote_style.rs +12 -6
  29. data/rust/src/commands/selectors.rs +3 -19
  30. data/rust/src/commands/sort.rs +22 -157
  31. data/rust/src/didyoumean.rs +2 -4
  32. data/rust/src/document/condition.rs +139 -0
  33. data/rust/src/document/delete.rs +91 -0
  34. data/rust/src/document/get.rs +262 -0
  35. data/rust/src/document/insert.rs +384 -0
  36. data/rust/src/document/mod.rs +784 -0
  37. data/rust/src/document/set.rs +100 -0
  38. data/rust/src/document/sort.rs +639 -0
  39. data/rust/src/document/style.rs +473 -0
  40. data/rust/src/error.rs +24 -6
  41. data/rust/src/ffi.rs +272 -518
  42. data/rust/src/json.rs +1 -7
  43. data/rust/src/lib.rs +88 -2
  44. data/rust/src/main.rs +2 -0
  45. data/rust/src/quote_style.rs +83 -7
  46. data/rust/src/selector.rs +2 -7
  47. data/rust/src/syntax.rs +41 -21
  48. data/rust/src/yerbafile.rs +86 -19
  49. metadata +13 -3
  50. data/rust/src/document.rs +0 -2304
data/rust/src/document.rs DELETED
@@ -1,2304 +0,0 @@
1
- use std::fs;
2
- use std::path::{Path, PathBuf};
3
-
4
- use rowan::ast::AstNode;
5
- use rowan::TextRange;
6
-
7
- use yaml_parser::ast::{BlockMap, BlockSeq, Root};
8
- use yaml_parser::{SyntaxKind, SyntaxNode, SyntaxToken};
9
-
10
- use crate::error::YerbaError;
11
- use crate::QuoteStyle;
12
-
13
- use crate::syntax::{
14
- extract_scalar, extract_scalar_text, find_entry_by_key, find_scalar_token, format_scalar_value, is_map_key,
15
- is_yaml_non_string, preceding_whitespace_indent, preceding_whitespace_token, removal_range, unescape_double_quoted,
16
- unescape_single_quoted, ScalarValue,
17
- };
18
-
19
- #[derive(Debug, Clone)]
20
- pub struct SortField {
21
- pub path: String,
22
- pub ascending: bool,
23
- }
24
-
25
- impl SortField {
26
- pub fn asc(path: &str) -> Self {
27
- SortField {
28
- path: path.to_string(),
29
- ascending: true,
30
- }
31
- }
32
-
33
- pub fn desc(path: &str) -> Self {
34
- SortField {
35
- path: path.to_string(),
36
- ascending: false,
37
- }
38
- }
39
-
40
- pub fn parse(input: &str) -> Self {
41
- if let Some((path, direction)) = input.rsplit_once(':') {
42
- match direction {
43
- "desc" | "descending" => SortField::desc(path),
44
- _ => SortField::asc(input),
45
- }
46
- } else {
47
- SortField::asc(input)
48
- }
49
- }
50
-
51
- pub fn parse_list(input: &str) -> Vec<Self> {
52
- input.split(',').map(|field| SortField::parse(field.trim())).collect()
53
- }
54
- }
55
-
56
- #[derive(Debug, Clone)]
57
- pub enum InsertPosition {
58
- At(usize),
59
- Last,
60
- Before(String),
61
- After(String),
62
- BeforeCondition(String),
63
- AfterCondition(String),
64
- FromSortOrder(Vec<String>),
65
- }
66
-
67
- #[derive(Debug)]
68
- pub struct Document {
69
- root: SyntaxNode,
70
- path: Option<PathBuf>,
71
- }
72
-
73
- impl Document {
74
- pub fn parse(source: &str) -> Result<Self, YerbaError> {
75
- let tree = yaml_parser::parse(source).map_err(|error| YerbaError::ParseError(format!("{}", error)))?;
76
-
77
- Ok(Document { root: tree, path: None })
78
- }
79
-
80
- pub fn parse_file(path: impl AsRef<Path>) -> Result<Self, YerbaError> {
81
- let path = path.as_ref();
82
- let source = fs::read_to_string(path)?;
83
-
84
- let mut document = Self::parse(&source)?;
85
-
86
- document.path = Some(path.to_path_buf());
87
-
88
- Ok(document)
89
- }
90
-
91
- pub fn get(&self, dot_path: &str) -> Option<String> {
92
- if dot_path.contains('[') {
93
- return self.get_all(dot_path).into_iter().next();
94
- }
95
-
96
- let current_node = self.navigate(dot_path).ok()?;
97
-
98
- extract_scalar_text(&current_node)
99
- }
100
-
101
- pub fn get_all(&self, dot_path: &str) -> Vec<String> {
102
- self
103
- .navigate_all(dot_path)
104
- .iter()
105
- .filter_map(extract_scalar_text)
106
- .collect()
107
- }
108
-
109
- pub fn get_typed(&self, dot_path: &str) -> Option<ScalarValue> {
110
- if crate::selector::Selector::parse(dot_path).has_wildcard() {
111
- return self.get_all_typed(dot_path).into_iter().next();
112
- }
113
-
114
- let current_node = self.navigate(dot_path).ok()?;
115
-
116
- if current_node
117
- .descendants()
118
- .any(|child| child.kind() == SyntaxKind::BLOCK_MAP || child.kind() == SyntaxKind::BLOCK_SEQ)
119
- {
120
- return None;
121
- }
122
-
123
- extract_scalar(&current_node)
124
- }
125
-
126
- pub fn get_all_typed(&self, dot_path: &str) -> Vec<ScalarValue> {
127
- self
128
- .navigate_all(dot_path)
129
- .iter()
130
- .filter(|node| {
131
- !node
132
- .descendants()
133
- .any(|child| child.kind() == SyntaxKind::BLOCK_MAP || child.kind() == SyntaxKind::BLOCK_SEQ)
134
- })
135
- .filter_map(extract_scalar)
136
- .collect()
137
- }
138
-
139
- pub fn get_value(&self, dot_path: &str) -> Option<serde_yaml::Value> {
140
- if dot_path.is_empty() {
141
- return Some(node_to_yaml_value(&self.root));
142
- }
143
-
144
- let nodes = self.navigate_all(dot_path);
145
-
146
- if nodes.is_empty() {
147
- return None;
148
- }
149
-
150
- if nodes.len() == 1 {
151
- return Some(node_to_yaml_value(&nodes[0]));
152
- }
153
-
154
- let values: Vec<serde_yaml::Value> = nodes.iter().map(node_to_yaml_value).collect();
155
-
156
- Some(serde_yaml::Value::Sequence(values))
157
- }
158
-
159
- pub fn get_values(&self, dot_path: &str) -> Vec<serde_yaml::Value> {
160
- self.navigate_all(dot_path).iter().map(node_to_yaml_value).collect()
161
- }
162
-
163
- pub fn filter(&self, dot_path: &str, condition: &str) -> Vec<serde_yaml::Value> {
164
- self
165
- .navigate_all(dot_path)
166
- .iter()
167
- .filter(|node| self.evaluate_condition_on_node(node, condition))
168
- .map(node_to_yaml_value)
169
- .collect()
170
- }
171
-
172
- pub fn selectors(&self) -> Vec<String> {
173
- let Some(value) = self.get_value("") else {
174
- return Vec::new();
175
- };
176
-
177
- let mut selectors = Vec::new();
178
- collect_selectors(&value, "", &mut selectors);
179
- selectors.sort();
180
- selectors.dedup();
181
- selectors
182
- }
183
-
184
- fn evaluate_condition_on_node(&self, node: &SyntaxNode, condition: &str) -> bool {
185
- let condition = condition.trim();
186
-
187
- let (left, operator, right) = match parse_condition(condition) {
188
- Some(parts) => parts,
189
- None => return false,
190
- };
191
-
192
- let path = crate::selector::Selector::parse(&left);
193
-
194
- if !path.is_relative() {
195
- return false;
196
- }
197
-
198
- let target_nodes = navigate_from_node(node, &path.to_selector_string());
199
- let values: Vec<String> = target_nodes.iter().filter_map(extract_scalar_text).collect();
200
-
201
- match operator {
202
- "==" => values.iter().any(|value| value == &right),
203
- "!=" => values.iter().all(|value| value != &right),
204
- "contains" => {
205
- if values.iter().any(|value| value == &right || value.contains(&right)) {
206
- return true;
207
- }
208
-
209
- for node in &target_nodes {
210
- if let Some(sequence) = node.descendants().find_map(BlockSeq::cast) {
211
- for entry in sequence.entries() {
212
- if let Some(text) = entry.flow().and_then(|flow| extract_scalar_text(flow.syntax())) {
213
- if text == right {
214
- return true;
215
- }
216
- }
217
- }
218
- }
219
- }
220
-
221
- false
222
- }
223
- "not_contains" => {
224
- for node in &target_nodes {
225
- if let Some(sequence) = node.descendants().find_map(BlockSeq::cast) {
226
- for entry in sequence.entries() {
227
- if let Some(text) = entry.flow().and_then(|flow| extract_scalar_text(flow.syntax())) {
228
- if text == right {
229
- return false;
230
- }
231
- }
232
- }
233
- }
234
- }
235
-
236
- !values.iter().any(|value| value == &right || value.contains(&right))
237
- }
238
- _ => false,
239
- }
240
- }
241
-
242
- pub fn exists(&self, dot_path: &str) -> bool {
243
- if dot_path.contains('[') {
244
- return !self.navigate_all(dot_path).is_empty();
245
- }
246
-
247
- self.get(dot_path).is_some()
248
- }
249
-
250
- pub fn evaluate_condition(&self, parent_path: &str, condition: &str) -> bool {
251
- let condition = condition.trim();
252
-
253
- let (left, operator, right) = match parse_condition(condition) {
254
- Some(parts) => parts,
255
- None => return false,
256
- };
257
-
258
- let path = crate::selector::Selector::parse(&left);
259
-
260
- let full_path = if path.is_relative() {
261
- let path_string = path.to_selector_string();
262
-
263
- if parent_path.is_empty() {
264
- path_string
265
- } else {
266
- format!("{}.{}", parent_path, path_string)
267
- }
268
- } else {
269
- path.to_selector_string()
270
- };
271
-
272
- let has_brackets = crate::selector::Selector::parse(&full_path).has_brackets();
273
-
274
- match operator {
275
- "==" => {
276
- if has_brackets {
277
- self.get_all(&full_path).iter().any(|value| value == &right)
278
- } else {
279
- self.get(&full_path).unwrap_or_default() == right
280
- }
281
- }
282
- "!=" => {
283
- if has_brackets {
284
- self.get_all(&full_path).iter().all(|value| value != &right)
285
- } else {
286
- self.get(&full_path).unwrap_or_default() != right
287
- }
288
- }
289
- "contains" => {
290
- if has_brackets {
291
- self
292
- .get_all(&full_path)
293
- .iter()
294
- .any(|value| value == &right || value.contains(&right))
295
- } else {
296
- let items = self.get_sequence_values(&full_path);
297
-
298
- if !items.is_empty() {
299
- items.iter().any(|item| item == &right)
300
- } else {
301
- self
302
- .get(&full_path)
303
- .map(|value| value.contains(&right))
304
- .unwrap_or(false)
305
- }
306
- }
307
- }
308
- "not_contains" => {
309
- if has_brackets {
310
- self
311
- .get_all(&full_path)
312
- .iter()
313
- .all(|value| value != &right && !value.contains(&right))
314
- } else {
315
- let items = self.get_sequence_values(&full_path);
316
-
317
- if !items.is_empty() {
318
- !items.iter().any(|item| item == &right)
319
- } else {
320
- self
321
- .get(&full_path)
322
- .map(|value| !value.contains(&right))
323
- .unwrap_or(true)
324
- }
325
- }
326
- }
327
- _ => false,
328
- }
329
- }
330
-
331
- pub fn get_sequence_values(&self, dot_path: &str) -> Vec<String> {
332
- let current_node = match self.navigate(dot_path) {
333
- Ok(node) => node,
334
- Err(_) => return Vec::new(),
335
- };
336
-
337
- let sequence = match current_node.descendants().find_map(BlockSeq::cast) {
338
- Some(sequence) => sequence,
339
- None => return Vec::new(),
340
- };
341
-
342
- sequence
343
- .entries()
344
- .filter_map(|entry| entry.flow().and_then(|flow| extract_scalar_text(flow.syntax())))
345
- .collect()
346
- }
347
-
348
- pub fn set(&mut self, dot_path: &str, value: &str) -> Result<(), YerbaError> {
349
- let current_node = self.navigate(dot_path)?;
350
-
351
- if let Some(block_scalar) = current_node
352
- .descendants()
353
- .find(|node| node.kind() == SyntaxKind::BLOCK_SCALAR)
354
- {
355
- let new_text = if value.is_empty() {
356
- "\"\"".to_string()
357
- } else if value.contains('\n') {
358
- format!("|-\n {}", value.replace('\n', "\n "))
359
- } else {
360
- format!("\"{}\"", value.replace('"', "\\\""))
361
- };
362
-
363
- let range = block_scalar.text_range();
364
-
365
- return self.apply_edit(range, &new_text);
366
- }
367
-
368
- let scalar_token =
369
- find_scalar_token(&current_node).ok_or_else(|| YerbaError::SelectorNotFound(dot_path.to_string()))?;
370
-
371
- let new_text = format_scalar_value(value, scalar_token.kind());
372
-
373
- self.replace_token(&scalar_token, &new_text)
374
- }
375
-
376
- pub fn set_all(&mut self, dot_path: &str, value: &str) -> Result<(), YerbaError> {
377
- let nodes = self.navigate_all(dot_path);
378
-
379
- if nodes.is_empty() {
380
- return Err(YerbaError::SelectorNotFound(dot_path.to_string()));
381
- }
382
-
383
- for node in nodes.into_iter().rev() {
384
- if let Some(scalar_token) = find_scalar_token(&node) {
385
- let new_text = format_scalar_value(value, scalar_token.kind());
386
-
387
- self.replace_token(&scalar_token, &new_text)?;
388
- }
389
- }
390
-
391
- Ok(())
392
- }
393
-
394
- pub fn set_scalar_style(&mut self, dot_path: &str, style: &QuoteStyle) -> Result<(), YerbaError> {
395
- let current_node = self.navigate(dot_path)?;
396
- let scalar_token =
397
- find_scalar_token(&current_node).ok_or_else(|| YerbaError::SelectorNotFound(dot_path.to_string()))?;
398
-
399
- let current_kind = scalar_token.kind();
400
- let target_kind = style.to_syntax_kind();
401
-
402
- if current_kind == target_kind {
403
- return Ok(());
404
- }
405
-
406
- let raw_value = match current_kind {
407
- SyntaxKind::DOUBLE_QUOTED_SCALAR => {
408
- let text = scalar_token.text();
409
- unescape_double_quoted(&text[1..text.len() - 1])
410
- }
411
-
412
- SyntaxKind::SINGLE_QUOTED_SCALAR => {
413
- let text = scalar_token.text();
414
- unescape_single_quoted(&text[1..text.len() - 1])
415
- }
416
-
417
- SyntaxKind::PLAIN_SCALAR => scalar_token.text().to_string(),
418
-
419
- _ => return Ok(()),
420
- };
421
-
422
- let new_text = format_scalar_value(&raw_value, target_kind);
423
-
424
- self.replace_token(&scalar_token, &new_text)
425
- }
426
-
427
- pub fn set_plain(&mut self, dot_path: &str, value: &str) -> Result<(), YerbaError> {
428
- let current_node = self.navigate(dot_path)?;
429
-
430
- if let Some(block_scalar) = current_node
431
- .descendants()
432
- .find(|node| node.kind() == SyntaxKind::BLOCK_SCALAR)
433
- {
434
- let range = block_scalar.text_range();
435
- return self.apply_edit(range, value);
436
- }
437
-
438
- let scalar_token =
439
- find_scalar_token(&current_node).ok_or_else(|| YerbaError::SelectorNotFound(dot_path.to_string()))?;
440
-
441
- self.replace_token(&scalar_token, value)
442
- }
443
-
444
- pub fn append(&mut self, dot_path: &str, value: &str) -> Result<(), YerbaError> {
445
- self.insert_into(dot_path, value, InsertPosition::Last)
446
- }
447
-
448
- pub fn insert_into(&mut self, dot_path: &str, value: &str, position: InsertPosition) -> Result<(), YerbaError> {
449
- Self::validate_path(dot_path)?;
450
-
451
- if let Ok(current_node) = self.navigate(dot_path) {
452
- if current_node.descendants().find_map(BlockSeq::cast).is_some() {
453
- return self.insert_sequence_item(dot_path, value, position);
454
- }
455
- }
456
-
457
- let (parent_path, key) = dot_path.rsplit_once('.').unwrap_or(("", dot_path));
458
-
459
- self.insert_map_key(parent_path, key, value, position)
460
- }
461
-
462
- fn insert_sequence_item(&mut self, dot_path: &str, value: &str, position: InsertPosition) -> Result<(), YerbaError> {
463
- let current_node = self.navigate(dot_path)?;
464
-
465
- let sequence = current_node
466
- .descendants()
467
- .find_map(BlockSeq::cast)
468
- .ok_or_else(|| YerbaError::NotASequence(dot_path.to_string()))?;
469
-
470
- let entries: Vec<_> = sequence.entries().collect();
471
-
472
- if entries.is_empty() {
473
- return Err(YerbaError::SelectorNotFound(dot_path.to_string()));
474
- }
475
-
476
- let indent = entries
477
- .get(1)
478
- .or(entries.first())
479
- .map(|entry| preceding_whitespace_indent(entry.syntax()))
480
- .unwrap_or_default();
481
-
482
- let new_item = if value.contains('\n') {
483
- let item_indent = format!("{} ", indent);
484
- let lines: Vec<&str> = value.split('\n').collect();
485
-
486
- let min_indent = lines
487
- .iter()
488
- .skip(1)
489
- .filter(|line| !line.trim().is_empty())
490
- .map(|line| line.len() - line.trim_start().len())
491
- .min()
492
- .unwrap_or(0);
493
-
494
- let indented: Vec<String> = lines
495
- .iter()
496
- .enumerate()
497
- .map(|(index, line)| {
498
- if index == 0 {
499
- line.to_string()
500
- } else if line.trim().is_empty() {
501
- String::new()
502
- } else {
503
- let relative = &line[min_indent..];
504
- format!("{}{}", item_indent, relative)
505
- }
506
- })
507
- .collect();
508
-
509
- format!("- {}", indented.join("\n"))
510
- } else {
511
- format!("- {}", value)
512
- };
513
-
514
- match position {
515
- InsertPosition::Last => {
516
- let last_entry = entries.last().unwrap();
517
- let new_text = format!("\n{}{}", indent, new_item);
518
-
519
- self.insert_after_node(last_entry.syntax(), &new_text)
520
- }
521
-
522
- InsertPosition::At(index) => {
523
- if index >= entries.len() {
524
- let last_entry = entries.last().unwrap();
525
- let new_text = format!("\n{}{}", indent, new_item);
526
-
527
- self.insert_after_node(last_entry.syntax(), &new_text)
528
- } else {
529
- let target_entry = &entries[index];
530
- let target_range = target_entry.syntax().text_range();
531
- let replacement = format!("{}\n{}", new_item, indent);
532
- let insert_range = TextRange::new(target_range.start(), target_range.start());
533
-
534
- self.apply_edit(insert_range, &replacement)
535
- }
536
- }
537
-
538
- InsertPosition::Before(target_value) => {
539
- let target_entry = entries
540
- .iter()
541
- .find(|entry| {
542
- entry
543
- .flow()
544
- .and_then(|flow| extract_scalar_text(flow.syntax()))
545
- .map(|text| text == target_value)
546
- .unwrap_or(false)
547
- })
548
- .ok_or_else(|| YerbaError::SelectorNotFound(format!("{} item '{}'", dot_path, target_value)))?;
549
-
550
- let target_range = target_entry.syntax().text_range();
551
- let replacement = format!("{}\n{}", new_item, indent);
552
- let insert_range = TextRange::new(target_range.start(), target_range.start());
553
-
554
- self.apply_edit(insert_range, &replacement)
555
- }
556
-
557
- InsertPosition::After(target_value) => {
558
- let target_entry = entries
559
- .iter()
560
- .find(|entry| {
561
- entry
562
- .flow()
563
- .and_then(|flow| extract_scalar_text(flow.syntax()))
564
- .map(|text| text == target_value)
565
- .unwrap_or(false)
566
- })
567
- .ok_or_else(|| YerbaError::SelectorNotFound(format!("{} item '{}'", dot_path, target_value)))?;
568
-
569
- let new_text = format!("\n{}{}", indent, new_item);
570
-
571
- self.insert_after_node(target_entry.syntax(), &new_text)
572
- }
573
-
574
- InsertPosition::BeforeCondition(condition) => {
575
- let target_entry = entries
576
- .iter()
577
- .find(|entry| self.evaluate_condition_on_node(entry.syntax(), &condition))
578
- .ok_or_else(|| YerbaError::SelectorNotFound(format!("{} condition '{}'", dot_path, condition)))?;
579
-
580
- let target_range = target_entry.syntax().text_range();
581
- let replacement = format!("{}\n{}", new_item, indent);
582
- let insert_range = TextRange::new(target_range.start(), target_range.start());
583
-
584
- self.apply_edit(insert_range, &replacement)
585
- }
586
-
587
- InsertPosition::AfterCondition(condition) => {
588
- let target_entry = entries
589
- .iter()
590
- .find(|entry| self.evaluate_condition_on_node(entry.syntax(), &condition))
591
- .ok_or_else(|| YerbaError::SelectorNotFound(format!("{} condition '{}'", dot_path, condition)))?;
592
-
593
- let new_text = format!("\n{}{}", indent, new_item);
594
-
595
- self.insert_after_node(target_entry.syntax(), &new_text)
596
- }
597
-
598
- InsertPosition::FromSortOrder(_) => {
599
- let last_entry = entries.last().unwrap();
600
- let new_text = format!("\n{}{}", indent, new_item);
601
-
602
- self.insert_after_node(last_entry.syntax(), &new_text)
603
- }
604
- }
605
- }
606
-
607
- fn insert_map_key(
608
- &mut self,
609
- dot_path: &str,
610
- key: &str,
611
- value: &str,
612
- position: InsertPosition,
613
- ) -> Result<(), YerbaError> {
614
- let current_node = self.navigate(dot_path)?;
615
-
616
- let map = current_node
617
- .descendants()
618
- .find_map(BlockMap::cast)
619
- .ok_or_else(|| YerbaError::SelectorNotFound(dot_path.to_string()))?;
620
-
621
- let entries: Vec<_> = map.entries().collect();
622
-
623
- if entries.is_empty() {
624
- let indent = preceding_whitespace_indent(map.syntax());
625
- let new_entry = format!("\n{}{}: {}", indent, key, value);
626
-
627
- return self.insert_after_node(map.syntax(), &new_entry);
628
- }
629
-
630
- if find_entry_by_key(&map, key).is_some() {
631
- return Err(YerbaError::ParseError(format!(
632
- "key '{}' already exists at '{}'",
633
- key, dot_path
634
- )));
635
- }
636
-
637
- let indent = entries
638
- .get(1)
639
- .or(entries.first())
640
- .map(|entry| preceding_whitespace_indent(entry.syntax()))
641
- .unwrap_or_default();
642
-
643
- let new_entry_text = format!("{}: {}", key, value);
644
-
645
- match position {
646
- InsertPosition::Last => {
647
- let last_entry = entries.last().unwrap();
648
- let new_text = format!("\n{}{}", indent, new_entry_text);
649
-
650
- self.insert_after_node(last_entry.syntax(), &new_text)
651
- }
652
-
653
- InsertPosition::At(index) => {
654
- if index >= entries.len() {
655
- let last_entry = entries.last().unwrap();
656
- let new_text = format!("\n{}{}", indent, new_entry_text);
657
-
658
- self.insert_after_node(last_entry.syntax(), &new_text)
659
- } else {
660
- let target_entry = &entries[index];
661
- let target_range = target_entry.syntax().text_range();
662
- let replacement = format!("{}\n{}", new_entry_text, indent);
663
- let insert_range = TextRange::new(target_range.start(), target_range.start());
664
-
665
- self.apply_edit(insert_range, &replacement)
666
- }
667
- }
668
-
669
- InsertPosition::Before(target_key) => {
670
- let target_entry = find_entry_by_key(&map, &target_key)
671
- .ok_or_else(|| YerbaError::SelectorNotFound(format!("{}.{}", dot_path, target_key)))?;
672
-
673
- let target_range = target_entry.syntax().text_range();
674
- let replacement = format!("{}\n{}", new_entry_text, indent);
675
- let insert_range = TextRange::new(target_range.start(), target_range.start());
676
-
677
- self.apply_edit(insert_range, &replacement)
678
- }
679
-
680
- InsertPosition::After(target_key) => {
681
- let target_entry = find_entry_by_key(&map, &target_key)
682
- .ok_or_else(|| YerbaError::SelectorNotFound(format!("{}.{}", dot_path, target_key)))?;
683
-
684
- let new_text = format!("\n{}{}", indent, new_entry_text);
685
-
686
- self.insert_after_node(target_entry.syntax(), &new_text)
687
- }
688
-
689
- InsertPosition::BeforeCondition(_) | InsertPosition::AfterCondition(_) => {
690
- self.insert_map_key(dot_path, key, value, InsertPosition::Last)
691
- }
692
-
693
- InsertPosition::FromSortOrder(order) => {
694
- let new_key_position = order.iter().position(|ordered_key| ordered_key == key);
695
-
696
- let resolved = match new_key_position {
697
- Some(new_position) => {
698
- let mut insert_after: Option<String> = None;
699
-
700
- for ordered_key in order.iter().take(new_position).rev() {
701
- if find_entry_by_key(&map, ordered_key).is_some() {
702
- insert_after = Some(ordered_key.clone());
703
- break;
704
- }
705
- }
706
-
707
- match insert_after {
708
- Some(after_key) => InsertPosition::After(after_key),
709
- None => InsertPosition::At(0),
710
- }
711
- }
712
-
713
- None => InsertPosition::Last,
714
- };
715
-
716
- self.insert_map_key(dot_path, key, value, resolved)
717
- }
718
- }
719
- }
720
-
721
- pub fn rename(&mut self, source_path: &str, destination_path: &str) -> Result<(), YerbaError> {
722
- Self::validate_path(source_path)?;
723
- Self::validate_path(destination_path)?;
724
-
725
- let source_parent = source_path.rsplit_once('.').map(|(parent, _)| parent).unwrap_or("");
726
-
727
- let destination_parent = destination_path
728
- .rsplit_once('.')
729
- .map(|(parent, _)| parent)
730
- .unwrap_or("");
731
-
732
- let destination_key = destination_path
733
- .rsplit_once('.')
734
- .map(|(_, key)| key)
735
- .unwrap_or(destination_path);
736
-
737
- if source_parent == destination_parent {
738
- let (parent_path, source_key) = source_path.rsplit_once('.').unwrap_or(("", source_path));
739
- let parent_node = self.navigate(parent_path)?;
740
-
741
- let map = parent_node
742
- .descendants()
743
- .find_map(BlockMap::cast)
744
- .ok_or_else(|| YerbaError::SelectorNotFound(source_path.to_string()))?;
745
-
746
- let entry =
747
- find_entry_by_key(&map, source_key).ok_or_else(|| YerbaError::SelectorNotFound(source_path.to_string()))?;
748
-
749
- let key_node = entry
750
- .key()
751
- .ok_or_else(|| YerbaError::SelectorNotFound(source_path.to_string()))?;
752
-
753
- let key_token =
754
- find_scalar_token(key_node.syntax()).ok_or_else(|| YerbaError::SelectorNotFound(source_path.to_string()))?;
755
-
756
- let new_text = format_scalar_value(destination_key, key_token.kind());
757
-
758
- self.replace_token(&key_token, &new_text)
759
- } else {
760
- let value = self
761
- .get(source_path)
762
- .ok_or_else(|| YerbaError::SelectorNotFound(source_path.to_string()))?;
763
-
764
- self.delete(source_path)?;
765
- self.insert_into(destination_path, &value, InsertPosition::Last)
766
- }
767
- }
768
-
769
- pub fn delete(&mut self, dot_path: &str) -> Result<(), YerbaError> {
770
- Self::validate_path(dot_path)?;
771
-
772
- let (parent_path, last_key) = dot_path.rsplit_once('.').unwrap_or(("", dot_path));
773
- let parent_node = self.navigate(parent_path)?;
774
-
775
- let map = parent_node
776
- .descendants()
777
- .find_map(BlockMap::cast)
778
- .ok_or_else(|| YerbaError::SelectorNotFound(dot_path.to_string()))?;
779
-
780
- let entry = find_entry_by_key(&map, last_key).ok_or_else(|| YerbaError::SelectorNotFound(dot_path.to_string()))?;
781
-
782
- self.remove_node(entry.syntax())
783
- }
784
-
785
- pub fn remove(&mut self, dot_path: &str, value: &str) -> Result<(), YerbaError> {
786
- let current_node = self.navigate(dot_path)?;
787
-
788
- let sequence = current_node
789
- .descendants()
790
- .find_map(BlockSeq::cast)
791
- .ok_or_else(|| YerbaError::NotASequence(dot_path.to_string()))?;
792
-
793
- let target_entry = sequence
794
- .entries()
795
- .find(|entry| {
796
- entry
797
- .flow()
798
- .and_then(|flow| extract_scalar_text(flow.syntax()))
799
- .map(|text| text == value)
800
- .unwrap_or(false)
801
- })
802
- .ok_or_else(|| YerbaError::SelectorNotFound(format!("{} item '{}'", dot_path, value)))?;
803
-
804
- self.remove_node(target_entry.syntax())
805
- }
806
-
807
- pub fn remove_at(&mut self, dot_path: &str, index: usize) -> Result<(), YerbaError> {
808
- Self::validate_path(dot_path)?;
809
-
810
- let current_node = self.navigate(dot_path)?;
811
-
812
- let sequence = current_node
813
- .descendants()
814
- .find_map(BlockSeq::cast)
815
- .ok_or_else(|| YerbaError::NotASequence(dot_path.to_string()))?;
816
-
817
- let entries: Vec<_> = sequence.entries().collect();
818
-
819
- if index >= entries.len() {
820
- return Err(YerbaError::IndexOutOfBounds(index, entries.len()));
821
- }
822
-
823
- self.remove_node(entries[index].syntax())
824
- }
825
-
826
- pub fn move_item(&mut self, dot_path: &str, from: usize, to: usize) -> Result<(), YerbaError> {
827
- if from == to {
828
- return Ok(());
829
- }
830
-
831
- let current_node = self.navigate(dot_path)?;
832
-
833
- let sequence = current_node
834
- .descendants()
835
- .find_map(BlockSeq::cast)
836
- .ok_or_else(|| YerbaError::NotASequence(dot_path.to_string()))?;
837
-
838
- let entries: Vec<_> = sequence.entries().collect();
839
-
840
- self.reorder_entries(sequence.syntax(), &entries, from, to)
841
- }
842
-
843
- pub fn move_key(&mut self, dot_path: &str, from: usize, to: usize) -> Result<(), YerbaError> {
844
- if from == to {
845
- return Ok(());
846
- }
847
-
848
- let current_node = self.navigate(dot_path)?;
849
-
850
- let map = current_node
851
- .descendants()
852
- .find_map(BlockMap::cast)
853
- .ok_or_else(|| YerbaError::SelectorNotFound(dot_path.to_string()))?;
854
-
855
- let entries: Vec<_> = map.entries().collect();
856
-
857
- self.reorder_entries(map.syntax(), &entries, from, to)
858
- }
859
-
860
- pub fn resolve_key_index(&self, dot_path: &str, reference: &str) -> Result<usize, YerbaError> {
861
- let current_node = self.navigate(dot_path)?;
862
-
863
- let map = current_node
864
- .descendants()
865
- .find_map(BlockMap::cast)
866
- .ok_or_else(|| YerbaError::SelectorNotFound(dot_path.to_string()))?;
867
-
868
- if let Ok(index) = reference.parse::<usize>() {
869
- let length = map.entries().count();
870
-
871
- if index >= length {
872
- return Err(YerbaError::IndexOutOfBounds(index, length));
873
- }
874
-
875
- return Ok(index);
876
- }
877
-
878
- map
879
- .entries()
880
- .enumerate()
881
- .find(|(_index, entry)| {
882
- entry
883
- .key()
884
- .and_then(|key_node| extract_scalar_text(key_node.syntax()))
885
- .map(|key_text| key_text == reference)
886
- .unwrap_or(false)
887
- })
888
- .map(|(index, _entry)| index)
889
- .ok_or_else(|| YerbaError::SelectorNotFound(format!("{} key '{}'", dot_path, reference)))
890
- }
891
-
892
- pub fn resolve_sequence_index(&self, dot_path: &str, reference: &str) -> Result<usize, YerbaError> {
893
- let current_node = self.navigate(dot_path)?;
894
-
895
- let sequence = current_node
896
- .descendants()
897
- .find_map(BlockSeq::cast)
898
- .ok_or_else(|| YerbaError::NotASequence(dot_path.to_string()))?;
899
-
900
- if let Ok(index) = reference.parse::<usize>() {
901
- let length = sequence.entries().count();
902
-
903
- if index >= length {
904
- return Err(YerbaError::IndexOutOfBounds(index, length));
905
- }
906
-
907
- return Ok(index);
908
- }
909
-
910
- if crate::selector::Selector::parse(reference).is_relative() {
911
- return sequence
912
- .entries()
913
- .enumerate()
914
- .find(|(_index, entry)| self.evaluate_condition_on_node(entry.syntax(), reference))
915
- .map(|(index, _entry)| index)
916
- .ok_or_else(|| YerbaError::SelectorNotFound(format!("{} condition '{}'", dot_path, reference)));
917
- }
918
-
919
- sequence
920
- .entries()
921
- .enumerate()
922
- .find(|(_index, entry)| {
923
- entry
924
- .flow()
925
- .and_then(|flow| extract_scalar_text(flow.syntax()))
926
- .map(|text| text == reference)
927
- .unwrap_or(false)
928
- })
929
- .map(|(index, _entry)| index)
930
- .ok_or_else(|| YerbaError::SelectorNotFound(format!("{} item '{}'", dot_path, reference)))
931
- }
932
-
933
- pub fn validate_sort_keys(&self, dot_path: &str, key_order: &[&str]) -> Result<(), YerbaError> {
934
- if dot_path == "[]" || dot_path.ends_with(".[]") {
935
- let seq_path = if dot_path == "[]" {
936
- ""
937
- } else {
938
- &dot_path[..dot_path.len() - 3]
939
- };
940
-
941
- return self.validate_each_sort_keys(seq_path, key_order);
942
- }
943
-
944
- let current_node = self.navigate(dot_path)?;
945
-
946
- let map = current_node
947
- .descendants()
948
- .find_map(BlockMap::cast)
949
- .ok_or_else(|| YerbaError::SelectorNotFound(dot_path.to_string()))?;
950
-
951
- let unknown_keys: Vec<String> = map
952
- .entries()
953
- .filter_map(|entry| entry.key().and_then(|key_node| extract_scalar_text(key_node.syntax())))
954
- .filter(|key_name| !key_order.contains(&key_name.as_str()))
955
- .collect();
956
-
957
- if unknown_keys.is_empty() {
958
- Ok(())
959
- } else {
960
- Err(YerbaError::UnknownKeys(unknown_keys))
961
- }
962
- }
963
-
964
- pub fn sort_keys(&mut self, dot_path: &str, key_order: &[&str]) -> Result<(), YerbaError> {
965
- if dot_path == "[]" || dot_path.ends_with(".[]") {
966
- let seq_path = if dot_path == "[]" {
967
- ""
968
- } else {
969
- &dot_path[..dot_path.len() - 3]
970
- };
971
-
972
- return self.sort_each_keys(seq_path, key_order);
973
- }
974
-
975
- let current_node = self.navigate(dot_path)?;
976
-
977
- let map = current_node
978
- .descendants()
979
- .find_map(BlockMap::cast)
980
- .ok_or_else(|| YerbaError::SelectorNotFound(dot_path.to_string()))?;
981
-
982
- let entries: Vec<_> = map.entries().collect();
983
-
984
- if entries.len() <= 1 {
985
- return Ok(());
986
- }
987
-
988
- let (groups, range) = collect_groups_with_range(map.syntax());
989
-
990
- let mut keyed: Vec<(String, EntryGroup)> = entries
991
- .iter()
992
- .zip(groups)
993
- .map(|(entry, group)| {
994
- let key_name = entry
995
- .key()
996
- .and_then(|key_node| extract_scalar_text(key_node.syntax()))
997
- .unwrap_or_default();
998
- (key_name, group)
999
- })
1000
- .collect();
1001
-
1002
- let original_keys: Vec<String> = keyed.iter().map(|(key, _)| key.clone()).collect();
1003
-
1004
- keyed.sort_by(|(key_a, _), (key_b, _)| {
1005
- let position_a = key_order.iter().position(|&key| key == key_a);
1006
- let position_b = key_order.iter().position(|&key| key == key_b);
1007
-
1008
- match (position_a, position_b) {
1009
- (Some(a), Some(b)) => a.cmp(&b),
1010
- (Some(_), None) => std::cmp::Ordering::Less,
1011
- (None, Some(_)) => std::cmp::Ordering::Greater,
1012
- (None, None) => {
1013
- let original_a = original_keys.iter().position(|key| key == key_a).unwrap();
1014
- let original_b = original_keys.iter().position(|key| key == key_b).unwrap();
1015
- original_a.cmp(&original_b)
1016
- }
1017
- }
1018
- });
1019
-
1020
- let sorted_keys: Vec<&str> = keyed.iter().map(|(key, _)| key.as_str()).collect();
1021
- let orig_refs: Vec<&str> = original_keys.iter().map(|key| key.as_str()).collect();
1022
-
1023
- if sorted_keys == orig_refs {
1024
- return Ok(());
1025
- }
1026
-
1027
- let indent = entries
1028
- .get(1)
1029
- .map(|entry| preceding_whitespace_indent(entry.syntax()))
1030
- .unwrap_or_default();
1031
-
1032
- let sorted_groups: Vec<EntryGroup> = keyed.into_iter().map(|(_, group)| group).collect();
1033
- let map_text = rebuild_from_groups(&sorted_groups, &indent, false);
1034
-
1035
- self.apply_edit(range, &map_text)
1036
- }
1037
-
1038
- pub fn sort_each_keys(&mut self, dot_path: &str, key_order: &[&str]) -> Result<(), YerbaError> {
1039
- let current_node = self.navigate(dot_path)?;
1040
-
1041
- let sequence = match current_node.descendants().find_map(BlockSeq::cast) {
1042
- Some(sequence) => sequence,
1043
- None => return Ok(()),
1044
- };
1045
-
1046
- let mut edits: Vec<(TextRange, String)> = Vec::new();
1047
-
1048
- for entry in sequence.entries() {
1049
- let entry_node = entry.syntax();
1050
-
1051
- let map = match entry_node.descendants().find_map(BlockMap::cast) {
1052
- Some(map) => map,
1053
- None => continue,
1054
- };
1055
-
1056
- let entries: Vec<_> = map.entries().collect();
1057
-
1058
- if entries.len() <= 1 {
1059
- continue;
1060
- }
1061
-
1062
- let (groups, group_range) = collect_groups_with_range(map.syntax());
1063
-
1064
- let mut keyed: Vec<(String, EntryGroup)> = entries
1065
- .iter()
1066
- .zip(groups)
1067
- .map(|(entry, group)| {
1068
- let key_name = entry
1069
- .key()
1070
- .and_then(|key_node| extract_scalar_text(key_node.syntax()))
1071
- .unwrap_or_default();
1072
- (key_name, group)
1073
- })
1074
- .collect();
1075
-
1076
- let original_keys: Vec<String> = keyed.iter().map(|(key, _)| key.clone()).collect();
1077
-
1078
- keyed.sort_by(|(key_a, _), (key_b, _)| {
1079
- let position_a = key_order.iter().position(|&key| key == key_a);
1080
- let position_b = key_order.iter().position(|&key| key == key_b);
1081
-
1082
- match (position_a, position_b) {
1083
- (Some(a), Some(b)) => a.cmp(&b),
1084
- (Some(_), None) => std::cmp::Ordering::Less,
1085
- (None, Some(_)) => std::cmp::Ordering::Greater,
1086
-
1087
- (None, None) => {
1088
- let original_a = original_keys.iter().position(|key| key == key_a).unwrap();
1089
- let original_b = original_keys.iter().position(|key| key == key_b).unwrap();
1090
-
1091
- original_a.cmp(&original_b)
1092
- }
1093
- }
1094
- });
1095
-
1096
- let sorted_keys: Vec<&str> = keyed.iter().map(|(key, _)| key.as_str()).collect();
1097
- let orig_refs: Vec<&str> = original_keys.iter().map(|key| key.as_str()).collect();
1098
-
1099
- if sorted_keys == orig_refs {
1100
- continue;
1101
- }
1102
-
1103
- let indent = entries
1104
- .get(1)
1105
- .map(|entry| preceding_whitespace_indent(entry.syntax()))
1106
- .unwrap_or_default();
1107
-
1108
- let sorted_groups: Vec<EntryGroup> = keyed.into_iter().map(|(_, group)| group).collect();
1109
- let map_text = rebuild_from_groups(&sorted_groups, &indent, false);
1110
- edits.push((group_range, map_text));
1111
- }
1112
-
1113
- if edits.is_empty() {
1114
- return Ok(());
1115
- }
1116
-
1117
- edits.reverse();
1118
-
1119
- let source = self.root.text().to_string();
1120
- let mut new_source = source;
1121
-
1122
- for (range, replacement) in edits {
1123
- let start: usize = range.start().into();
1124
- let end: usize = range.end().into();
1125
-
1126
- new_source.replace_range(start..end, &replacement);
1127
- }
1128
-
1129
- let path = self.path.take();
1130
- *self = Self::parse(&new_source)?;
1131
- self.path = path;
1132
-
1133
- Ok(())
1134
- }
1135
-
1136
- pub fn validate_each_sort_keys(&self, dot_path: &str, key_order: &[&str]) -> Result<(), YerbaError> {
1137
- let current_node = self.navigate(dot_path)?;
1138
-
1139
- let sequence = match current_node.descendants().find_map(BlockSeq::cast) {
1140
- Some(sequence) => sequence,
1141
- None => return Ok(()),
1142
- };
1143
-
1144
- let mut all_unknown: Vec<String> = Vec::new();
1145
-
1146
- for entry in sequence.entries() {
1147
- if let Some(map) = entry.syntax().descendants().find_map(BlockMap::cast) {
1148
- for map_entry in map.entries() {
1149
- if let Some(key_name) = map_entry
1150
- .key()
1151
- .and_then(|key_node| extract_scalar_text(key_node.syntax()))
1152
- {
1153
- if !key_order.contains(&key_name.as_str()) && !all_unknown.contains(&key_name) {
1154
- all_unknown.push(key_name);
1155
- }
1156
- }
1157
- }
1158
- }
1159
- }
1160
-
1161
- if all_unknown.is_empty() {
1162
- Ok(())
1163
- } else {
1164
- Err(YerbaError::UnknownKeys(all_unknown))
1165
- }
1166
- }
1167
-
1168
- pub fn sort_items(
1169
- &mut self,
1170
- dot_path: &str,
1171
- sort_fields: &[SortField],
1172
- case_sensitive: bool,
1173
- ) -> Result<(), YerbaError> {
1174
- if dot_path.contains("[].") {
1175
- return self.sort_each_items(dot_path, sort_fields, case_sensitive);
1176
- }
1177
-
1178
- let current_node = self.navigate(dot_path)?;
1179
-
1180
- let sequence = match current_node.descendants().find_map(BlockSeq::cast) {
1181
- Some(sequence) => sequence,
1182
- None => return Ok(()),
1183
- };
1184
-
1185
- let entries: Vec<_> = sequence.entries().collect();
1186
-
1187
- if entries.len() <= 1 {
1188
- return Ok(());
1189
- }
1190
-
1191
- let (groups, range) = collect_groups_with_range(sequence.syntax());
1192
-
1193
- let mut sortable: Vec<(Vec<String>, EntryGroup)> = entries
1194
- .iter()
1195
- .zip(groups)
1196
- .map(|(entry, group)| {
1197
- let sort_values = if sort_fields.is_empty() {
1198
- vec![entry
1199
- .flow()
1200
- .and_then(|flow| extract_scalar_text(flow.syntax()))
1201
- .unwrap_or_default()]
1202
- } else {
1203
- sort_fields
1204
- .iter()
1205
- .map(|field| {
1206
- let nodes = navigate_from_node(entry.syntax(), &field.path);
1207
- nodes.first().and_then(extract_scalar_text).unwrap_or_default()
1208
- })
1209
- .collect()
1210
- };
1211
-
1212
- (sort_values, group)
1213
- })
1214
- .collect();
1215
-
1216
- let original_bodies: Vec<String> = sortable.iter().map(|(_, group)| group.body.clone()).collect();
1217
-
1218
- sortable.sort_by(|(values_a, _), (values_b, _)| {
1219
- for (index, field) in sort_fields.iter().enumerate().take(values_a.len()) {
1220
- let value_a = &values_a[index];
1221
- let value_b = &values_b[index];
1222
-
1223
- let ordering = if case_sensitive {
1224
- value_a.cmp(value_b)
1225
- } else {
1226
- value_a.to_lowercase().cmp(&value_b.to_lowercase())
1227
- };
1228
-
1229
- let ordering = if field.ascending { ordering } else { ordering.reverse() };
1230
-
1231
- if ordering != std::cmp::Ordering::Equal {
1232
- return ordering;
1233
- }
1234
- }
1235
-
1236
- if sort_fields.is_empty() && !values_a.is_empty() && !values_b.is_empty() {
1237
- return if case_sensitive {
1238
- values_a[0].cmp(&values_b[0])
1239
- } else {
1240
- values_a[0].to_lowercase().cmp(&values_b[0].to_lowercase())
1241
- };
1242
- }
1243
-
1244
- std::cmp::Ordering::Equal
1245
- });
1246
-
1247
- let sorted_bodies: Vec<String> = sortable.iter().map(|(_, group)| group.body.clone()).collect();
1248
-
1249
- if sorted_bodies == original_bodies {
1250
- return Ok(());
1251
- }
1252
-
1253
- let indent = entries
1254
- .get(1)
1255
- .map(|entry| preceding_whitespace_indent(entry.syntax()))
1256
- .unwrap_or_default();
1257
-
1258
- let sorted_groups: Vec<EntryGroup> = sortable.into_iter().map(|(_, group)| group).collect();
1259
- let sequence_text = rebuild_from_groups(&sorted_groups, &indent, true);
1260
-
1261
- self.apply_edit(range, &sequence_text)
1262
- }
1263
-
1264
- fn sort_each_items(
1265
- &mut self,
1266
- dot_path: &str,
1267
- sort_fields: &[SortField],
1268
- case_sensitive: bool,
1269
- ) -> Result<(), YerbaError> {
1270
- let (parent_path, child_path) = if let Some(last_bracket) = dot_path.rfind("[].") {
1271
- (&dot_path[..last_bracket + 2], &dot_path[last_bracket + 3..])
1272
- } else {
1273
- (dot_path, "")
1274
- };
1275
-
1276
- let parent_nodes = self.navigate_all(parent_path);
1277
- let source = self.root.text().to_string();
1278
- let mut edits: Vec<(TextRange, String)> = Vec::new();
1279
-
1280
- for parent_node in &parent_nodes {
1281
- let child_nodes = if child_path.is_empty() {
1282
- vec![parent_node.clone()]
1283
- } else {
1284
- navigate_from_node(parent_node, child_path)
1285
- };
1286
-
1287
- for child_node in &child_nodes {
1288
- let sequence = match child_node.descendants().find_map(BlockSeq::cast) {
1289
- Some(sequence) => sequence,
1290
- None => continue,
1291
- };
1292
-
1293
- let entries: Vec<_> = sequence.entries().collect();
1294
-
1295
- if entries.len() <= 1 {
1296
- continue;
1297
- }
1298
-
1299
- let (groups, group_range) = collect_groups_with_range(sequence.syntax());
1300
-
1301
- let mut sortable: Vec<(Vec<String>, EntryGroup)> = entries
1302
- .iter()
1303
- .zip(groups)
1304
- .map(|(entry, group)| {
1305
- let sort_values = if sort_fields.is_empty() {
1306
- vec![entry
1307
- .flow()
1308
- .and_then(|flow| extract_scalar_text(flow.syntax()))
1309
- .unwrap_or_default()]
1310
- } else {
1311
- sort_fields
1312
- .iter()
1313
- .map(|field| {
1314
- let nodes = navigate_from_node(entry.syntax(), &field.path);
1315
- nodes.first().and_then(extract_scalar_text).unwrap_or_default()
1316
- })
1317
- .collect()
1318
- };
1319
-
1320
- (sort_values, group)
1321
- })
1322
- .collect();
1323
-
1324
- let original_bodies: Vec<String> = sortable.iter().map(|(_, group)| group.body.clone()).collect();
1325
-
1326
- sortable.sort_by(|(values_a, _), (values_b, _)| {
1327
- for (index, field) in sort_fields.iter().enumerate().take(values_a.len()) {
1328
- let value_a = &values_a[index];
1329
- let value_b = &values_b[index];
1330
-
1331
- let ordering = if case_sensitive {
1332
- value_a.cmp(value_b)
1333
- } else {
1334
- value_a.to_lowercase().cmp(&value_b.to_lowercase())
1335
- };
1336
-
1337
- let ordering = if field.ascending { ordering } else { ordering.reverse() };
1338
-
1339
- if ordering != std::cmp::Ordering::Equal {
1340
- return ordering;
1341
- }
1342
- }
1343
-
1344
- if sort_fields.is_empty() && !values_a.is_empty() && !values_b.is_empty() {
1345
- return if case_sensitive {
1346
- values_a[0].cmp(&values_b[0])
1347
- } else {
1348
- values_a[0].to_lowercase().cmp(&values_b[0].to_lowercase())
1349
- };
1350
- }
1351
-
1352
- std::cmp::Ordering::Equal
1353
- });
1354
-
1355
- let sorted_bodies: Vec<String> = sortable.iter().map(|(_, group)| group.body.clone()).collect();
1356
-
1357
- if sorted_bodies == original_bodies {
1358
- continue;
1359
- }
1360
-
1361
- let indent = entries
1362
- .get(1)
1363
- .map(|entry| preceding_whitespace_indent(entry.syntax()))
1364
- .unwrap_or_default();
1365
-
1366
- let sorted_groups: Vec<EntryGroup> = sortable.into_iter().map(|(_, group)| group).collect();
1367
- let sequence_text = rebuild_from_groups(&sorted_groups, &indent, true);
1368
- edits.push((group_range, sequence_text));
1369
- }
1370
- }
1371
-
1372
- if edits.is_empty() {
1373
- return Ok(());
1374
- }
1375
-
1376
- edits.reverse();
1377
-
1378
- let mut new_source = source;
1379
-
1380
- for (range, replacement) in edits {
1381
- let start: usize = range.start().into();
1382
- let end: usize = range.end().into();
1383
-
1384
- new_source.replace_range(start..end, &replacement);
1385
- }
1386
-
1387
- let path = self.path.take();
1388
- *self = Self::parse(&new_source)?;
1389
- self.path = path;
1390
-
1391
- Ok(())
1392
- }
1393
-
1394
- pub fn enforce_blank_lines(&mut self, dot_path: &str, blank_lines: usize) -> Result<(), YerbaError> {
1395
- let nodes = if dot_path.contains('[') {
1396
- self.navigate_all(dot_path)
1397
- } else {
1398
- vec![self.navigate(dot_path)?]
1399
- };
1400
-
1401
- let mut edits: Vec<(TextRange, String)> = Vec::new();
1402
-
1403
- for current_node in &nodes {
1404
- let sequence = match current_node.descendants().find_map(BlockSeq::cast) {
1405
- Some(sequence) => sequence,
1406
- None => continue,
1407
- };
1408
-
1409
- let entries: Vec<_> = sequence.entries().collect();
1410
-
1411
- if entries.len() <= 1 {
1412
- continue;
1413
- }
1414
-
1415
- for entry in entries.iter().skip(1) {
1416
- if let Some(whitespace_token) = preceding_whitespace_token(entry.syntax()) {
1417
- let whitespace_text = whitespace_token.text();
1418
- let newline_count = whitespace_text.chars().filter(|character| *character == '\n').count();
1419
-
1420
- let indent = whitespace_text
1421
- .rfind('\n')
1422
- .map(|position| &whitespace_text[position + 1..])
1423
- .unwrap_or("");
1424
-
1425
- let desired_newlines = blank_lines + 1;
1426
-
1427
- if newline_count != desired_newlines {
1428
- let new_whitespace = format!("{}{}", "\n".repeat(desired_newlines), indent);
1429
-
1430
- edits.push((whitespace_token.text_range(), new_whitespace));
1431
- }
1432
- }
1433
- }
1434
- }
1435
-
1436
- if edits.is_empty() {
1437
- return Ok(());
1438
- }
1439
-
1440
- edits.sort_by_key(|edit| std::cmp::Reverse(edit.0.start()));
1441
-
1442
- let source = self.root.text().to_string();
1443
- let mut new_source = source;
1444
-
1445
- for (range, replacement) in edits {
1446
- let start: usize = range.start().into();
1447
- let end: usize = range.end().into();
1448
-
1449
- new_source.replace_range(start..end, &replacement);
1450
- }
1451
-
1452
- let path = self.path.take();
1453
- *self = Self::parse(&new_source)?;
1454
- self.path = path;
1455
-
1456
- Ok(())
1457
- }
1458
-
1459
- pub fn enforce_key_style(&mut self, style: &QuoteStyle, dot_path: Option<&str>) -> Result<(), YerbaError> {
1460
- let source = self.root.text().to_string();
1461
-
1462
- let scope_ranges: Vec<TextRange> = match dot_path {
1463
- Some(path) if !path.is_empty() => self.navigate_all(path).iter().map(|node| node.text_range()).collect(),
1464
- _ => vec![self.root.text_range()],
1465
- };
1466
-
1467
- let mut edits: Vec<(TextRange, String)> = Vec::new();
1468
-
1469
- for element in self.root.descendants_with_tokens() {
1470
- if let Some(token) = element.into_token() {
1471
- if !scope_ranges
1472
- .iter()
1473
- .any(|range| range.contains_range(token.text_range()))
1474
- {
1475
- continue;
1476
- }
1477
-
1478
- if !is_map_key(&token) {
1479
- continue;
1480
- }
1481
-
1482
- let current_kind = token.kind();
1483
-
1484
- if !matches!(
1485
- current_kind,
1486
- SyntaxKind::PLAIN_SCALAR | SyntaxKind::DOUBLE_QUOTED_SCALAR | SyntaxKind::SINGLE_QUOTED_SCALAR
1487
- ) {
1488
- continue;
1489
- }
1490
-
1491
- let target_kind = style.to_syntax_kind();
1492
-
1493
- if current_kind == target_kind {
1494
- continue;
1495
- }
1496
-
1497
- let raw_value = match current_kind {
1498
- SyntaxKind::DOUBLE_QUOTED_SCALAR => {
1499
- let text = token.text();
1500
- unescape_double_quoted(&text[1..text.len() - 1])
1501
- }
1502
-
1503
- SyntaxKind::SINGLE_QUOTED_SCALAR => {
1504
- let text = token.text();
1505
- unescape_single_quoted(&text[1..text.len() - 1])
1506
- }
1507
-
1508
- SyntaxKind::PLAIN_SCALAR => token.text().to_string(),
1509
-
1510
- _ => continue,
1511
- };
1512
-
1513
- let new_text = match style {
1514
- QuoteStyle::Double => {
1515
- let escaped = raw_value.replace('\\', "\\\\").replace('"', "\\\"");
1516
- format!("\"{}\"", escaped)
1517
- }
1518
-
1519
- QuoteStyle::Single => {
1520
- let escaped = raw_value.replace('\'', "''");
1521
- format!("'{}'", escaped)
1522
- }
1523
-
1524
- QuoteStyle::Plain => raw_value,
1525
-
1526
- _ => continue,
1527
- };
1528
-
1529
- if new_text != token.text() {
1530
- edits.push((token.text_range(), new_text));
1531
- }
1532
- }
1533
- }
1534
-
1535
- if edits.is_empty() {
1536
- return Ok(());
1537
- }
1538
-
1539
- edits.reverse();
1540
-
1541
- let mut new_source = source;
1542
-
1543
- for (range, replacement) in edits {
1544
- let start: usize = range.start().into();
1545
- let end: usize = range.end().into();
1546
-
1547
- new_source.replace_range(start..end, &replacement);
1548
- }
1549
-
1550
- let path = self.path.take();
1551
- *self = Self::parse(&new_source)?;
1552
- self.path = path;
1553
-
1554
- Ok(())
1555
- }
1556
-
1557
- pub fn enforce_quotes(&mut self, style: &QuoteStyle) -> Result<(), YerbaError> {
1558
- self.enforce_quotes_at(style, None)
1559
- }
1560
-
1561
- pub fn enforce_quotes_at(&mut self, style: &QuoteStyle, dot_path: Option<&str>) -> Result<(), YerbaError> {
1562
- let source = self.root.text().to_string();
1563
-
1564
- let scope_ranges: Vec<TextRange> = match dot_path {
1565
- Some(path) if !path.is_empty() => self.navigate_all(path).iter().map(|node| node.text_range()).collect(),
1566
- _ => vec![self.root.text_range()],
1567
- };
1568
-
1569
- let mut edits: Vec<(TextRange, String)> = Vec::new();
1570
-
1571
- for element in self.root.descendants_with_tokens() {
1572
- if let Some(token) = element.into_token() {
1573
- if !scope_ranges
1574
- .iter()
1575
- .any(|range| range.contains_range(token.text_range()))
1576
- {
1577
- continue;
1578
- }
1579
-
1580
- if is_map_key(&token) {
1581
- continue;
1582
- }
1583
-
1584
- let current_kind = token.kind();
1585
-
1586
- if !matches!(
1587
- current_kind,
1588
- SyntaxKind::PLAIN_SCALAR | SyntaxKind::DOUBLE_QUOTED_SCALAR | SyntaxKind::SINGLE_QUOTED_SCALAR
1589
- ) {
1590
- continue;
1591
- }
1592
-
1593
- let target_kind = style.to_syntax_kind();
1594
-
1595
- if current_kind == target_kind {
1596
- continue;
1597
- }
1598
-
1599
- let raw_value = match current_kind {
1600
- SyntaxKind::DOUBLE_QUOTED_SCALAR => {
1601
- let text = token.text();
1602
- unescape_double_quoted(&text[1..text.len() - 1])
1603
- }
1604
-
1605
- SyntaxKind::SINGLE_QUOTED_SCALAR => {
1606
- let text = token.text();
1607
- unescape_single_quoted(&text[1..text.len() - 1])
1608
- }
1609
-
1610
- SyntaxKind::PLAIN_SCALAR => token.text().to_string(),
1611
-
1612
- _ => continue,
1613
- };
1614
-
1615
- if is_yaml_non_string(&raw_value) {
1616
- continue;
1617
- }
1618
-
1619
- let new_text = match style {
1620
- QuoteStyle::Double => {
1621
- if raw_value.contains('"') && current_kind == SyntaxKind::SINGLE_QUOTED_SCALAR {
1622
- continue;
1623
- }
1624
-
1625
- let escaped = raw_value.replace('\\', "\\\\").replace('"', "\\\"");
1626
- format!("\"{}\"", escaped)
1627
- }
1628
-
1629
- QuoteStyle::Single => {
1630
- let escaped = raw_value.replace('\'', "''");
1631
- format!("'{}'", escaped)
1632
- }
1633
-
1634
- QuoteStyle::Plain => {
1635
- if raw_value.contains('"') || raw_value.contains('\'') || raw_value.contains(':') || raw_value.contains('#')
1636
- {
1637
- continue;
1638
- }
1639
-
1640
- raw_value
1641
- }
1642
-
1643
- _ => continue,
1644
- };
1645
-
1646
- if new_text != token.text() {
1647
- edits.push((token.text_range(), new_text));
1648
- }
1649
- }
1650
- }
1651
-
1652
- if edits.is_empty() {
1653
- return Ok(());
1654
- }
1655
-
1656
- edits.reverse();
1657
-
1658
- let mut new_source = source;
1659
-
1660
- for (range, replacement) in edits {
1661
- let start: usize = range.start().into();
1662
- let end: usize = range.end().into();
1663
-
1664
- new_source.replace_range(start..end, &replacement);
1665
- }
1666
-
1667
- let path = self.path.take();
1668
- *self = Self::parse(&new_source)?;
1669
- self.path = path;
1670
-
1671
- Ok(())
1672
- }
1673
-
1674
- pub fn save(&self) -> Result<(), YerbaError> {
1675
- let path = self.path.as_ref().ok_or_else(|| {
1676
- YerbaError::IoError(std::io::Error::new(
1677
- std::io::ErrorKind::NotFound,
1678
- "no file path associated with this document",
1679
- ))
1680
- })?;
1681
-
1682
- fs::write(path, self.to_string())?;
1683
-
1684
- Ok(())
1685
- }
1686
-
1687
- pub fn save_to(&self, path: impl AsRef<Path>) -> Result<(), YerbaError> {
1688
- fs::write(path, self.to_string())?;
1689
-
1690
- Ok(())
1691
- }
1692
-
1693
- pub fn navigate(&self, dot_path: &str) -> Result<SyntaxNode, YerbaError> {
1694
- Self::validate_path(dot_path)?;
1695
-
1696
- if dot_path.is_empty() {
1697
- let root = Root::cast(self.root.clone()).ok_or_else(|| YerbaError::SelectorNotFound(dot_path.to_string()))?;
1698
-
1699
- let document = root
1700
- .documents()
1701
- .next()
1702
- .ok_or_else(|| YerbaError::SelectorNotFound(dot_path.to_string()))?;
1703
-
1704
- return Ok(document.syntax().clone());
1705
- }
1706
-
1707
- let nodes = self.navigate_all(dot_path);
1708
-
1709
- match nodes.len() {
1710
- 0 => Err(YerbaError::SelectorNotFound(dot_path.to_string())),
1711
- 1 => Ok(nodes.into_iter().next().unwrap()),
1712
- _ => Err(YerbaError::AmbiguousSelector(dot_path.to_string(), nodes.len())),
1713
- }
1714
- }
1715
-
1716
- pub fn validate_path(dot_path: &str) -> Result<(), YerbaError> {
1717
- if dot_path.ends_with('.') {
1718
- return Err(YerbaError::ParseError(format!(
1719
- "invalid path: trailing dot in '{}'",
1720
- dot_path
1721
- )));
1722
- }
1723
-
1724
- if dot_path.contains("..") {
1725
- return Err(YerbaError::ParseError(format!(
1726
- "invalid path: double dot in '{}'",
1727
- dot_path
1728
- )));
1729
- }
1730
-
1731
- if dot_path.starts_with('.') {
1732
- return Err(YerbaError::ParseError(format!(
1733
- "invalid path: leading dot in '{}'",
1734
- dot_path
1735
- )));
1736
- }
1737
-
1738
- if dot_path.contains('[') && !dot_path.contains(']') {
1739
- return Err(YerbaError::ParseError(format!(
1740
- "invalid path: unclosed bracket in '{}'",
1741
- dot_path
1742
- )));
1743
- }
1744
-
1745
- Ok(())
1746
- }
1747
-
1748
- pub fn navigate_all(&self, dot_path: &str) -> Vec<SyntaxNode> {
1749
- if Document::validate_path(dot_path).is_err() {
1750
- return Vec::new();
1751
- }
1752
-
1753
- let parsed = crate::selector::Selector::parse(dot_path);
1754
-
1755
- let root = match Root::cast(self.root.clone()) {
1756
- Some(root) => root,
1757
- None => return Vec::new(),
1758
- };
1759
-
1760
- let document = match root.documents().next() {
1761
- Some(document) => document,
1762
- None => return Vec::new(),
1763
- };
1764
-
1765
- let mut current_nodes = vec![document.syntax().clone()];
1766
-
1767
- if parsed.is_empty() {
1768
- if let Some(sequence) = document.syntax().descendants().find_map(BlockSeq::cast) {
1769
- current_nodes = sequence.entries().map(|entry| entry.syntax().clone()).collect();
1770
- }
1771
-
1772
- return current_nodes;
1773
- }
1774
-
1775
- for segment in parsed.segments() {
1776
- let mut next_nodes = Vec::new();
1777
-
1778
- for node in &current_nodes {
1779
- next_nodes.extend(resolve_segment(node, segment));
1780
- }
1781
-
1782
- current_nodes = next_nodes;
1783
-
1784
- if current_nodes.is_empty() {
1785
- break;
1786
- }
1787
- }
1788
-
1789
- current_nodes
1790
- }
1791
-
1792
- fn replace_token(&mut self, token: &SyntaxToken, new_text: &str) -> Result<(), YerbaError> {
1793
- let range = token.text_range();
1794
-
1795
- self.apply_edit(range, new_text)
1796
- }
1797
-
1798
- fn insert_after_node(&mut self, node: &SyntaxNode, text: &str) -> Result<(), YerbaError> {
1799
- let position = node.text_range().end();
1800
- let range = TextRange::new(position, position);
1801
-
1802
- self.apply_edit(range, text)
1803
- }
1804
-
1805
- fn remove_node(&mut self, node: &SyntaxNode) -> Result<(), YerbaError> {
1806
- let inline_comment = self.find_inline_comment(node);
1807
- let range = removal_range(node);
1808
-
1809
- if let Some((comment_text, comment_end)) = inline_comment {
1810
- let indent = preceding_whitespace_indent(node);
1811
- let replacement = format!("\n{}{}", indent, comment_text);
1812
- let expanded_range = TextRange::new(range.start(), comment_end);
1813
-
1814
- self.apply_edit(expanded_range, &replacement)
1815
- } else {
1816
- self.apply_edit(range, "")
1817
- }
1818
- }
1819
-
1820
- fn find_inline_comment(&self, node: &SyntaxNode) -> Option<(String, rowan::TextSize)> {
1821
- let mut sibling = node.next_sibling_or_token();
1822
-
1823
- while let Some(ref element) = sibling {
1824
- match element {
1825
- rowan::NodeOrToken::Token(token) => {
1826
- if token.kind() == SyntaxKind::COMMENT {
1827
- return Some((token.text().to_string(), token.text_range().end()));
1828
- } else if token.kind() == SyntaxKind::WHITESPACE {
1829
- if token.text().contains('\n') {
1830
- return None;
1831
- }
1832
- } else {
1833
- return None;
1834
- }
1835
- }
1836
- _ => return None,
1837
- }
1838
-
1839
- sibling = match element {
1840
- rowan::NodeOrToken::Token(token) => token.next_sibling_or_token(),
1841
- rowan::NodeOrToken::Node(node) => node.next_sibling_or_token(),
1842
- };
1843
- }
1844
-
1845
- None
1846
- }
1847
-
1848
- fn reorder_entries<T>(&mut self, parent: &SyntaxNode, entries: &[T], from: usize, to: usize) -> Result<(), YerbaError>
1849
- where
1850
- T: rowan::ast::AstNode<Language = yaml_parser::YamlLanguage>,
1851
- {
1852
- let length = entries.len();
1853
-
1854
- if from >= length {
1855
- return Err(YerbaError::IndexOutOfBounds(from, length));
1856
- }
1857
-
1858
- if to >= length {
1859
- return Err(YerbaError::IndexOutOfBounds(to, length));
1860
- }
1861
-
1862
- let (groups, range) = collect_groups_with_range(parent);
1863
-
1864
- let mut reordered = groups.clone();
1865
- let item = reordered.remove(from);
1866
- reordered.insert(to, item);
1867
-
1868
- let indent = entries
1869
- .get(1)
1870
- .map(|entry| preceding_whitespace_indent(entry.syntax()))
1871
- .unwrap_or_default();
1872
-
1873
- let text = rebuild_from_groups(&reordered, &indent, true);
1874
-
1875
- self.apply_edit(range, &text)
1876
- }
1877
-
1878
- fn apply_edit(&mut self, range: TextRange, replacement: &str) -> Result<(), YerbaError> {
1879
- let source = self.root.text().to_string();
1880
- let start: usize = range.start().into();
1881
- let end: usize = range.end().into();
1882
-
1883
- let mut new_source = source;
1884
- new_source.replace_range(start..end, replacement);
1885
-
1886
- let path = self.path.take();
1887
- *self = Self::parse(&new_source)?;
1888
- self.path = path;
1889
-
1890
- Ok(())
1891
- }
1892
- }
1893
-
1894
- impl std::fmt::Display for Document {
1895
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1896
- write!(f, "{}", self.root.text())
1897
- }
1898
- }
1899
-
1900
- pub fn collect_selectors(value: &serde_yaml::Value, prefix: &str, selectors: &mut Vec<String>) {
1901
- match value {
1902
- serde_yaml::Value::Mapping(map) => {
1903
- for (key, child) in map {
1904
- if let serde_yaml::Value::String(key_string) = key {
1905
- let selector = if prefix.is_empty() {
1906
- key_string.clone()
1907
- } else {
1908
- format!("{}.{}", prefix, key_string)
1909
- };
1910
-
1911
- selectors.push(selector.clone());
1912
- collect_selectors(child, &selector, selectors);
1913
- }
1914
- }
1915
- }
1916
-
1917
- serde_yaml::Value::Sequence(sequence) => {
1918
- let bracket_prefix = format!("{}[]", prefix);
1919
- selectors.push(bracket_prefix.clone());
1920
-
1921
- for item in sequence {
1922
- collect_selectors(item, &bracket_prefix, selectors);
1923
- }
1924
- }
1925
-
1926
- _ => {}
1927
- }
1928
- }
1929
-
1930
- pub fn node_to_yaml_value(node: &SyntaxNode) -> serde_yaml::Value {
1931
- if let Some(sequence) = node.descendants().find_map(BlockSeq::cast) {
1932
- let map_position = node
1933
- .descendants()
1934
- .find_map(BlockMap::cast)
1935
- .map(|map| map.syntax().text_range().start());
1936
-
1937
- let sequence_position = sequence.syntax().text_range().start();
1938
-
1939
- if map_position.is_none() || sequence_position <= map_position.unwrap() {
1940
- let values: Vec<serde_yaml::Value> = sequence
1941
- .entries()
1942
- .map(|entry| node_to_yaml_value(entry.syntax()))
1943
- .collect();
1944
-
1945
- return serde_yaml::Value::Sequence(values);
1946
- }
1947
- }
1948
-
1949
- if let Some(map) = node.descendants().find_map(BlockMap::cast) {
1950
- let mut mapping = serde_yaml::Mapping::new();
1951
-
1952
- for entry in map.entries() {
1953
- let key = entry
1954
- .key()
1955
- .and_then(|key_node| extract_scalar_text(key_node.syntax()))
1956
- .unwrap_or_default();
1957
-
1958
- let value = entry
1959
- .value()
1960
- .map(|value_node| node_to_yaml_value(value_node.syntax()))
1961
- .unwrap_or(serde_yaml::Value::Null);
1962
-
1963
- mapping.insert(serde_yaml::Value::String(key), value);
1964
- }
1965
-
1966
- return serde_yaml::Value::Mapping(mapping);
1967
- }
1968
-
1969
- if let Some(sequence) = node.descendants().find_map(BlockSeq::cast) {
1970
- let values: Vec<serde_yaml::Value> = sequence
1971
- .entries()
1972
- .map(|entry| node_to_yaml_value(entry.syntax()))
1973
- .collect();
1974
-
1975
- return serde_yaml::Value::Sequence(values);
1976
- }
1977
-
1978
- if let Some(block_scalar) = node
1979
- .descendants()
1980
- .find(|child| child.kind() == SyntaxKind::BLOCK_SCALAR)
1981
- {
1982
- let text = block_scalar
1983
- .descendants_with_tokens()
1984
- .filter_map(|element| element.into_token())
1985
- .find(|token| token.kind() == SyntaxKind::BLOCK_SCALAR_TEXT)
1986
- .map(|token| token.text().to_string())
1987
- .unwrap_or_default();
1988
-
1989
- return serde_yaml::Value::String(text);
1990
- }
1991
-
1992
- if let Some(scalar) = extract_scalar(node) {
1993
- use crate::syntax::{detect_yaml_type, is_yaml_truthy, YerbaValueType};
1994
-
1995
- return match detect_yaml_type(&scalar) {
1996
- YerbaValueType::Null => serde_yaml::Value::Null,
1997
- YerbaValueType::Boolean => serde_yaml::Value::Bool(is_yaml_truthy(&scalar.text)),
1998
-
1999
- YerbaValueType::Integer => scalar
2000
- .text
2001
- .parse::<i64>()
2002
- .map(|n| serde_yaml::Value::Number(serde_yaml::Number::from(n)))
2003
- .unwrap_or(serde_yaml::Value::String(scalar.text)),
2004
-
2005
- YerbaValueType::Float => scalar
2006
- .text
2007
- .parse::<f64>()
2008
- .map(|n| serde_yaml::Value::Number(serde_yaml::Number::from(n)))
2009
- .unwrap_or(serde_yaml::Value::String(scalar.text)),
2010
-
2011
- YerbaValueType::String => serde_yaml::Value::String(scalar.text),
2012
- };
2013
- }
2014
-
2015
- let text = node.text().to_string();
2016
-
2017
- serde_yaml::from_str(&text).unwrap_or(serde_yaml::Value::String(text))
2018
- }
2019
-
2020
- fn parse_condition(condition: &str) -> Option<(String, &str, String)> {
2021
- let (left, operator, right) = if let Some(index) = condition.find(" not_contains ") {
2022
- (
2023
- condition[..index].trim(),
2024
- "not_contains",
2025
- condition[index + 14..].trim(),
2026
- )
2027
- } else if let Some(index) = condition.find(" contains ") {
2028
- (condition[..index].trim(), "contains", condition[index + 10..].trim())
2029
- } else if let Some(index) = condition.find("!=") {
2030
- (condition[..index].trim(), "!=", condition[index + 2..].trim())
2031
- } else if let Some(index) = condition.find("==") {
2032
- (condition[..index].trim(), "==", condition[index + 2..].trim())
2033
- } else {
2034
- return None;
2035
- };
2036
-
2037
- let right = right
2038
- .trim_start_matches('"')
2039
- .trim_end_matches('"')
2040
- .trim_start_matches('\'')
2041
- .trim_end_matches('\'');
2042
-
2043
- Some((left.to_string(), operator, right.to_string()))
2044
- }
2045
-
2046
- fn resolve_segment(node: &SyntaxNode, segment: &crate::selector::SelectorSegment) -> Vec<SyntaxNode> {
2047
- use crate::selector::SelectorSegment;
2048
-
2049
- match segment {
2050
- SelectorSegment::AllItems => {
2051
- if let Some(sequence) = node.descendants().find_map(BlockSeq::cast) {
2052
- sequence.entries().map(|entry| entry.syntax().clone()).collect()
2053
- } else {
2054
- Vec::new()
2055
- }
2056
- }
2057
-
2058
- SelectorSegment::Index(index) => {
2059
- if let Some(sequence) = node.descendants().find_map(BlockSeq::cast) {
2060
- sequence
2061
- .entries()
2062
- .nth(*index)
2063
- .map(|entry| vec![entry.syntax().clone()])
2064
- .unwrap_or_default()
2065
- } else {
2066
- Vec::new()
2067
- }
2068
- }
2069
-
2070
- SelectorSegment::Key(key) => {
2071
- if let Some(map) = node.descendants().find_map(BlockMap::cast) {
2072
- if let Some(entry) = find_entry_by_key(&map, key) {
2073
- if let Some(value) = entry.value() {
2074
- return vec![value.syntax().clone()];
2075
- }
2076
- }
2077
- }
2078
-
2079
- Vec::new()
2080
- }
2081
- }
2082
- }
2083
-
2084
- fn navigate_from_node(node: &SyntaxNode, path: &str) -> Vec<SyntaxNode> {
2085
- let parsed = crate::selector::Selector::parse(path);
2086
- let mut current_nodes = vec![node.clone()];
2087
-
2088
- for segment in parsed.segments() {
2089
- let mut next_nodes = Vec::new();
2090
-
2091
- for current in &current_nodes {
2092
- next_nodes.extend(resolve_segment(current, segment));
2093
- }
2094
-
2095
- current_nodes = next_nodes;
2096
-
2097
- if current_nodes.is_empty() {
2098
- break;
2099
- }
2100
- }
2101
-
2102
- current_nodes
2103
- }
2104
-
2105
- #[derive(Debug, Clone)]
2106
- struct EntryGroup {
2107
- separator: String,
2108
- preceding: String,
2109
- body: String,
2110
- }
2111
-
2112
- impl EntryGroup {
2113
- fn full_text(&self) -> String {
2114
- if self.preceding.is_empty() {
2115
- self.body.clone()
2116
- } else {
2117
- format!("{}\n{}", self.preceding, self.body)
2118
- }
2119
- }
2120
- }
2121
-
2122
- fn collect_entry_groups(parent: &SyntaxNode) -> Vec<EntryGroup> {
2123
- let mut groups: Vec<EntryGroup> = Vec::new();
2124
- let mut buffer = String::new();
2125
-
2126
- for child in parent.children_with_tokens() {
2127
- let is_entry = child.as_node().is_some()
2128
- && matches!(
2129
- child.as_node().unwrap().kind(),
2130
- SyntaxKind::BLOCK_MAP_ENTRY | SyntaxKind::BLOCK_SEQ_ENTRY
2131
- );
2132
-
2133
- if is_entry {
2134
- let entry_text = child.as_node().unwrap().text().to_string();
2135
-
2136
- if groups.is_empty() {
2137
- let preceding = buffer.trim_start_matches('\n').to_string();
2138
-
2139
- groups.push(EntryGroup {
2140
- separator: String::new(),
2141
- preceding,
2142
- body: entry_text,
2143
- });
2144
- } else {
2145
- let (trailing, separator, preceding) = split_at_blank_line(&buffer);
2146
-
2147
- if let Some(last) = groups.last_mut() {
2148
- last.body.push_str(&trailing);
2149
- }
2150
-
2151
- groups.push(EntryGroup {
2152
- separator,
2153
- preceding,
2154
- body: entry_text,
2155
- });
2156
- }
2157
-
2158
- buffer.clear();
2159
- } else {
2160
- let text = match &child {
2161
- rowan::NodeOrToken::Node(node) => node.text().to_string(),
2162
- rowan::NodeOrToken::Token(token) => token.text().to_string(),
2163
- };
2164
-
2165
- buffer.push_str(&text);
2166
- }
2167
- }
2168
-
2169
- if let Some(last) = groups.last_mut() {
2170
- let trimmed = buffer.trim_end_matches(['\n', ' ', '\t']);
2171
-
2172
- if !trimmed.is_empty() {
2173
- last.body.push_str(trimmed);
2174
- }
2175
- }
2176
-
2177
- for group in &mut groups {
2178
- let trimmed = group.body.trim_end_matches(['\n', ' ', '\t']);
2179
-
2180
- group.body = trimmed.to_string();
2181
- }
2182
-
2183
- groups
2184
- }
2185
-
2186
- fn split_at_blank_line(text: &str) -> (String, String, String) {
2187
- if let Some(position) = text.find("\n\n") {
2188
- let trailing = text[..position].to_string();
2189
- let rest = &text[position..];
2190
- let content_start = rest.len() - rest.trim_start_matches('\n').len();
2191
- let separator = rest[..content_start].to_string();
2192
- let preceding = rest[content_start..].trim_end_matches(['\n', ' ', '\t']).to_string();
2193
-
2194
- (trailing, separator, preceding)
2195
- } else {
2196
- (text.to_string(), String::new(), String::new())
2197
- }
2198
- }
2199
-
2200
- fn collect_preceding_sibling_comments(parent: &SyntaxNode) -> (String, Option<rowan::TextSize>) {
2201
- let mut comments: Vec<String> = Vec::new();
2202
- let mut earliest_start = None;
2203
- let mut node = parent.clone();
2204
-
2205
- loop {
2206
- let mut sibling = node.prev_sibling_or_token();
2207
-
2208
- while let Some(ref element) = sibling {
2209
- match element {
2210
- rowan::NodeOrToken::Token(token) => {
2211
- if token.kind() == SyntaxKind::COMMENT {
2212
- comments.push(token.text().to_string());
2213
- earliest_start = Some(token.text_range().start());
2214
- } else if token.kind() == SyntaxKind::WHITESPACE {
2215
- // Keep looking past whitespace
2216
- } else {
2217
- break;
2218
- }
2219
- }
2220
- _ => break,
2221
- }
2222
-
2223
- sibling = match element {
2224
- rowan::NodeOrToken::Token(token) => token.prev_sibling_or_token(),
2225
- rowan::NodeOrToken::Node(node) => node.prev_sibling_or_token(),
2226
- };
2227
- }
2228
-
2229
- if !comments.is_empty() {
2230
- break;
2231
- }
2232
-
2233
- match node.parent() {
2234
- Some(parent)
2235
- if parent.kind() == SyntaxKind::BLOCK
2236
- || parent.kind() == SyntaxKind::DOCUMENT
2237
- || parent.kind() == SyntaxKind::BLOCK_MAP_VALUE =>
2238
- {
2239
- node = parent
2240
- }
2241
- _ => break,
2242
- }
2243
- }
2244
-
2245
- comments.reverse();
2246
- (comments.join("\n"), earliest_start)
2247
- }
2248
-
2249
- fn collect_groups_with_range(parent: &SyntaxNode) -> (Vec<EntryGroup>, TextRange) {
2250
- let mut groups = collect_entry_groups(parent);
2251
-
2252
- let (sibling_comments, earliest_start) = collect_preceding_sibling_comments(parent);
2253
-
2254
- if !sibling_comments.is_empty() {
2255
- if let Some(first) = groups.first_mut() {
2256
- if first.preceding.is_empty() {
2257
- first.preceding = sibling_comments;
2258
- } else {
2259
- first.preceding = format!("{}\n{}", sibling_comments, first.preceding);
2260
- }
2261
- }
2262
- }
2263
-
2264
- let range = match earliest_start {
2265
- Some(start) => TextRange::new(start, parent.text_range().end()),
2266
- None => parent.text_range(),
2267
- };
2268
-
2269
- (groups, range)
2270
- }
2271
-
2272
- fn rebuild_from_groups(groups: &[EntryGroup], indent: &str, preserve_separators: bool) -> String {
2273
- let default_separator = if preserve_separators {
2274
- groups
2275
- .iter()
2276
- .find(|group| !group.separator.is_empty())
2277
- .map(|group| group.separator.clone())
2278
- .unwrap_or_else(|| "\n".to_string())
2279
- } else {
2280
- "\n".to_string()
2281
- };
2282
-
2283
- groups
2284
- .iter()
2285
- .enumerate()
2286
- .map(|(index, group)| {
2287
- if index == 0 {
2288
- group.full_text()
2289
- } else {
2290
- let separator = if preserve_separators && !group.separator.is_empty() {
2291
- &group.separator
2292
- } else {
2293
- &default_separator
2294
- };
2295
-
2296
- if group.preceding.is_empty() {
2297
- format!("{}{}{}", separator, indent, group.body)
2298
- } else {
2299
- format!("{}{}\n{}{}", separator, group.preceding, indent, group.body)
2300
- }
2301
- }
2302
- })
2303
- .collect()
2304
- }