yerba 0.4.2 → 0.5.0

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.
@@ -9,6 +9,8 @@ use crate::{Document, QuoteStyle, YerbaError};
9
9
  pub struct Yerbafile {
10
10
  #[serde(default)]
11
11
  pub rules: Vec<Rule>,
12
+ #[serde(skip)]
13
+ pub directory: Option<PathBuf>,
12
14
  }
13
15
 
14
16
  #[derive(Debug, Clone, Deserialize)]
@@ -31,6 +33,8 @@ pub enum PipelineStep {
31
33
  BlankLines(BlankLinesConfig),
32
34
  Sort(SortConfig),
33
35
  Directives(DirectivesConfig),
36
+ Unique(UniqueConfig),
37
+ Schema(SchemaConfig),
34
38
  }
35
39
 
36
40
  #[derive(Debug, Clone, Deserialize)]
@@ -58,6 +62,31 @@ pub struct DirectivesConfig {
58
62
  pub remove: bool,
59
63
  }
60
64
 
65
+ #[derive(Debug, Clone, Deserialize)]
66
+ pub struct UniqueConfig {
67
+ #[serde(default)]
68
+ pub path: Option<String>,
69
+ #[serde(default = "default_dot")]
70
+ pub by: String,
71
+ #[serde(default)]
72
+ pub remove: bool,
73
+ #[serde(default)]
74
+ pub allow_blank_duplicates: bool,
75
+ }
76
+
77
+ fn default_dot() -> String {
78
+ ".".to_string()
79
+ }
80
+
81
+ #[derive(Debug, Clone, Deserialize)]
82
+ pub struct SchemaConfig {
83
+ pub file: String,
84
+ #[serde(default)]
85
+ pub path: Option<String>,
86
+ #[serde(default)]
87
+ pub items: bool,
88
+ }
89
+
61
90
  #[derive(Debug, Clone, Deserialize)]
62
91
  pub struct RenameConfig {
63
92
  pub from: String,
@@ -131,8 +160,18 @@ impl<'de> Deserialize<'de> for PipelineStep {
131
160
  return Ok(PipelineStep::Sort(config));
132
161
  }
133
162
 
163
+ if let Some(value) = mapping.get(serde_yaml::Value::String("unique".to_string())) {
164
+ let config: UniqueConfig = serde_yaml::from_value(value.clone()).map_err(serde::de::Error::custom)?;
165
+ return Ok(PipelineStep::Unique(config));
166
+ }
167
+
168
+ if let Some(value) = mapping.get(serde_yaml::Value::String("schema".to_string())) {
169
+ let config: SchemaConfig = serde_yaml::from_value(value.clone()).map_err(serde::de::Error::custom)?;
170
+ return Ok(PipelineStep::Schema(config));
171
+ }
172
+
134
173
  Err(serde::de::Error::custom(
135
- "unknown pipeline step: expected sort_keys, quote_style, set, insert, delete, rename, remove, blank_lines, sort, or directives",
174
+ "unknown pipeline step: expected sort_keys, quote_style, set, insert, delete, rename, remove, blank_lines, sort, directives, unique, or schema",
136
175
  ))
137
176
  }
138
177
  }
@@ -184,16 +223,24 @@ fn default_key_style() -> String {
184
223
  pub struct RuleResult {
185
224
  pub file: String,
186
225
  pub changed: bool,
187
- pub error: Option<String>,
226
+ pub error: Option<YerbaError>,
188
227
  }
189
228
 
190
229
  impl Yerbafile {
191
230
  pub fn load(path: impl AsRef<Path>) -> Result<Self, YerbaError> {
192
231
  let content = fs::read_to_string(path.as_ref())?;
193
- let yerbafile: Yerbafile = serde_yaml::from_str(&content).map_err(|error| YerbaError::ParseError(format!("{}", error)))?;
232
+ let mut yerbafile: Yerbafile = serde_yaml::from_str(&content).map_err(|error| YerbaError::ParseError(format!("{}", error)))?;
233
+ yerbafile.directory = path.as_ref().parent().map(|p| p.to_path_buf());
194
234
  Ok(yerbafile)
195
235
  }
196
236
 
237
+ pub fn resolve_path(&self, relative: &str) -> PathBuf {
238
+ match &self.directory {
239
+ Some(directory) => directory.join(relative),
240
+ None => PathBuf::from(relative),
241
+ }
242
+ }
243
+
197
244
  pub fn find() -> Option<PathBuf> {
198
245
  Self::find_from(std::env::current_dir().ok()?)
199
246
  }
@@ -255,7 +302,7 @@ impl Yerbafile {
255
302
  results.push(RuleResult {
256
303
  file: rule.files.clone(),
257
304
  changed: false,
258
- error: Some(format!("invalid glob: {}", error)),
305
+ error: Some(YerbaError::ParseError(format!("invalid glob: {}", error))),
259
306
  });
260
307
 
261
308
  continue;
@@ -280,7 +327,7 @@ impl Yerbafile {
280
327
  return Some(RuleResult {
281
328
  file: file.clone(),
282
329
  changed: false,
283
- error: Some(format!("{}", error)),
330
+ error: Some(error),
284
331
  });
285
332
  }
286
333
  };
@@ -289,7 +336,7 @@ impl Yerbafile {
289
336
  Some(RuleResult {
290
337
  file: file.clone(),
291
338
  changed: false,
292
- error: Some(format!("{}", error)),
339
+ error: Some(error),
293
340
  })
294
341
  } else {
295
342
  None
@@ -323,7 +370,7 @@ impl Yerbafile {
323
370
  return RuleResult {
324
371
  file: file.to_string(),
325
372
  changed: false,
326
- error: Some(format!("{}", error)),
373
+ error: Some(error),
327
374
  }
328
375
  }
329
376
  };
@@ -332,11 +379,11 @@ impl Yerbafile {
332
379
  let base_path = rule.path.as_deref();
333
380
 
334
381
  for step in &rule.pipeline {
335
- if let Err(error) = execute_step(&mut document, step, base_path) {
382
+ if let Err(error) = execute_step(&mut document, step, base_path, file, self) {
336
383
  return RuleResult {
337
384
  file: file.to_string(),
338
385
  changed: false,
339
- error: Some(format!("{}", error)),
386
+ error: Some(error),
340
387
  };
341
388
  }
342
389
  }
@@ -349,7 +396,7 @@ impl Yerbafile {
349
396
  return RuleResult {
350
397
  file: file.to_string(),
351
398
  changed,
352
- error: Some(format!("{}", error)),
399
+ error: Some(YerbaError::IoError(error)),
353
400
  };
354
401
  }
355
402
  }
@@ -396,7 +443,7 @@ impl Yerbafile {
396
443
  let base_path = rule.path.as_deref();
397
444
 
398
445
  for step in &rule.pipeline {
399
- execute_step(document, step, base_path)?;
446
+ execute_step(document, step, base_path, file_path, self)?;
400
447
  }
401
448
  }
402
449
 
@@ -404,7 +451,7 @@ impl Yerbafile {
404
451
  }
405
452
  }
406
453
 
407
- fn execute_step(document: &mut Document, step: &PipelineStep, base_path: Option<&str>) -> Result<(), YerbaError> {
454
+ fn execute_step(document: &mut Document, step: &PipelineStep, base_path: Option<&str>, _file: &str, yerbafile: &Yerbafile) -> Result<(), YerbaError> {
408
455
  match step {
409
456
  PipelineStep::QuoteStyle(config) => {
410
457
  let dot_path = config.path.as_deref();
@@ -461,15 +508,33 @@ fn execute_step(document: &mut Document, step: &PipelineStep, base_path: Option<
461
508
  PipelineStep::Delete(config) => {
462
509
  let full_path = resolve_step_path(base_path, Some(&config.path));
463
510
 
464
- if let Some(condition) = &config.condition {
465
- let parent_path = full_path.rsplit_once('.').map(|(parent, _)| parent).unwrap_or("");
511
+ if full_path.contains("[]") {
512
+ let concrete_selectors = document.resolve_selectors(&full_path);
466
513
 
467
- if !document.evaluate_condition(parent_path, condition) {
468
- return Ok(());
514
+ for selector in concrete_selectors.into_iter().rev() {
515
+ if let Some(condition) = &config.condition {
516
+ let parent_path = selector.rsplit_once('.').map(|(parent, _)| parent).unwrap_or("");
517
+
518
+ if !document.evaluate_condition(parent_path, condition) {
519
+ continue;
520
+ }
521
+ }
522
+
523
+ document.delete(&selector)?;
469
524
  }
470
- }
471
525
 
472
- document.delete(&full_path)
526
+ Ok(())
527
+ } else {
528
+ if let Some(condition) = &config.condition {
529
+ let parent_path = full_path.rsplit_once('.').map(|(parent, _)| parent).unwrap_or("");
530
+
531
+ if !document.evaluate_condition(parent_path, condition) {
532
+ return Ok(());
533
+ }
534
+ }
535
+
536
+ document.delete(&full_path)
537
+ }
473
538
  }
474
539
 
475
540
  PipelineStep::Rename(config) => {
@@ -526,6 +591,30 @@ fn execute_step(document: &mut Document, step: &PipelineStep, base_path: Option<
526
591
  Ok(())
527
592
  }
528
593
  }
594
+
595
+ PipelineStep::Schema(config) => {
596
+ let schema_path = yerbafile.resolve_path(&config.file);
597
+ let schema = crate::schema::load_schema(&schema_path)?;
598
+ let selector = resolve_step_path(base_path, config.path.as_deref());
599
+ let errors = document.validate_schema(&schema, config.items, if selector.is_empty() { None } else { Some(&selector) });
600
+
601
+ if errors.is_empty() {
602
+ Ok(())
603
+ } else {
604
+ Err(YerbaError::SchemaValidation(errors))
605
+ }
606
+ }
607
+
608
+ PipelineStep::Unique(config) => {
609
+ let full_path = resolve_step_path(base_path, config.path.as_deref());
610
+ let duplicates = document.unique_with_options(&full_path, &config.by, config.remove, config.allow_blank_duplicates)?;
611
+
612
+ if !duplicates.is_empty() && !config.remove {
613
+ return Err(YerbaError::DuplicateValues(duplicates));
614
+ }
615
+
616
+ Ok(())
617
+ }
529
618
  }
530
619
  }
531
620
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: yerba
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.2
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Marco Roth
@@ -31,6 +31,7 @@ files:
31
31
  - lib/yerba/formatting.rb
32
32
  - lib/yerba/location.rb
33
33
  - lib/yerba/map.rb
34
+ - lib/yerba/node.rb
34
35
  - lib/yerba/query_result.rb
35
36
  - lib/yerba/scalar.rb
36
37
  - lib/yerba/sequence.rb
@@ -49,6 +50,7 @@ files:
49
50
  - rust/src/commands/get.rs
50
51
  - rust/src/commands/init.rs
51
52
  - rust/src/commands/insert.rs
53
+ - rust/src/commands/location.rs
52
54
  - rust/src/commands/mate.rs
53
55
  - rust/src/commands/mod.rs
54
56
  - rust/src/commands/move_item.rs
@@ -56,10 +58,12 @@ files:
56
58
  - rust/src/commands/quote_style.rs
57
59
  - rust/src/commands/remove.rs
58
60
  - rust/src/commands/rename.rs
61
+ - rust/src/commands/schema.rs
59
62
  - rust/src/commands/selectors.rs
60
63
  - rust/src/commands/set.rs
61
64
  - rust/src/commands/sort.rs
62
65
  - rust/src/commands/sort_keys.rs
66
+ - rust/src/commands/unique.rs
63
67
  - rust/src/commands/version.rs
64
68
  - rust/src/didyoumean.rs
65
69
  - rust/src/document/condition.rs
@@ -67,15 +71,18 @@ files:
67
71
  - rust/src/document/get.rs
68
72
  - rust/src/document/insert.rs
69
73
  - rust/src/document/mod.rs
74
+ - rust/src/document/schema.rs
70
75
  - rust/src/document/set.rs
71
76
  - rust/src/document/sort.rs
72
77
  - rust/src/document/style.rs
78
+ - rust/src/document/unique.rs
73
79
  - rust/src/error.rs
74
80
  - rust/src/ffi.rs
75
81
  - rust/src/json.rs
76
82
  - rust/src/lib.rs
77
83
  - rust/src/main.rs
78
84
  - rust/src/quote_style.rs
85
+ - rust/src/schema.rs
79
86
  - rust/src/selector.rs
80
87
  - rust/src/syntax.rs
81
88
  - rust/src/yaml_writer.rs