yerba 0.9.1-arm-linux-gnu → 0.10.0-arm-linux-gnu

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +60 -16
  3. data/exe/arm-linux-gnu/yerba +0 -0
  4. data/ext/yerba/include/yerba.h +67 -32
  5. data/ext/yerba/yerba.c +296 -186
  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 +142 -0
  19. data/lib/yerba/sequence.rb +38 -33
  20. data/lib/yerba/version.rb +1 -1
  21. data/lib/yerba.rb +1 -0
  22. data/rust/Cargo.lock +1 -1
  23. data/rust/Cargo.toml +1 -1
  24. data/rust/src/commands/get.rs +12 -12
  25. data/rust/src/commands/init.rs +8 -8
  26. data/rust/src/commands/insert.rs +4 -18
  27. data/rust/src/commands/location.rs +2 -0
  28. data/rust/src/commands/mod.rs +34 -6
  29. data/rust/src/commands/move_item.rs +1 -1
  30. data/rust/src/commands/move_key.rs +8 -8
  31. data/rust/src/commands/rename.rs +1 -1
  32. data/rust/src/commands/selectors.rs +1 -1
  33. data/rust/src/commands/set.rs +14 -6
  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 +138 -64
  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 +54 -54
  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 +272 -102
  47. data/rust/src/json.rs +62 -2
  48. data/rust/src/lib.rs +2 -1
  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 +124 -76
  54. metadata +4 -2
@@ -15,13 +15,16 @@ 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,
23
25
  #[serde(default)]
24
- pub path: Option<String>,
26
+ #[serde(alias = "path")]
27
+ pub selector: Option<String>,
25
28
  pub pipeline: Vec<PipelineStep>,
26
29
  }
27
30
 
@@ -48,7 +51,8 @@ pub enum PipelineStep {
48
51
  #[derive(Debug, Clone, Deserialize)]
49
52
  pub struct SortConfig {
50
53
  #[serde(default)]
51
- pub path: Option<String>,
54
+ #[serde(alias = "path")]
55
+ pub selector: Option<String>,
52
56
  #[serde(default)]
53
57
  pub by: Option<String>,
54
58
  #[serde(default)]
@@ -58,7 +62,8 @@ pub struct SortConfig {
58
62
  #[derive(Debug, Clone, Deserialize)]
59
63
  pub struct BlankLinesConfig {
60
64
  #[serde(default)]
61
- pub path: Option<String>,
65
+ #[serde(alias = "path")]
66
+ pub selector: Option<String>,
62
67
  pub count: usize,
63
68
  #[serde(default)]
64
69
  pub before: Vec<String>,
@@ -81,7 +86,8 @@ pub struct DirectivesConfig {
81
86
  #[derive(Debug, Clone, Deserialize)]
82
87
  pub struct UniqueConfig {
83
88
  #[serde(default)]
84
- pub path: Option<String>,
89
+ #[serde(alias = "path")]
90
+ pub selector: Option<String>,
85
91
  #[serde(default = "default_dot")]
86
92
  pub by: String,
87
93
  #[serde(default)]
@@ -98,7 +104,8 @@ fn default_dot() -> String {
98
104
  pub struct SchemaConfig {
99
105
  pub file: String,
100
106
  #[serde(default)]
101
- pub path: Option<String>,
107
+ #[serde(alias = "path")]
108
+ pub selector: Option<String>,
102
109
  #[serde(default)]
103
110
  pub items: bool,
104
111
  }
@@ -112,7 +119,8 @@ pub struct FinalNewlineConfig {
112
119
  #[derive(Debug, Clone, Deserialize)]
113
120
  pub struct EscapesConfig {
114
121
  #[serde(default)]
115
- pub path: Option<String>,
122
+ #[serde(alias = "path")]
123
+ pub selector: Option<String>,
116
124
  }
117
125
 
118
126
  fn default_one() -> usize {
@@ -129,7 +137,8 @@ pub struct RenameConfig {
129
137
 
130
138
  #[derive(Debug, Clone, Deserialize)]
131
139
  pub struct RemoveConfig {
132
- pub path: String,
140
+ #[serde(alias = "path")]
141
+ pub selector: String,
133
142
  pub value: String,
134
143
  #[serde(default)]
135
144
  pub condition: Option<String>,
@@ -231,7 +240,8 @@ impl<'de> Deserialize<'de> for PipelineStep {
231
240
  #[derive(Debug, Clone, Deserialize)]
232
241
  pub struct SortKeysConfig {
233
242
  #[serde(default)]
234
- pub path: Option<String>,
243
+ #[serde(alias = "path")]
244
+ pub selector: Option<String>,
235
245
  pub order: Vec<String>,
236
246
  }
237
247
 
@@ -241,26 +251,30 @@ pub struct QuoteStyleConfig {
241
251
  pub key_style: String,
242
252
  pub value_style: String,
243
253
  #[serde(default)]
244
- pub path: Option<String>,
254
+ #[serde(alias = "path")]
255
+ pub selector: Option<String>,
245
256
  }
246
257
 
247
258
  #[derive(Debug, Clone, Deserialize)]
248
259
  pub struct CollectionStyleConfig {
249
260
  pub style: String,
250
261
  #[serde(default)]
251
- pub path: Option<String>,
262
+ #[serde(alias = "path")]
263
+ pub selector: Option<String>,
252
264
  }
253
265
 
254
266
  #[derive(Debug, Clone, Deserialize)]
255
267
  pub struct SequenceIndentConfig {
256
268
  pub style: String,
257
269
  #[serde(default)]
258
- pub path: Option<String>,
270
+ #[serde(alias = "path")]
271
+ pub selector: Option<String>,
259
272
  }
260
273
 
261
274
  #[derive(Debug, Clone, Deserialize)]
262
275
  pub struct SetConfig {
263
- pub path: String,
276
+ #[serde(alias = "path")]
277
+ pub selector: String,
264
278
  pub value: String,
265
279
  #[serde(default)]
266
280
  pub condition: Option<String>,
@@ -270,7 +284,8 @@ pub struct SetConfig {
270
284
 
271
285
  #[derive(Debug, Clone, Deserialize)]
272
286
  pub struct InsertConfig {
273
- pub path: String,
287
+ #[serde(alias = "path")]
288
+ pub selector: String,
274
289
  pub value: String,
275
290
  #[serde(default)]
276
291
  pub condition: Option<String>,
@@ -280,7 +295,8 @@ pub struct InsertConfig {
280
295
 
281
296
  #[derive(Debug, Clone, Deserialize)]
282
297
  pub struct DeleteConfig {
283
- pub path: String,
298
+ #[serde(alias = "path")]
299
+ pub selector: String,
284
300
  #[serde(default)]
285
301
  pub condition: Option<String>,
286
302
  }
@@ -301,6 +317,7 @@ impl Yerbafile {
301
317
  let content = fs::read_to_string(path.as_ref())?;
302
318
  let mut yerbafile: Yerbafile = yaml_serde::from_str(&content).map_err(|error| YerbaError::ParseError(format!("{}", error)))?;
303
319
  yerbafile.directory = path.as_ref().parent().map(|p| p.to_path_buf());
320
+ yerbafile.deprecated_path_keys = deprecated_path_keys(&content);
304
321
  yerbafile.validate_conditions()?;
305
322
  Ok(yerbafile)
306
323
  }
@@ -359,7 +376,7 @@ impl Yerbafile {
359
376
  }
360
377
  }
361
378
 
362
- pub fn sort_order_for(&self, file_path: &str, dot_path: &str) -> Option<Vec<String>> {
379
+ pub fn sort_order_for(&self, file_path: &str, selector: &str) -> Option<Vec<String>> {
363
380
  for rule in &self.rules {
364
381
  let sort_keys_config = rule.pipeline.iter().find_map(|step| match step {
365
382
  PipelineStep::SortKeys(config) => Some(config),
@@ -367,10 +384,10 @@ impl Yerbafile {
367
384
  });
368
385
 
369
386
  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('.');
387
+ let full_selector = resolve_step_selector(rule.selector.as_deref(), config.selector.as_deref());
388
+ let normalized = full_selector.trim_end_matches("[]").trim_end_matches('.');
372
389
 
373
- if normalized != dot_path && !normalized.is_empty() && !dot_path.is_empty() {
390
+ if normalized != selector && !normalized.is_empty() && !selector.is_empty() {
374
391
  continue;
375
392
  }
376
393
 
@@ -472,10 +489,10 @@ impl Yerbafile {
472
489
  }
473
490
  }
474
491
 
475
- let base_path = rule.path.as_deref();
492
+ let base_selector = rule.selector.as_deref();
476
493
 
477
494
  for step in &rule.pipeline {
478
- if let Err(error) = execute_step(&mut document, step, base_path, file, self) {
495
+ if let Err(error) = execute_step(&mut document, step, base_selector, file, self) {
479
496
  return RuleResult {
480
497
  file: file.to_string(),
481
498
  changed: false,
@@ -635,10 +652,10 @@ impl Yerbafile {
635
652
  }
636
653
  }
637
654
 
638
- let base_path = rule.path.as_deref();
655
+ let base_selector = rule.selector.as_deref();
639
656
 
640
657
  for step in &rule.pipeline {
641
- execute_step(document, step, base_path, file_path, self)?;
658
+ execute_step(document, step, base_selector, file_path, self)?;
642
659
  }
643
660
  }
644
661
 
@@ -652,7 +669,7 @@ fn control_character_error(source: &str) -> Option<YerbaError> {
652
669
  (!found.is_empty()).then_some(YerbaError::ControlCharacters(found))
653
670
  }
654
671
 
655
- fn execute_pipeline(document: &mut Document, steps: &[PipelineStep], base_path: Option<&str>, file: &str, yerbafile: &Yerbafile) -> Result<(), YerbaError> {
672
+ fn execute_pipeline(document: &mut Document, steps: &[PipelineStep], base_selector: Option<&str>, file: &str, yerbafile: &Yerbafile) -> Result<(), YerbaError> {
656
673
  use crate::document::style::StyleEnforcement;
657
674
 
658
675
  let mut enforcement = StyleEnforcement {
@@ -667,17 +684,17 @@ fn execute_pipeline(document: &mut Document, steps: &[PipelineStep], base_path:
667
684
 
668
685
  for step in steps {
669
686
  match step {
670
- PipelineStep::CollectionStyle(config) if config.path.is_none() && base_path.is_none() => {
687
+ PipelineStep::CollectionStyle(config) if config.selector.is_none() && base_selector.is_none() => {
671
688
  enforcement.collection_style = Some(config.style.clone());
672
689
  has_enforcement = true;
673
690
  }
674
691
 
675
- PipelineStep::SequenceIndent(config) if config.path.is_none() && base_path.is_none() => {
692
+ PipelineStep::SequenceIndent(config) if config.selector.is_none() && base_selector.is_none() => {
676
693
  enforcement.sequence_indent = Some(config.style.clone());
677
694
  has_enforcement = true;
678
695
  }
679
696
 
680
- PipelineStep::QuoteStyle(config) if config.path.is_none() && base_path.is_none() => {
697
+ PipelineStep::QuoteStyle(config) if config.selector.is_none() && base_selector.is_none() => {
681
698
  let key_style = config.key_style.parse::<crate::KeyStyle>().ok();
682
699
  let value_style = config.value_style.parse::<QuoteStyle>().ok();
683
700
 
@@ -695,23 +712,23 @@ fn execute_pipeline(document: &mut Document, steps: &[PipelineStep], base_path:
695
712
  }
696
713
 
697
714
  for step in remaining_steps {
698
- execute_step(document, step, base_path, file, yerbafile)?;
715
+ execute_step(document, step, base_selector, file, yerbafile)?;
699
716
  }
700
717
 
701
718
  Ok(())
702
719
  }
703
720
 
704
- fn execute_step(document: &mut Document, step: &PipelineStep, base_path: Option<&str>, _file: &str, yerbafile: &Yerbafile) -> Result<(), YerbaError> {
721
+ fn execute_step(document: &mut Document, step: &PipelineStep, base_selector: Option<&str>, _file: &str, yerbafile: &Yerbafile) -> Result<(), YerbaError> {
705
722
  match step {
706
723
  PipelineStep::QuoteStyle(config) => {
707
- let dot_path = config.path.as_deref();
724
+ let selector = config.selector.as_deref();
708
725
 
709
726
  let key_style = config.key_style.parse::<crate::KeyStyle>().map_err(YerbaError::ParseError)?;
710
727
 
711
728
  let value_style = config.value_style.parse::<QuoteStyle>().map_err(YerbaError::ParseError)?;
712
729
 
713
- document.enforce_key_style(&key_style, dot_path)?;
714
- let warnings = document.enforce_quotes_at(&value_style, dot_path)?;
730
+ document.enforce_key_style(&key_style, selector)?;
731
+ let warnings = document.enforce_quotes_at(&value_style, selector)?;
715
732
 
716
733
  for warning in &warnings {
717
734
  eprintln!(" warning: {}", warning);
@@ -721,52 +738,52 @@ fn execute_step(document: &mut Document, step: &PipelineStep, base_path: Option<
721
738
  }
722
739
 
723
740
  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()) };
741
+ let selector = resolve_step_selector(base_selector, config.selector.as_deref());
742
+ let path = if selector.is_empty() { None } else { Some(selector.as_str()) };
726
743
 
727
744
  document.enforce_collection_style(&config.style, path)
728
745
  }
729
746
 
730
747
  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()) };
748
+ let selector = resolve_step_selector(base_selector, config.selector.as_deref());
749
+ let path = if selector.is_empty() { None } else { Some(selector.as_str()) };
733
750
 
734
751
  document.enforce_sequence_indent(&config.style, path)
735
752
  }
736
753
 
737
754
  PipelineStep::SortKeys(config) => {
738
- let full_path = resolve_step_path(base_path, config.path.as_deref());
755
+ let full_selector = resolve_step_selector(base_selector, config.selector.as_deref());
739
756
  let key_order: Vec<&str> = config.order.iter().map(|key| key.as_str()).collect();
740
757
 
741
- document.validate_sort_keys(&full_path, &key_order)?;
742
- document.sort_keys(&full_path, &key_order)
758
+ document.validate_sort_keys(&full_selector, &key_order)?;
759
+ document.sort_keys(&full_selector, &key_order)
743
760
  }
744
761
 
745
762
  PipelineStep::Set(config) => {
746
- let full_path = resolve_step_path(base_path, Some(&config.path));
763
+ let full_selector = resolve_step_selector(base_selector, Some(&config.selector));
747
764
 
748
765
  if let Some(condition) = &config.condition {
749
- let parent_path = full_path.rsplit_once('.').map(|(parent, _)| parent).unwrap_or("");
766
+ let parent_selector = full_selector.rsplit_once('.').map(|(parent, _)| parent).unwrap_or("");
750
767
 
751
- if !document.evaluate_condition(parent_path, condition)? {
768
+ if !document.evaluate_condition(parent_selector, condition)? {
752
769
  return Ok(());
753
770
  }
754
771
  }
755
772
 
756
773
  if config.plain {
757
- document.set_plain(&full_path, &config.value)
774
+ document.set_plain(&full_selector, &config.value)
758
775
  } else {
759
- document.set(&full_path, &config.value)
776
+ document.set(&full_selector, &config.value)
760
777
  }
761
778
  }
762
779
 
763
780
  PipelineStep::Insert(config) => {
764
- let full_path = resolve_step_path(base_path, Some(&config.path));
781
+ let full_selector = resolve_step_selector(base_selector, Some(&config.selector));
765
782
 
766
783
  if let Some(condition) = &config.condition {
767
- let parent_path = full_path.rsplit_once('.').map(|(parent, _)| parent).unwrap_or("");
784
+ let parent_selector = full_selector.rsplit_once('.').map(|(parent, _)| parent).unwrap_or("");
768
785
 
769
- if !document.evaluate_condition(parent_path, condition)? {
786
+ if !document.evaluate_condition(parent_selector, condition)? {
770
787
  return Ok(());
771
788
  }
772
789
  }
@@ -777,20 +794,20 @@ fn execute_step(document: &mut Document, step: &PipelineStep, base_path: Option<
777
794
  crate::syntax::quote_scalar(&config.value)
778
795
  };
779
796
 
780
- document.insert_into(&full_path, &value, crate::InsertPosition::Last)
797
+ document.insert_into(&full_selector, &value, crate::InsertPosition::Last)
781
798
  }
782
799
 
783
800
  PipelineStep::Delete(config) => {
784
- let full_path = resolve_step_path(base_path, Some(&config.path));
801
+ let full_selector = resolve_step_selector(base_selector, Some(&config.selector));
785
802
 
786
- if full_path.contains("[]") {
787
- let concrete_selectors = document.resolve_selectors(&full_path);
803
+ if full_selector.contains("[]") {
804
+ let concrete_selectors = document.resolve_selectors(&full_selector);
788
805
 
789
806
  for selector in concrete_selectors.into_iter().rev() {
790
807
  if let Some(condition) = &config.condition {
791
- let parent_path = selector.rsplit_once('.').map(|(parent, _)| parent).unwrap_or("");
808
+ let parent_selector = selector.rsplit_once('.').map(|(parent, _)| parent).unwrap_or("");
792
809
 
793
- if !document.evaluate_condition(parent_path, condition)? {
810
+ if !document.evaluate_condition(parent_selector, condition)? {
794
811
  continue;
795
812
  }
796
813
  }
@@ -801,47 +818,47 @@ fn execute_step(document: &mut Document, step: &PipelineStep, base_path: Option<
801
818
  Ok(())
802
819
  } else {
803
820
  if let Some(condition) = &config.condition {
804
- let parent_path = full_path.rsplit_once('.').map(|(parent, _)| parent).unwrap_or("");
821
+ let parent_selector = full_selector.rsplit_once('.').map(|(parent, _)| parent).unwrap_or("");
805
822
 
806
- if !document.evaluate_condition(parent_path, condition)? {
823
+ if !document.evaluate_condition(parent_selector, condition)? {
807
824
  return Ok(());
808
825
  }
809
826
  }
810
827
 
811
- document.delete(&full_path)
828
+ document.delete(&full_selector)
812
829
  }
813
830
  }
814
831
 
815
832
  PipelineStep::Rename(config) => {
816
- let full_path = resolve_step_path(base_path, Some(&config.from));
833
+ let full_selector = resolve_step_selector(base_selector, Some(&config.from));
817
834
 
818
835
  if let Some(condition) = &config.condition {
819
- let parent_path = full_path.rsplit_once('.').map(|(parent, _)| parent).unwrap_or("");
836
+ let parent_selector = full_selector.rsplit_once('.').map(|(parent, _)| parent).unwrap_or("");
820
837
 
821
- if !document.evaluate_condition(parent_path, condition)? {
838
+ if !document.evaluate_condition(parent_selector, condition)? {
822
839
  return Ok(());
823
840
  }
824
841
  }
825
842
 
826
- document.rename(&full_path, &config.to)
843
+ document.rename(&full_selector, &config.to)
827
844
  }
828
845
 
829
846
  PipelineStep::Remove(config) => {
830
- let full_path = resolve_step_path(base_path, Some(&config.path));
847
+ let full_selector = resolve_step_selector(base_selector, Some(&config.selector));
831
848
 
832
849
  if let Some(condition) = &config.condition {
833
- let parent_path = full_path.rsplit_once('.').map(|(parent, _)| parent).unwrap_or("");
850
+ let parent_selector = full_selector.rsplit_once('.').map(|(parent, _)| parent).unwrap_or("");
834
851
 
835
- if !document.evaluate_condition(parent_path, condition)? {
852
+ if !document.evaluate_condition(parent_selector, condition)? {
836
853
  return Ok(());
837
854
  }
838
855
  }
839
856
 
840
- document.remove(&full_path, &config.value)
857
+ document.remove(&full_selector, &config.value)
841
858
  }
842
859
 
843
860
  PipelineStep::BlankLines(config) => {
844
- let full_path = resolve_step_path(base_path, config.path.as_deref());
861
+ let full_selector = resolve_step_selector(base_selector, config.selector.as_deref());
845
862
 
846
863
  let options = crate::BlankLineOptions {
847
864
  before: config.before.clone(),
@@ -849,14 +866,14 @@ fn execute_step(document: &mut Document, step: &PipelineStep, base_path: Option<
849
866
  skip_empty: config.skip_empty,
850
867
  };
851
868
 
852
- document.enforce_blank_lines_with(&full_path, config.count, &options)
869
+ document.enforce_blank_lines_with(&full_selector, config.count, &options)
853
870
  }
854
871
 
855
872
  PipelineStep::Sort(config) => {
856
- let full_path = resolve_step_path(base_path, config.path.as_deref());
873
+ let full_selector = resolve_step_selector(base_selector, config.selector.as_deref());
857
874
  let sort_fields = config.by.as_deref().map(crate::SortField::parse_list).unwrap_or_default();
858
875
 
859
- document.sort_items(&full_path, &sort_fields, config.case_sensitive)
876
+ document.sort_items(&full_selector, &sort_fields, config.case_sensitive)
860
877
  }
861
878
 
862
879
  PipelineStep::Directives(config) => {
@@ -868,7 +885,7 @@ fn execute_step(document: &mut Document, step: &PipelineStep, base_path: Option<
868
885
  let locations = document.directive_locations();
869
886
 
870
887
  if locations.len() > max {
871
- let positions: Vec<String> = locations.iter().map(|(line, col)| format!("{}:{}", line, col)).collect();
888
+ let positions: Vec<String> = locations.iter().map(|(line, column)| format!("{}:{}", line, column + 1)).collect();
872
889
 
873
890
  return Err(YerbaError::ParseError(format!(
874
891
  "found {} directive markers (---) at {}, expected at most {}",
@@ -891,7 +908,7 @@ fn execute_step(document: &mut Document, step: &PipelineStep, base_path: Option<
891
908
  PipelineStep::Schema(config) => {
892
909
  let schema_path = yerbafile.resolve_path(&config.file);
893
910
  let schema = crate::schema::load_schema(&schema_path)?;
894
- let selector = resolve_step_path(base_path, config.path.as_deref());
911
+ let selector = resolve_step_selector(base_selector, config.selector.as_deref());
895
912
  let errors = document.validate_schema(&schema, config.items, if selector.is_empty() { None } else { Some(&selector) });
896
913
 
897
914
  if errors.is_empty() {
@@ -902,8 +919,8 @@ fn execute_step(document: &mut Document, step: &PipelineStep, base_path: Option<
902
919
  }
903
920
 
904
921
  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)?;
922
+ let full_selector = resolve_step_selector(base_selector, config.selector.as_deref());
923
+ let duplicates = document.unique_with_options(&full_selector, &config.by, config.remove, config.allow_blank_duplicates)?;
907
924
 
908
925
  if !duplicates.is_empty() && !config.remove {
909
926
  return Err(YerbaError::DuplicateValues(duplicates));
@@ -915,17 +932,17 @@ fn execute_step(document: &mut Document, step: &PipelineStep, base_path: Option<
915
932
  PipelineStep::FinalNewline(config) => document.enforce_final_newline(config.count),
916
933
 
917
934
  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()) };
935
+ let full_selector = resolve_step_selector(base_selector, config.selector.as_deref());
936
+ let scope = if full_selector.is_empty() { None } else { Some(full_selector.as_str()) };
920
937
 
921
938
  document.decode_escapes(scope).map(|_| ())
922
939
  }
923
940
  }
924
941
  }
925
942
 
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("");
943
+ fn resolve_step_selector(base_selector: Option<&str>, step_selector: Option<&str>) -> String {
944
+ let base = base_selector.unwrap_or("");
945
+ let step = step_selector.unwrap_or("");
929
946
 
930
947
  match (base.is_empty(), step.is_empty()) {
931
948
  (true, true) => String::new(),
@@ -934,3 +951,34 @@ fn resolve_step_path(base_path: Option<&str>, step_path: Option<&str>) -> String
934
951
  (false, false) => format!("{}.{}", base, step),
935
952
  }
936
953
  }
954
+
955
+ // TODO(v1.0): remove together with the `path` aliases on the rule and step configs.
956
+ fn deprecated_path_keys(content: &str) -> Vec<String> {
957
+ let Ok(yerbafile) = yaml_serde::from_str::<yaml_serde::Value>(content) else {
958
+ return Vec::new();
959
+ };
960
+
961
+ let mut found = Vec::new();
962
+
963
+ let check_pipeline = |pipeline: Option<&yaml_serde::Value>, prefix: &str, found: &mut Vec<String>| {
964
+ for (index, step) in pipeline.and_then(|pipeline| pipeline.as_sequence()).into_iter().flatten().enumerate() {
965
+ for (name, config) in step.as_mapping().into_iter().flatten() {
966
+ if config.get("path").is_some() {
967
+ found.push(format!("{}pipeline[{}].{}", prefix, index, name.as_str().unwrap_or_default()));
968
+ }
969
+ }
970
+ }
971
+ };
972
+
973
+ check_pipeline(yerbafile.get("pipeline"), "", &mut found);
974
+
975
+ for (index, rule) in yerbafile.get("rules").and_then(|rules| rules.as_sequence()).into_iter().flatten().enumerate() {
976
+ if rule.get("path").is_some() {
977
+ found.push(format!("rules[{}]", index));
978
+ }
979
+
980
+ check_pipeline(rule.get("pipeline"), &format!("rules[{}].", index), &mut found);
981
+ }
982
+
983
+ found
984
+ }
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.1
4
+ version: 0.10.0
5
5
  platform: arm-linux-gnu
6
6
  authors:
7
7
  - Marco Roth
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-10-12 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.
@@ -39,6 +39,7 @@ files:
39
39
  - lib/yerba/node.rb
40
40
  - lib/yerba/query_result.rb
41
41
  - lib/yerba/scalar.rb
42
+ - lib/yerba/selector.rb
42
43
  - lib/yerba/sequence.rb
43
44
  - lib/yerba/version.rb
44
45
  - lib/yerba/yerbafile.rb
@@ -77,6 +78,7 @@ files:
77
78
  - rust/src/document/get.rs
78
79
  - rust/src/document/insert.rs
79
80
  - rust/src/document/mod.rs
81
+ - rust/src/document/nodes.rs
80
82
  - rust/src/document/schema.rs
81
83
  - rust/src/document/set.rs
82
84
  - rust/src/document/sort.rs