yerba 0.9.2-aarch64-linux-gnu → 0.10.1-aarch64-linux-gnu

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +53 -17
  3. data/exe/aarch64-linux-gnu/yerba +0 -0
  4. data/ext/yerba/include/yerba.h +58 -35
  5. data/ext/yerba/yerba.c +281 -187
  6. data/lib/yerba/3.2/yerba.so +0 -0
  7. data/lib/yerba/3.3/yerba.so +0 -0
  8. data/lib/yerba/3.4/yerba.so +0 -0
  9. data/lib/yerba/4.0/yerba.so +0 -0
  10. data/lib/yerba/collection.rb +4 -4
  11. data/lib/yerba/document.rb +134 -0
  12. data/lib/yerba/formatting.rb +1 -0
  13. data/lib/yerba/location.rb +8 -1
  14. data/lib/yerba/map.rb +16 -6
  15. data/lib/yerba/node.rb +41 -0
  16. data/lib/yerba/query_result.rb +33 -10
  17. data/lib/yerba/scalar.rb +29 -0
  18. data/lib/yerba/selector.rb +3 -1
  19. data/lib/yerba/sequence.rb +38 -33
  20. data/lib/yerba/version.rb +1 -1
  21. data/rust/Cargo.lock +1 -1
  22. data/rust/Cargo.toml +1 -1
  23. data/rust/src/commands/get.rs +12 -12
  24. data/rust/src/commands/init.rs +8 -8
  25. data/rust/src/commands/insert.rs +4 -18
  26. data/rust/src/commands/location.rs +2 -0
  27. data/rust/src/commands/mod.rs +34 -6
  28. data/rust/src/commands/move_item.rs +1 -1
  29. data/rust/src/commands/move_key.rs +8 -8
  30. data/rust/src/commands/rename.rs +1 -1
  31. data/rust/src/commands/selectors.rs +1 -1
  32. data/rust/src/commands/set.rs +14 -6
  33. data/rust/src/commands/sort.rs +5 -1
  34. data/rust/src/document/condition.rs +25 -25
  35. data/rust/src/document/delete.rs +57 -43
  36. data/rust/src/document/get.rs +91 -82
  37. data/rust/src/document/insert.rs +77 -77
  38. data/rust/src/document/mod.rs +202 -76
  39. data/rust/src/document/nodes.rs +162 -0
  40. data/rust/src/document/schema.rs +8 -8
  41. data/rust/src/document/set.rs +49 -26
  42. data/rust/src/document/sort.rs +105 -60
  43. data/rust/src/document/style.rs +50 -49
  44. data/rust/src/document/unique.rs +5 -5
  45. data/rust/src/error.rs +8 -4
  46. data/rust/src/ffi.rs +234 -107
  47. data/rust/src/json.rs +62 -2
  48. data/rust/src/lib.rs +4 -2
  49. data/rust/src/main.rs +2 -1
  50. data/rust/src/selector.rs +48 -7
  51. data/rust/src/syntax.rs +449 -56
  52. data/rust/src/validation.rs +5 -3
  53. data/rust/src/yerbafile.rs +168 -94
  54. metadata +3 -2
@@ -15,16 +15,51 @@ pub struct Yerbafile {
15
15
  pub rules: Vec<Rule>,
16
16
  #[serde(skip)]
17
17
  pub directory: Option<PathBuf>,
18
+ #[serde(skip)]
19
+ pub deprecated_path_keys: Vec<String>,
18
20
  }
19
21
 
20
22
  #[derive(Debug, Clone, Deserialize)]
21
23
  pub struct Rule {
22
24
  pub files: String,
25
+ #[serde(default, deserialize_with = "string_or_list")]
26
+ pub exclude: Vec<String>,
23
27
  #[serde(default)]
24
- pub path: Option<String>,
28
+ #[serde(alias = "path")]
29
+ pub selector: Option<String>,
25
30
  pub pipeline: Vec<PipelineStep>,
26
31
  }
27
32
 
33
+ impl Rule {
34
+ pub fn matches(&self, file: &str) -> bool {
35
+ matches_glob(&self.files, file) && !self.excludes(file)
36
+ }
37
+
38
+ pub fn excludes(&self, file: &str) -> bool {
39
+ self.exclude.iter().any(|pattern| matches_glob(pattern, file))
40
+ }
41
+ }
42
+
43
+ fn matches_glob(pattern: &str, file: &str) -> bool {
44
+ glob::Pattern::new(pattern)
45
+ .map(|pattern| pattern.matches(file) || pattern.matches_path(Path::new(file)))
46
+ .unwrap_or(false)
47
+ }
48
+
49
+ fn string_or_list<'de, D: serde::Deserializer<'de>>(deserializer: D) -> Result<Vec<String>, D::Error> {
50
+ #[derive(Deserialize)]
51
+ #[serde(untagged)]
52
+ enum StringOrList {
53
+ String(String),
54
+ List(Vec<String>),
55
+ }
56
+
57
+ Ok(match StringOrList::deserialize(deserializer)? {
58
+ StringOrList::String(string) => vec![string],
59
+ StringOrList::List(list) => list,
60
+ })
61
+ }
62
+
28
63
  #[derive(Debug, Clone)]
29
64
  pub enum PipelineStep {
30
65
  SortKeys(SortKeysConfig),
@@ -48,17 +83,21 @@ pub enum PipelineStep {
48
83
  #[derive(Debug, Clone, Deserialize)]
49
84
  pub struct SortConfig {
50
85
  #[serde(default)]
51
- pub path: Option<String>,
86
+ #[serde(alias = "path")]
87
+ pub selector: Option<String>,
52
88
  #[serde(default)]
53
89
  pub by: Option<String>,
54
90
  #[serde(default)]
55
91
  pub case_sensitive: bool,
92
+ #[serde(default)]
93
+ pub missing: Option<crate::SortMissing>,
56
94
  }
57
95
 
58
96
  #[derive(Debug, Clone, Deserialize)]
59
97
  pub struct BlankLinesConfig {
60
98
  #[serde(default)]
61
- pub path: Option<String>,
99
+ #[serde(alias = "path")]
100
+ pub selector: Option<String>,
62
101
  pub count: usize,
63
102
  #[serde(default)]
64
103
  pub before: Vec<String>,
@@ -81,7 +120,8 @@ pub struct DirectivesConfig {
81
120
  #[derive(Debug, Clone, Deserialize)]
82
121
  pub struct UniqueConfig {
83
122
  #[serde(default)]
84
- pub path: Option<String>,
123
+ #[serde(alias = "path")]
124
+ pub selector: Option<String>,
85
125
  #[serde(default = "default_dot")]
86
126
  pub by: String,
87
127
  #[serde(default)]
@@ -98,7 +138,8 @@ fn default_dot() -> String {
98
138
  pub struct SchemaConfig {
99
139
  pub file: String,
100
140
  #[serde(default)]
101
- pub path: Option<String>,
141
+ #[serde(alias = "path")]
142
+ pub selector: Option<String>,
102
143
  #[serde(default)]
103
144
  pub items: bool,
104
145
  }
@@ -112,7 +153,8 @@ pub struct FinalNewlineConfig {
112
153
  #[derive(Debug, Clone, Deserialize)]
113
154
  pub struct EscapesConfig {
114
155
  #[serde(default)]
115
- pub path: Option<String>,
156
+ #[serde(alias = "path")]
157
+ pub selector: Option<String>,
116
158
  }
117
159
 
118
160
  fn default_one() -> usize {
@@ -129,7 +171,8 @@ pub struct RenameConfig {
129
171
 
130
172
  #[derive(Debug, Clone, Deserialize)]
131
173
  pub struct RemoveConfig {
132
- pub path: String,
174
+ #[serde(alias = "path")]
175
+ pub selector: String,
133
176
  pub value: String,
134
177
  #[serde(default)]
135
178
  pub condition: Option<String>,
@@ -231,7 +274,8 @@ impl<'de> Deserialize<'de> for PipelineStep {
231
274
  #[derive(Debug, Clone, Deserialize)]
232
275
  pub struct SortKeysConfig {
233
276
  #[serde(default)]
234
- pub path: Option<String>,
277
+ #[serde(alias = "path")]
278
+ pub selector: Option<String>,
235
279
  pub order: Vec<String>,
236
280
  }
237
281
 
@@ -241,26 +285,30 @@ pub struct QuoteStyleConfig {
241
285
  pub key_style: String,
242
286
  pub value_style: String,
243
287
  #[serde(default)]
244
- pub path: Option<String>,
288
+ #[serde(alias = "path")]
289
+ pub selector: Option<String>,
245
290
  }
246
291
 
247
292
  #[derive(Debug, Clone, Deserialize)]
248
293
  pub struct CollectionStyleConfig {
249
294
  pub style: String,
250
295
  #[serde(default)]
251
- pub path: Option<String>,
296
+ #[serde(alias = "path")]
297
+ pub selector: Option<String>,
252
298
  }
253
299
 
254
300
  #[derive(Debug, Clone, Deserialize)]
255
301
  pub struct SequenceIndentConfig {
256
302
  pub style: String,
257
303
  #[serde(default)]
258
- pub path: Option<String>,
304
+ #[serde(alias = "path")]
305
+ pub selector: Option<String>,
259
306
  }
260
307
 
261
308
  #[derive(Debug, Clone, Deserialize)]
262
309
  pub struct SetConfig {
263
- pub path: String,
310
+ #[serde(alias = "path")]
311
+ pub selector: String,
264
312
  pub value: String,
265
313
  #[serde(default)]
266
314
  pub condition: Option<String>,
@@ -270,7 +318,8 @@ pub struct SetConfig {
270
318
 
271
319
  #[derive(Debug, Clone, Deserialize)]
272
320
  pub struct InsertConfig {
273
- pub path: String,
321
+ #[serde(alias = "path")]
322
+ pub selector: String,
274
323
  pub value: String,
275
324
  #[serde(default)]
276
325
  pub condition: Option<String>,
@@ -280,7 +329,8 @@ pub struct InsertConfig {
280
329
 
281
330
  #[derive(Debug, Clone, Deserialize)]
282
331
  pub struct DeleteConfig {
283
- pub path: String,
332
+ #[serde(alias = "path")]
333
+ pub selector: String,
284
334
  #[serde(default)]
285
335
  pub condition: Option<String>,
286
336
  }
@@ -301,6 +351,7 @@ impl Yerbafile {
301
351
  let content = fs::read_to_string(path.as_ref())?;
302
352
  let mut yerbafile: Yerbafile = yaml_serde::from_str(&content).map_err(|error| YerbaError::ParseError(format!("{}", error)))?;
303
353
  yerbafile.directory = path.as_ref().parent().map(|p| p.to_path_buf());
354
+ yerbafile.deprecated_path_keys = deprecated_path_keys(&content);
304
355
  yerbafile.validate_conditions()?;
305
356
  Ok(yerbafile)
306
357
  }
@@ -359,7 +410,7 @@ impl Yerbafile {
359
410
  }
360
411
  }
361
412
 
362
- pub fn sort_order_for(&self, file_path: &str, dot_path: &str) -> Option<Vec<String>> {
413
+ pub fn sort_order_for(&self, file_path: &str, selector: &str) -> Option<Vec<String>> {
363
414
  for rule in &self.rules {
364
415
  let sort_keys_config = rule.pipeline.iter().find_map(|step| match step {
365
416
  PipelineStep::SortKeys(config) => Some(config),
@@ -367,17 +418,15 @@ impl Yerbafile {
367
418
  });
368
419
 
369
420
  if let Some(config) = sort_keys_config {
370
- let full_path = resolve_step_path(rule.path.as_deref(), config.path.as_deref());
371
- let normalized = full_path.trim_end_matches("[]").trim_end_matches('.');
421
+ let full_selector = resolve_step_selector(rule.selector.as_deref(), config.selector.as_deref());
422
+ let normalized = full_selector.trim_end_matches("[]").trim_end_matches('.');
372
423
 
373
- if normalized != dot_path && !normalized.is_empty() && !dot_path.is_empty() {
424
+ if normalized != selector && !normalized.is_empty() && !selector.is_empty() {
374
425
  continue;
375
426
  }
376
427
 
377
- if let Ok(pattern) = glob::Pattern::new(&rule.files) {
378
- if pattern.matches(file_path) || pattern.matches_path(Path::new(file_path)) {
379
- return Some(config.order.clone());
380
- }
428
+ if rule.matches(file_path) {
429
+ return Some(config.order.clone());
381
430
  }
382
431
  }
383
432
  }
@@ -404,7 +453,11 @@ impl Yerbafile {
404
453
  }
405
454
  };
406
455
 
407
- let file_strings: Vec<String> = files.iter().map(|path| path.to_string_lossy().to_string()).collect();
456
+ let file_strings: Vec<String> = files
457
+ .iter()
458
+ .map(|path| path.to_string_lossy().to_string())
459
+ .filter(|file| !rule.excludes(file))
460
+ .collect();
408
461
 
409
462
  let file_results: Vec<RuleResult> = file_strings
410
463
  .par_iter()
@@ -472,10 +525,10 @@ impl Yerbafile {
472
525
  }
473
526
  }
474
527
 
475
- let base_path = rule.path.as_deref();
528
+ let base_selector = rule.selector.as_deref();
476
529
 
477
530
  for step in &rule.pipeline {
478
- if let Err(error) = execute_step(&mut document, step, base_path, file, self) {
531
+ if let Err(error) = execute_step(&mut document, step, base_selector, file, self) {
479
532
  return RuleResult {
480
533
  file: file.to_string(),
481
534
  changed: false,
@@ -509,11 +562,7 @@ impl Yerbafile {
509
562
  let mut ran_global = false;
510
563
 
511
564
  for rule in &self.rules {
512
- if let Ok(pattern) = glob::Pattern::new(&rule.files) {
513
- if !pattern.matches(file) && !pattern.matches_path(Path::new(file)) {
514
- continue;
515
- }
516
- } else {
565
+ if !rule.matches(file) {
517
566
  continue;
518
567
  }
519
568
 
@@ -625,20 +674,14 @@ impl Yerbafile {
625
674
  }
626
675
 
627
676
  for rule in &self.rules {
628
- if !match_path.is_empty() {
629
- if let Ok(pattern) = glob::Pattern::new(&rule.files) {
630
- if !pattern.matches(match_path) && !pattern.matches_path(Path::new(match_path)) {
631
- continue;
632
- }
633
- } else {
634
- continue;
635
- }
677
+ if !match_path.is_empty() && !rule.matches(match_path) {
678
+ continue;
636
679
  }
637
680
 
638
- let base_path = rule.path.as_deref();
681
+ let base_selector = rule.selector.as_deref();
639
682
 
640
683
  for step in &rule.pipeline {
641
- execute_step(document, step, base_path, file_path, self)?;
684
+ execute_step(document, step, base_selector, file_path, self)?;
642
685
  }
643
686
  }
644
687
 
@@ -652,7 +695,7 @@ fn control_character_error(source: &str) -> Option<YerbaError> {
652
695
  (!found.is_empty()).then_some(YerbaError::ControlCharacters(found))
653
696
  }
654
697
 
655
- fn execute_pipeline(document: &mut Document, steps: &[PipelineStep], base_path: Option<&str>, file: &str, yerbafile: &Yerbafile) -> Result<(), YerbaError> {
698
+ fn execute_pipeline(document: &mut Document, steps: &[PipelineStep], base_selector: Option<&str>, file: &str, yerbafile: &Yerbafile) -> Result<(), YerbaError> {
656
699
  use crate::document::style::StyleEnforcement;
657
700
 
658
701
  let mut enforcement = StyleEnforcement {
@@ -667,17 +710,17 @@ fn execute_pipeline(document: &mut Document, steps: &[PipelineStep], base_path:
667
710
 
668
711
  for step in steps {
669
712
  match step {
670
- PipelineStep::CollectionStyle(config) if config.path.is_none() && base_path.is_none() => {
713
+ PipelineStep::CollectionStyle(config) if config.selector.is_none() && base_selector.is_none() => {
671
714
  enforcement.collection_style = Some(config.style.clone());
672
715
  has_enforcement = true;
673
716
  }
674
717
 
675
- PipelineStep::SequenceIndent(config) if config.path.is_none() && base_path.is_none() => {
718
+ PipelineStep::SequenceIndent(config) if config.selector.is_none() && base_selector.is_none() => {
676
719
  enforcement.sequence_indent = Some(config.style.clone());
677
720
  has_enforcement = true;
678
721
  }
679
722
 
680
- PipelineStep::QuoteStyle(config) if config.path.is_none() && base_path.is_none() => {
723
+ PipelineStep::QuoteStyle(config) if config.selector.is_none() && base_selector.is_none() => {
681
724
  let key_style = config.key_style.parse::<crate::KeyStyle>().ok();
682
725
  let value_style = config.value_style.parse::<QuoteStyle>().ok();
683
726
 
@@ -695,23 +738,23 @@ fn execute_pipeline(document: &mut Document, steps: &[PipelineStep], base_path:
695
738
  }
696
739
 
697
740
  for step in remaining_steps {
698
- execute_step(document, step, base_path, file, yerbafile)?;
741
+ execute_step(document, step, base_selector, file, yerbafile)?;
699
742
  }
700
743
 
701
744
  Ok(())
702
745
  }
703
746
 
704
- fn execute_step(document: &mut Document, step: &PipelineStep, base_path: Option<&str>, _file: &str, yerbafile: &Yerbafile) -> Result<(), YerbaError> {
747
+ fn execute_step(document: &mut Document, step: &PipelineStep, base_selector: Option<&str>, _file: &str, yerbafile: &Yerbafile) -> Result<(), YerbaError> {
705
748
  match step {
706
749
  PipelineStep::QuoteStyle(config) => {
707
- let dot_path = config.path.as_deref();
750
+ let selector = config.selector.as_deref();
708
751
 
709
752
  let key_style = config.key_style.parse::<crate::KeyStyle>().map_err(YerbaError::ParseError)?;
710
753
 
711
754
  let value_style = config.value_style.parse::<QuoteStyle>().map_err(YerbaError::ParseError)?;
712
755
 
713
- document.enforce_key_style(&key_style, dot_path)?;
714
- let warnings = document.enforce_quotes_at(&value_style, dot_path)?;
756
+ document.enforce_key_style(&key_style, selector)?;
757
+ let warnings = document.enforce_quotes_at(&value_style, selector)?;
715
758
 
716
759
  for warning in &warnings {
717
760
  eprintln!(" warning: {}", warning);
@@ -721,52 +764,52 @@ fn execute_step(document: &mut Document, step: &PipelineStep, base_path: Option<
721
764
  }
722
765
 
723
766
  PipelineStep::CollectionStyle(config) => {
724
- let dot_path = resolve_step_path(base_path, config.path.as_deref());
725
- let path = if dot_path.is_empty() { None } else { Some(dot_path.as_str()) };
767
+ let selector = resolve_step_selector(base_selector, config.selector.as_deref());
768
+ let path = if selector.is_empty() { None } else { Some(selector.as_str()) };
726
769
 
727
770
  document.enforce_collection_style(&config.style, path)
728
771
  }
729
772
 
730
773
  PipelineStep::SequenceIndent(config) => {
731
- let dot_path = resolve_step_path(base_path, config.path.as_deref());
732
- let path = if dot_path.is_empty() { None } else { Some(dot_path.as_str()) };
774
+ let selector = resolve_step_selector(base_selector, config.selector.as_deref());
775
+ let path = if selector.is_empty() { None } else { Some(selector.as_str()) };
733
776
 
734
777
  document.enforce_sequence_indent(&config.style, path)
735
778
  }
736
779
 
737
780
  PipelineStep::SortKeys(config) => {
738
- let full_path = resolve_step_path(base_path, config.path.as_deref());
781
+ let full_selector = resolve_step_selector(base_selector, config.selector.as_deref());
739
782
  let key_order: Vec<&str> = config.order.iter().map(|key| key.as_str()).collect();
740
783
 
741
- document.validate_sort_keys(&full_path, &key_order)?;
742
- document.sort_keys(&full_path, &key_order)
784
+ document.validate_sort_keys(&full_selector, &key_order)?;
785
+ document.sort_keys(&full_selector, &key_order)
743
786
  }
744
787
 
745
788
  PipelineStep::Set(config) => {
746
- let full_path = resolve_step_path(base_path, Some(&config.path));
789
+ let full_selector = resolve_step_selector(base_selector, Some(&config.selector));
747
790
 
748
791
  if let Some(condition) = &config.condition {
749
- let parent_path = full_path.rsplit_once('.').map(|(parent, _)| parent).unwrap_or("");
792
+ let parent_selector = full_selector.rsplit_once('.').map(|(parent, _)| parent).unwrap_or("");
750
793
 
751
- if !document.evaluate_condition(parent_path, condition)? {
794
+ if !document.evaluate_condition(parent_selector, condition)? {
752
795
  return Ok(());
753
796
  }
754
797
  }
755
798
 
756
799
  if config.plain {
757
- document.set_plain(&full_path, &config.value)
800
+ document.set_plain(&full_selector, &config.value)
758
801
  } else {
759
- document.set(&full_path, &config.value)
802
+ document.set(&full_selector, &config.value)
760
803
  }
761
804
  }
762
805
 
763
806
  PipelineStep::Insert(config) => {
764
- let full_path = resolve_step_path(base_path, Some(&config.path));
807
+ let full_selector = resolve_step_selector(base_selector, Some(&config.selector));
765
808
 
766
809
  if let Some(condition) = &config.condition {
767
- let parent_path = full_path.rsplit_once('.').map(|(parent, _)| parent).unwrap_or("");
810
+ let parent_selector = full_selector.rsplit_once('.').map(|(parent, _)| parent).unwrap_or("");
768
811
 
769
- if !document.evaluate_condition(parent_path, condition)? {
812
+ if !document.evaluate_condition(parent_selector, condition)? {
770
813
  return Ok(());
771
814
  }
772
815
  }
@@ -777,20 +820,20 @@ fn execute_step(document: &mut Document, step: &PipelineStep, base_path: Option<
777
820
  crate::syntax::quote_scalar(&config.value)
778
821
  };
779
822
 
780
- document.insert_into(&full_path, &value, crate::InsertPosition::Last)
823
+ document.insert_into(&full_selector, &value, crate::InsertPosition::Last)
781
824
  }
782
825
 
783
826
  PipelineStep::Delete(config) => {
784
- let full_path = resolve_step_path(base_path, Some(&config.path));
827
+ let full_selector = resolve_step_selector(base_selector, Some(&config.selector));
785
828
 
786
- if full_path.contains("[]") {
787
- let concrete_selectors = document.resolve_selectors(&full_path);
829
+ if full_selector.contains("[]") {
830
+ let concrete_selectors = document.resolve_selectors(&full_selector);
788
831
 
789
832
  for selector in concrete_selectors.into_iter().rev() {
790
833
  if let Some(condition) = &config.condition {
791
- let parent_path = selector.rsplit_once('.').map(|(parent, _)| parent).unwrap_or("");
834
+ let parent_selector = selector.rsplit_once('.').map(|(parent, _)| parent).unwrap_or("");
792
835
 
793
- if !document.evaluate_condition(parent_path, condition)? {
836
+ if !document.evaluate_condition(parent_selector, condition)? {
794
837
  continue;
795
838
  }
796
839
  }
@@ -801,47 +844,47 @@ fn execute_step(document: &mut Document, step: &PipelineStep, base_path: Option<
801
844
  Ok(())
802
845
  } else {
803
846
  if let Some(condition) = &config.condition {
804
- let parent_path = full_path.rsplit_once('.').map(|(parent, _)| parent).unwrap_or("");
847
+ let parent_selector = full_selector.rsplit_once('.').map(|(parent, _)| parent).unwrap_or("");
805
848
 
806
- if !document.evaluate_condition(parent_path, condition)? {
849
+ if !document.evaluate_condition(parent_selector, condition)? {
807
850
  return Ok(());
808
851
  }
809
852
  }
810
853
 
811
- document.delete(&full_path)
854
+ document.delete(&full_selector)
812
855
  }
813
856
  }
814
857
 
815
858
  PipelineStep::Rename(config) => {
816
- let full_path = resolve_step_path(base_path, Some(&config.from));
859
+ let full_selector = resolve_step_selector(base_selector, Some(&config.from));
817
860
 
818
861
  if let Some(condition) = &config.condition {
819
- let parent_path = full_path.rsplit_once('.').map(|(parent, _)| parent).unwrap_or("");
862
+ let parent_selector = full_selector.rsplit_once('.').map(|(parent, _)| parent).unwrap_or("");
820
863
 
821
- if !document.evaluate_condition(parent_path, condition)? {
864
+ if !document.evaluate_condition(parent_selector, condition)? {
822
865
  return Ok(());
823
866
  }
824
867
  }
825
868
 
826
- document.rename(&full_path, &config.to)
869
+ document.rename(&full_selector, &config.to)
827
870
  }
828
871
 
829
872
  PipelineStep::Remove(config) => {
830
- let full_path = resolve_step_path(base_path, Some(&config.path));
873
+ let full_selector = resolve_step_selector(base_selector, Some(&config.selector));
831
874
 
832
875
  if let Some(condition) = &config.condition {
833
- let parent_path = full_path.rsplit_once('.').map(|(parent, _)| parent).unwrap_or("");
876
+ let parent_selector = full_selector.rsplit_once('.').map(|(parent, _)| parent).unwrap_or("");
834
877
 
835
- if !document.evaluate_condition(parent_path, condition)? {
878
+ if !document.evaluate_condition(parent_selector, condition)? {
836
879
  return Ok(());
837
880
  }
838
881
  }
839
882
 
840
- document.remove(&full_path, &config.value)
883
+ document.remove(&full_selector, &config.value)
841
884
  }
842
885
 
843
886
  PipelineStep::BlankLines(config) => {
844
- let full_path = resolve_step_path(base_path, config.path.as_deref());
887
+ let full_selector = resolve_step_selector(base_selector, config.selector.as_deref());
845
888
 
846
889
  let options = crate::BlankLineOptions {
847
890
  before: config.before.clone(),
@@ -849,14 +892,14 @@ fn execute_step(document: &mut Document, step: &PipelineStep, base_path: Option<
849
892
  skip_empty: config.skip_empty,
850
893
  };
851
894
 
852
- document.enforce_blank_lines_with(&full_path, config.count, &options)
895
+ document.enforce_blank_lines_with(&full_selector, config.count, &options)
853
896
  }
854
897
 
855
898
  PipelineStep::Sort(config) => {
856
- let full_path = resolve_step_path(base_path, config.path.as_deref());
899
+ let full_selector = resolve_step_selector(base_selector, config.selector.as_deref());
857
900
  let sort_fields = config.by.as_deref().map(crate::SortField::parse_list).unwrap_or_default();
858
901
 
859
- document.sort_items(&full_path, &sort_fields, config.case_sensitive)
902
+ document.sort_items_with(&full_selector, &sort_fields, config.case_sensitive, config.missing)
860
903
  }
861
904
 
862
905
  PipelineStep::Directives(config) => {
@@ -868,7 +911,7 @@ fn execute_step(document: &mut Document, step: &PipelineStep, base_path: Option<
868
911
  let locations = document.directive_locations();
869
912
 
870
913
  if locations.len() > max {
871
- let positions: Vec<String> = locations.iter().map(|(line, col)| format!("{}:{}", line, col)).collect();
914
+ let positions: Vec<String> = locations.iter().map(|(line, column)| format!("{}:{}", line, column + 1)).collect();
872
915
 
873
916
  return Err(YerbaError::ParseError(format!(
874
917
  "found {} directive markers (---) at {}, expected at most {}",
@@ -891,7 +934,7 @@ fn execute_step(document: &mut Document, step: &PipelineStep, base_path: Option<
891
934
  PipelineStep::Schema(config) => {
892
935
  let schema_path = yerbafile.resolve_path(&config.file);
893
936
  let schema = crate::schema::load_schema(&schema_path)?;
894
- let selector = resolve_step_path(base_path, config.path.as_deref());
937
+ let selector = resolve_step_selector(base_selector, config.selector.as_deref());
895
938
  let errors = document.validate_schema(&schema, config.items, if selector.is_empty() { None } else { Some(&selector) });
896
939
 
897
940
  if errors.is_empty() {
@@ -902,8 +945,8 @@ fn execute_step(document: &mut Document, step: &PipelineStep, base_path: Option<
902
945
  }
903
946
 
904
947
  PipelineStep::Unique(config) => {
905
- let full_path = resolve_step_path(base_path, config.path.as_deref());
906
- let duplicates = document.unique_with_options(&full_path, &config.by, config.remove, config.allow_blank_duplicates)?;
948
+ let full_selector = resolve_step_selector(base_selector, config.selector.as_deref());
949
+ let duplicates = document.unique_with_options(&full_selector, &config.by, config.remove, config.allow_blank_duplicates)?;
907
950
 
908
951
  if !duplicates.is_empty() && !config.remove {
909
952
  return Err(YerbaError::DuplicateValues(duplicates));
@@ -915,17 +958,17 @@ fn execute_step(document: &mut Document, step: &PipelineStep, base_path: Option<
915
958
  PipelineStep::FinalNewline(config) => document.enforce_final_newline(config.count),
916
959
 
917
960
  PipelineStep::Escapes(config) => {
918
- let full_path = resolve_step_path(base_path, config.path.as_deref());
919
- let scope = if full_path.is_empty() { None } else { Some(full_path.as_str()) };
961
+ let full_selector = resolve_step_selector(base_selector, config.selector.as_deref());
962
+ let scope = if full_selector.is_empty() { None } else { Some(full_selector.as_str()) };
920
963
 
921
964
  document.decode_escapes(scope).map(|_| ())
922
965
  }
923
966
  }
924
967
  }
925
968
 
926
- fn resolve_step_path(base_path: Option<&str>, step_path: Option<&str>) -> String {
927
- let base = base_path.unwrap_or("");
928
- let step = step_path.unwrap_or("");
969
+ fn resolve_step_selector(base_selector: Option<&str>, step_selector: Option<&str>) -> String {
970
+ let base = base_selector.unwrap_or("");
971
+ let step = step_selector.unwrap_or("");
929
972
 
930
973
  match (base.is_empty(), step.is_empty()) {
931
974
  (true, true) => String::new(),
@@ -934,3 +977,34 @@ fn resolve_step_path(base_path: Option<&str>, step_path: Option<&str>) -> String
934
977
  (false, false) => format!("{}.{}", base, step),
935
978
  }
936
979
  }
980
+
981
+ // TODO(v1.0): remove together with the `path` aliases on the rule and step configs.
982
+ fn deprecated_path_keys(content: &str) -> Vec<String> {
983
+ let Ok(yerbafile) = yaml_serde::from_str::<yaml_serde::Value>(content) else {
984
+ return Vec::new();
985
+ };
986
+
987
+ let mut found = Vec::new();
988
+
989
+ let check_pipeline = |pipeline: Option<&yaml_serde::Value>, prefix: &str, found: &mut Vec<String>| {
990
+ for (index, step) in pipeline.and_then(|pipeline| pipeline.as_sequence()).into_iter().flatten().enumerate() {
991
+ for (name, config) in step.as_mapping().into_iter().flatten() {
992
+ if config.get("path").is_some() {
993
+ found.push(format!("{}pipeline[{}].{}", prefix, index, name.as_str().unwrap_or_default()));
994
+ }
995
+ }
996
+ }
997
+ };
998
+
999
+ check_pipeline(yerbafile.get("pipeline"), "", &mut found);
1000
+
1001
+ for (index, rule) in yerbafile.get("rules").and_then(|rules| rules.as_sequence()).into_iter().flatten().enumerate() {
1002
+ if rule.get("path").is_some() {
1003
+ found.push(format!("rules[{}]", index));
1004
+ }
1005
+
1006
+ check_pipeline(rule.get("pipeline"), &format!("rules[{}].", index), &mut found);
1007
+ }
1008
+
1009
+ found
1010
+ }
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: yerba
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.2
4
+ version: 0.10.1
5
5
  platform: aarch64-linux-gnu
6
6
  authors:
7
7
  - Marco Roth
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-10-13 00:00:00.000000000 Z
11
+ date: 2026-10-14 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: A CLI tool for editing YAML while preserving structure, comments, and
14
14
  format.
@@ -78,6 +78,7 @@ files:
78
78
  - rust/src/document/get.rs
79
79
  - rust/src/document/insert.rs
80
80
  - rust/src/document/mod.rs
81
+ - rust/src/document/nodes.rs
81
82
  - rust/src/document/schema.rs
82
83
  - rust/src/document/set.rs
83
84
  - rust/src/document/sort.rs