yerba 0.2.0-arm-linux-gnu
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/LICENSE.txt +21 -0
- data/README.md +528 -0
- data/exe/yerba +6 -0
- data/ext/yerba/extconf.rb +111 -0
- data/ext/yerba/yerba.c +752 -0
- data/lib/yerba/3.2/yerba.so +0 -0
- data/lib/yerba/3.3/yerba.so +0 -0
- data/lib/yerba/3.4/yerba.so +0 -0
- data/lib/yerba/4.0/yerba.so +0 -0
- data/lib/yerba/collection.rb +31 -0
- data/lib/yerba/document.rb +59 -0
- data/lib/yerba/formatting.rb +18 -0
- data/lib/yerba/location.rb +5 -0
- data/lib/yerba/map.rb +166 -0
- data/lib/yerba/scalar.rb +85 -0
- data/lib/yerba/sequence.rb +182 -0
- data/lib/yerba/version.rb +5 -0
- data/lib/yerba.rb +131 -0
- data/rust/Cargo.lock +805 -0
- data/rust/Cargo.toml +36 -0
- data/rust/build.rs +11 -0
- data/rust/cbindgen.toml +27 -0
- data/rust/rustfmt.toml +2 -0
- data/rust/src/commands/apply.rs +5 -0
- data/rust/src/commands/blank_lines.rs +58 -0
- data/rust/src/commands/check.rs +5 -0
- data/rust/src/commands/delete.rs +35 -0
- data/rust/src/commands/get.rs +194 -0
- data/rust/src/commands/init.rs +89 -0
- data/rust/src/commands/insert.rs +106 -0
- data/rust/src/commands/mate.rs +55 -0
- data/rust/src/commands/mod.rs +349 -0
- data/rust/src/commands/move_item.rs +54 -0
- data/rust/src/commands/move_key.rs +87 -0
- data/rust/src/commands/quote_style.rs +62 -0
- data/rust/src/commands/remove.rs +35 -0
- data/rust/src/commands/rename.rs +37 -0
- data/rust/src/commands/set.rs +59 -0
- data/rust/src/commands/sort.rs +52 -0
- data/rust/src/commands/sort_keys.rs +62 -0
- data/rust/src/commands/version.rs +8 -0
- data/rust/src/document.rs +2237 -0
- data/rust/src/error.rs +45 -0
- data/rust/src/ffi.rs +991 -0
- data/rust/src/json.rs +128 -0
- data/rust/src/lib.rs +29 -0
- data/rust/src/main.rs +72 -0
- data/rust/src/quote_style.rs +42 -0
- data/rust/src/selector.rs +241 -0
- data/rust/src/syntax.rs +262 -0
- data/rust/src/yaml_writer.rs +89 -0
- data/rust/src/yerbafile.rs +475 -0
- data/sig/yerba.rbs +3 -0
- data/yerba.gemspec +52 -0
- metadata +108 -0
|
@@ -0,0 +1,475 @@
|
|
|
1
|
+
use rayon::prelude::*;
|
|
2
|
+
use serde::Deserialize;
|
|
3
|
+
use std::fs;
|
|
4
|
+
use std::path::{Path, PathBuf};
|
|
5
|
+
|
|
6
|
+
use crate::{Document, QuoteStyle, YerbaError};
|
|
7
|
+
|
|
8
|
+
#[derive(Debug, Deserialize)]
|
|
9
|
+
pub struct Yerbafile {
|
|
10
|
+
#[serde(default)]
|
|
11
|
+
pub rules: Vec<Rule>,
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
#[derive(Debug, Clone, Deserialize)]
|
|
15
|
+
pub struct Rule {
|
|
16
|
+
pub files: String,
|
|
17
|
+
#[serde(default)]
|
|
18
|
+
pub path: Option<String>,
|
|
19
|
+
pub pipeline: Vec<PipelineStep>,
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
#[derive(Debug, Clone)]
|
|
23
|
+
pub enum PipelineStep {
|
|
24
|
+
SortKeys(SortKeysConfig),
|
|
25
|
+
QuoteStyle(QuoteStyleConfig),
|
|
26
|
+
Set(SetConfig),
|
|
27
|
+
Insert(InsertConfig),
|
|
28
|
+
Delete(DeleteConfig),
|
|
29
|
+
Rename(RenameConfig),
|
|
30
|
+
Remove(RemoveConfig),
|
|
31
|
+
BlankLines(BlankLinesConfig),
|
|
32
|
+
Sort(SortConfig),
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
#[derive(Debug, Clone, Deserialize)]
|
|
36
|
+
pub struct SortConfig {
|
|
37
|
+
#[serde(default)]
|
|
38
|
+
pub path: Option<String>,
|
|
39
|
+
#[serde(default)]
|
|
40
|
+
pub by: Option<String>,
|
|
41
|
+
#[serde(default)]
|
|
42
|
+
pub case_sensitive: bool,
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
#[derive(Debug, Clone, Deserialize)]
|
|
46
|
+
pub struct BlankLinesConfig {
|
|
47
|
+
#[serde(default)]
|
|
48
|
+
pub path: Option<String>,
|
|
49
|
+
pub count: usize,
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
#[derive(Debug, Clone, Deserialize)]
|
|
53
|
+
pub struct RenameConfig {
|
|
54
|
+
pub from: String,
|
|
55
|
+
pub to: String,
|
|
56
|
+
#[serde(default)]
|
|
57
|
+
pub condition: Option<String>,
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
#[derive(Debug, Clone, Deserialize)]
|
|
61
|
+
pub struct RemoveConfig {
|
|
62
|
+
pub path: String,
|
|
63
|
+
pub value: String,
|
|
64
|
+
#[serde(default)]
|
|
65
|
+
pub condition: Option<String>,
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
impl<'de> Deserialize<'de> for PipelineStep {
|
|
69
|
+
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
|
70
|
+
where
|
|
71
|
+
D: serde::Deserializer<'de>,
|
|
72
|
+
{
|
|
73
|
+
let mapping = serde_yaml::Mapping::deserialize(deserializer)?;
|
|
74
|
+
|
|
75
|
+
if let Some(value) = mapping.get(serde_yaml::Value::String("sort_keys".to_string())) {
|
|
76
|
+
let config: SortKeysConfig = serde_yaml::from_value(value.clone()).map_err(serde::de::Error::custom)?;
|
|
77
|
+
return Ok(PipelineStep::SortKeys(config));
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if let Some(value) = mapping.get(serde_yaml::Value::String("quote_style".to_string())) {
|
|
81
|
+
let config: QuoteStyleConfig = serde_yaml::from_value(value.clone()).map_err(serde::de::Error::custom)?;
|
|
82
|
+
return Ok(PipelineStep::QuoteStyle(config));
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if let Some(value) = mapping.get(serde_yaml::Value::String("set".to_string())) {
|
|
86
|
+
let config: SetConfig = serde_yaml::from_value(value.clone()).map_err(serde::de::Error::custom)?;
|
|
87
|
+
return Ok(PipelineStep::Set(config));
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if let Some(value) = mapping.get(serde_yaml::Value::String("insert".to_string())) {
|
|
91
|
+
let config: InsertConfig = serde_yaml::from_value(value.clone()).map_err(serde::de::Error::custom)?;
|
|
92
|
+
return Ok(PipelineStep::Insert(config));
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if let Some(value) = mapping.get(serde_yaml::Value::String("delete".to_string())) {
|
|
96
|
+
let config: DeleteConfig = serde_yaml::from_value(value.clone()).map_err(serde::de::Error::custom)?;
|
|
97
|
+
return Ok(PipelineStep::Delete(config));
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if let Some(value) = mapping.get(serde_yaml::Value::String("rename".to_string())) {
|
|
101
|
+
let config: RenameConfig = serde_yaml::from_value(value.clone()).map_err(serde::de::Error::custom)?;
|
|
102
|
+
return Ok(PipelineStep::Rename(config));
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if let Some(value) = mapping.get(serde_yaml::Value::String("remove".to_string())) {
|
|
106
|
+
let config: RemoveConfig = serde_yaml::from_value(value.clone()).map_err(serde::de::Error::custom)?;
|
|
107
|
+
return Ok(PipelineStep::Remove(config));
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if let Some(value) = mapping.get(serde_yaml::Value::String("blank_lines".to_string())) {
|
|
111
|
+
let config: BlankLinesConfig = serde_yaml::from_value(value.clone()).map_err(serde::de::Error::custom)?;
|
|
112
|
+
return Ok(PipelineStep::BlankLines(config));
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if let Some(value) = mapping.get(serde_yaml::Value::String("sort".to_string())) {
|
|
116
|
+
let config: SortConfig = serde_yaml::from_value(value.clone()).map_err(serde::de::Error::custom)?;
|
|
117
|
+
return Ok(PipelineStep::Sort(config));
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
Err(serde::de::Error::custom(
|
|
121
|
+
"unknown pipeline step: expected sort_keys, quote_style, set, insert, delete, rename, remove, blank_lines, or sort",
|
|
122
|
+
))
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
#[derive(Debug, Clone, Deserialize)]
|
|
127
|
+
pub struct SortKeysConfig {
|
|
128
|
+
#[serde(default)]
|
|
129
|
+
pub path: Option<String>,
|
|
130
|
+
pub order: Vec<String>,
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
#[derive(Debug, Clone, Deserialize)]
|
|
134
|
+
pub struct QuoteStyleConfig {
|
|
135
|
+
#[serde(default = "default_key_style")]
|
|
136
|
+
pub key_style: String,
|
|
137
|
+
pub value_style: String,
|
|
138
|
+
#[serde(default)]
|
|
139
|
+
pub path: Option<String>,
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
#[derive(Debug, Clone, Deserialize)]
|
|
143
|
+
pub struct SetConfig {
|
|
144
|
+
pub path: String,
|
|
145
|
+
pub value: String,
|
|
146
|
+
#[serde(default)]
|
|
147
|
+
pub condition: Option<String>,
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
#[derive(Debug, Clone, Deserialize)]
|
|
151
|
+
pub struct InsertConfig {
|
|
152
|
+
pub path: String,
|
|
153
|
+
pub value: String,
|
|
154
|
+
#[serde(default)]
|
|
155
|
+
pub condition: Option<String>,
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
#[derive(Debug, Clone, Deserialize)]
|
|
159
|
+
pub struct DeleteConfig {
|
|
160
|
+
pub path: String,
|
|
161
|
+
#[serde(default)]
|
|
162
|
+
pub condition: Option<String>,
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
fn default_key_style() -> String {
|
|
166
|
+
"plain".to_string()
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
#[derive(Debug)]
|
|
170
|
+
pub struct RuleResult {
|
|
171
|
+
pub file: String,
|
|
172
|
+
pub changed: bool,
|
|
173
|
+
pub error: Option<String>,
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
impl Yerbafile {
|
|
177
|
+
pub fn load(path: impl AsRef<Path>) -> Result<Self, YerbaError> {
|
|
178
|
+
let content = fs::read_to_string(path.as_ref())?;
|
|
179
|
+
let yerbafile: Yerbafile =
|
|
180
|
+
serde_yaml::from_str(&content).map_err(|error| YerbaError::ParseError(format!("{}", error)))?;
|
|
181
|
+
Ok(yerbafile)
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
pub fn find() -> Option<PathBuf> {
|
|
185
|
+
let candidates = ["Yerbafile", "Yerbafile.yml", "Yerbafile.yaml", ".yerbafile"];
|
|
186
|
+
|
|
187
|
+
let mut directory = std::env::current_dir().ok()?;
|
|
188
|
+
|
|
189
|
+
loop {
|
|
190
|
+
for candidate in &candidates {
|
|
191
|
+
let path = directory.join(candidate);
|
|
192
|
+
|
|
193
|
+
if path.exists() {
|
|
194
|
+
return Some(path);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
if !directory.pop() {
|
|
199
|
+
return None;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
pub fn sort_order_for(&self, file_path: &str, dot_path: &str) -> Option<Vec<String>> {
|
|
205
|
+
for rule in &self.rules {
|
|
206
|
+
let sort_keys_config = rule.pipeline.iter().find_map(|step| match step {
|
|
207
|
+
PipelineStep::SortKeys(config) => Some(config),
|
|
208
|
+
_ => None,
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
if let Some(config) = sort_keys_config {
|
|
212
|
+
let full_path = resolve_step_path(rule.path.as_deref(), config.path.as_deref());
|
|
213
|
+
let normalized = full_path.trim_end_matches("[]").trim_end_matches('.');
|
|
214
|
+
|
|
215
|
+
if normalized != dot_path && !normalized.is_empty() && !dot_path.is_empty() {
|
|
216
|
+
continue;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
if let Ok(pattern) = glob::Pattern::new(&rule.files) {
|
|
220
|
+
if pattern.matches(file_path) || pattern.matches_path(Path::new(file_path)) {
|
|
221
|
+
return Some(config.order.clone());
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
None
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
pub fn apply(&self, write: bool) -> Vec<RuleResult> {
|
|
231
|
+
let mut results = Vec::new();
|
|
232
|
+
|
|
233
|
+
for rule in &self.rules {
|
|
234
|
+
let files = match glob::glob(&rule.files) {
|
|
235
|
+
Ok(paths) => paths.filter_map(|entry| entry.ok()).collect::<Vec<_>>(),
|
|
236
|
+
|
|
237
|
+
Err(error) => {
|
|
238
|
+
results.push(RuleResult {
|
|
239
|
+
file: rule.files.clone(),
|
|
240
|
+
changed: false,
|
|
241
|
+
error: Some(format!("invalid glob: {}", error)),
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
continue;
|
|
245
|
+
}
|
|
246
|
+
};
|
|
247
|
+
|
|
248
|
+
let file_strings: Vec<String> = files.iter().map(|path| path.to_string_lossy().to_string()).collect();
|
|
249
|
+
|
|
250
|
+
let mut has_validation_error = false;
|
|
251
|
+
|
|
252
|
+
for step in &rule.pipeline {
|
|
253
|
+
if let PipelineStep::SortKeys(config) = step {
|
|
254
|
+
let full_path = resolve_step_path(rule.path.as_deref(), config.path.as_deref());
|
|
255
|
+
let key_order: Vec<&str> = config.order.iter().map(|key| key.as_str()).collect();
|
|
256
|
+
|
|
257
|
+
let validation_results: Vec<RuleResult> = file_strings
|
|
258
|
+
.par_iter()
|
|
259
|
+
.filter_map(|file| {
|
|
260
|
+
let document = match Document::parse_file(file) {
|
|
261
|
+
Ok(document) => document,
|
|
262
|
+
Err(error) => {
|
|
263
|
+
return Some(RuleResult {
|
|
264
|
+
file: file.clone(),
|
|
265
|
+
changed: false,
|
|
266
|
+
error: Some(format!("{}", error)),
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
};
|
|
270
|
+
|
|
271
|
+
if let Err(error) = document.validate_sort_keys(&full_path, &key_order) {
|
|
272
|
+
Some(RuleResult {
|
|
273
|
+
file: file.clone(),
|
|
274
|
+
changed: false,
|
|
275
|
+
error: Some(format!("{}", error)),
|
|
276
|
+
})
|
|
277
|
+
} else {
|
|
278
|
+
None
|
|
279
|
+
}
|
|
280
|
+
})
|
|
281
|
+
.collect();
|
|
282
|
+
|
|
283
|
+
if !validation_results.is_empty() {
|
|
284
|
+
has_validation_error = true;
|
|
285
|
+
results.extend(validation_results);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
if has_validation_error {
|
|
291
|
+
continue;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
let file_results: Vec<RuleResult> = file_strings
|
|
295
|
+
.par_iter()
|
|
296
|
+
.map(|file| self.apply_pipeline_to_file(rule, file, write))
|
|
297
|
+
.collect();
|
|
298
|
+
|
|
299
|
+
results.extend(file_results);
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
results
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
fn apply_pipeline_to_file(&self, rule: &Rule, file: &str, write: bool) -> RuleResult {
|
|
306
|
+
let mut document = match Document::parse_file(file) {
|
|
307
|
+
Ok(document) => document,
|
|
308
|
+
Err(error) => {
|
|
309
|
+
return RuleResult {
|
|
310
|
+
file: file.to_string(),
|
|
311
|
+
changed: false,
|
|
312
|
+
error: Some(format!("{}", error)),
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
};
|
|
316
|
+
|
|
317
|
+
let original = document.to_string();
|
|
318
|
+
let base_path = rule.path.as_deref();
|
|
319
|
+
|
|
320
|
+
for step in &rule.pipeline {
|
|
321
|
+
if let Err(error) = execute_step(&mut document, step, base_path) {
|
|
322
|
+
return RuleResult {
|
|
323
|
+
file: file.to_string(),
|
|
324
|
+
changed: false,
|
|
325
|
+
error: Some(format!("{}", error)),
|
|
326
|
+
};
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
let new_content = document.to_string();
|
|
331
|
+
let changed = new_content != original;
|
|
332
|
+
|
|
333
|
+
if changed && write {
|
|
334
|
+
if let Err(error) = fs::write(file, &new_content) {
|
|
335
|
+
return RuleResult {
|
|
336
|
+
file: file.to_string(),
|
|
337
|
+
changed,
|
|
338
|
+
error: Some(format!("{}", error)),
|
|
339
|
+
};
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
RuleResult {
|
|
344
|
+
file: file.to_string(),
|
|
345
|
+
changed,
|
|
346
|
+
error: None,
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
fn execute_step(document: &mut Document, step: &PipelineStep, base_path: Option<&str>) -> Result<(), YerbaError> {
|
|
352
|
+
match step {
|
|
353
|
+
PipelineStep::QuoteStyle(config) => {
|
|
354
|
+
let dot_path = config.path.as_deref();
|
|
355
|
+
|
|
356
|
+
let key_style = config.key_style.parse::<QuoteStyle>().map_err(YerbaError::ParseError)?;
|
|
357
|
+
|
|
358
|
+
let value_style = config
|
|
359
|
+
.value_style
|
|
360
|
+
.parse::<QuoteStyle>()
|
|
361
|
+
.map_err(YerbaError::ParseError)?;
|
|
362
|
+
|
|
363
|
+
document.enforce_key_style(&key_style, dot_path)?;
|
|
364
|
+
document.enforce_quotes_at(&value_style, dot_path)?;
|
|
365
|
+
|
|
366
|
+
Ok(())
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
PipelineStep::SortKeys(config) => {
|
|
370
|
+
let full_path = resolve_step_path(base_path, config.path.as_deref());
|
|
371
|
+
let key_order: Vec<&str> = config.order.iter().map(|key| key.as_str()).collect();
|
|
372
|
+
|
|
373
|
+
document.sort_keys(&full_path, &key_order)
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
PipelineStep::Set(config) => {
|
|
377
|
+
let full_path = resolve_step_path(base_path, Some(&config.path));
|
|
378
|
+
|
|
379
|
+
if let Some(condition) = &config.condition {
|
|
380
|
+
let parent_path = full_path.rsplit_once('.').map(|(parent, _)| parent).unwrap_or("");
|
|
381
|
+
|
|
382
|
+
if !document.evaluate_condition(parent_path, condition) {
|
|
383
|
+
return Ok(());
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
document.set(&full_path, &config.value)
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
PipelineStep::Insert(config) => {
|
|
391
|
+
let full_path = resolve_step_path(base_path, Some(&config.path));
|
|
392
|
+
|
|
393
|
+
if let Some(condition) = &config.condition {
|
|
394
|
+
let parent_path = full_path.rsplit_once('.').map(|(parent, _)| parent).unwrap_or("");
|
|
395
|
+
|
|
396
|
+
if !document.evaluate_condition(parent_path, condition) {
|
|
397
|
+
return Ok(());
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
document.insert_into(&full_path, &config.value, crate::InsertPosition::Last)
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
PipelineStep::Delete(config) => {
|
|
405
|
+
let full_path = resolve_step_path(base_path, Some(&config.path));
|
|
406
|
+
|
|
407
|
+
if let Some(condition) = &config.condition {
|
|
408
|
+
let parent_path = full_path.rsplit_once('.').map(|(parent, _)| parent).unwrap_or("");
|
|
409
|
+
|
|
410
|
+
if !document.evaluate_condition(parent_path, condition) {
|
|
411
|
+
return Ok(());
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
document.delete(&full_path)
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
PipelineStep::Rename(config) => {
|
|
419
|
+
let full_path = resolve_step_path(base_path, Some(&config.from));
|
|
420
|
+
|
|
421
|
+
if let Some(condition) = &config.condition {
|
|
422
|
+
let parent_path = full_path.rsplit_once('.').map(|(parent, _)| parent).unwrap_or("");
|
|
423
|
+
|
|
424
|
+
if !document.evaluate_condition(parent_path, condition) {
|
|
425
|
+
return Ok(());
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
document.rename(&full_path, &config.to)
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
PipelineStep::Remove(config) => {
|
|
433
|
+
let full_path = resolve_step_path(base_path, Some(&config.path));
|
|
434
|
+
|
|
435
|
+
if let Some(condition) = &config.condition {
|
|
436
|
+
let parent_path = full_path.rsplit_once('.').map(|(parent, _)| parent).unwrap_or("");
|
|
437
|
+
|
|
438
|
+
if !document.evaluate_condition(parent_path, condition) {
|
|
439
|
+
return Ok(());
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
document.remove(&full_path, &config.value)
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
PipelineStep::BlankLines(config) => {
|
|
447
|
+
let full_path = resolve_step_path(base_path, config.path.as_deref());
|
|
448
|
+
|
|
449
|
+
document.enforce_blank_lines(&full_path, config.count)
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
PipelineStep::Sort(config) => {
|
|
453
|
+
let full_path = resolve_step_path(base_path, config.path.as_deref());
|
|
454
|
+
let sort_fields = config
|
|
455
|
+
.by
|
|
456
|
+
.as_deref()
|
|
457
|
+
.map(crate::SortField::parse_list)
|
|
458
|
+
.unwrap_or_default();
|
|
459
|
+
|
|
460
|
+
document.sort_items(&full_path, &sort_fields, config.case_sensitive)
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
fn resolve_step_path(base_path: Option<&str>, step_path: Option<&str>) -> String {
|
|
466
|
+
let base = base_path.unwrap_or("");
|
|
467
|
+
let step = step_path.unwrap_or("");
|
|
468
|
+
|
|
469
|
+
match (base.is_empty(), step.is_empty()) {
|
|
470
|
+
(true, true) => String::new(),
|
|
471
|
+
(true, false) => step.to_string(),
|
|
472
|
+
(false, true) => base.to_string(),
|
|
473
|
+
(false, false) => format!("{}.{}", base, step),
|
|
474
|
+
}
|
|
475
|
+
}
|
data/sig/yerba.rbs
ADDED
data/yerba.gemspec
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
begin
|
|
4
|
+
require_relative "lib/yerba/version"
|
|
5
|
+
rescue LoadError
|
|
6
|
+
puts "WARNING: Could not load Yerba::VERSION"
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
Gem::Specification.new do |spec|
|
|
10
|
+
spec.name = "yerba"
|
|
11
|
+
spec.version = defined?(Yerba::VERSION) ? Yerba::VERSION : "0.0.0"
|
|
12
|
+
spec.authors = ["Marco Roth"]
|
|
13
|
+
spec.email = ["marco.roth@intergga.ch"]
|
|
14
|
+
|
|
15
|
+
spec.summary = "YAML Editing and Refactoring with Better Accuracy"
|
|
16
|
+
spec.description = "A CLI tool for editing YAML while preserving structure, comments, and format."
|
|
17
|
+
spec.homepage = "https://github.com/marcoroth/yerba"
|
|
18
|
+
spec.license = "MIT"
|
|
19
|
+
|
|
20
|
+
spec.required_ruby_version = ">= 3.2.0"
|
|
21
|
+
spec.require_paths = ["lib"]
|
|
22
|
+
|
|
23
|
+
spec.files = Dir[
|
|
24
|
+
"yerba.gemspec",
|
|
25
|
+
"LICENSE.txt",
|
|
26
|
+
"README.md",
|
|
27
|
+
"lib/**/*.rb",
|
|
28
|
+
"sig/**/*.rbs",
|
|
29
|
+
"exe/yerba",
|
|
30
|
+
"ext/yerba/extconf.rb",
|
|
31
|
+
"ext/yerba/yerba.c",
|
|
32
|
+
"ext/yerba/include/**/*.h",
|
|
33
|
+
"rust/build.rs",
|
|
34
|
+
"rust/cbindgen.toml",
|
|
35
|
+
"rust/Cargo.toml",
|
|
36
|
+
"rust/Cargo.lock",
|
|
37
|
+
"rust/src/**/*.rs",
|
|
38
|
+
"rust/rustfmt.toml"
|
|
39
|
+
]
|
|
40
|
+
|
|
41
|
+
spec.bindir = "exe"
|
|
42
|
+
spec.executables = ["yerba"]
|
|
43
|
+
spec.extensions = ["ext/yerba/extconf.rb"]
|
|
44
|
+
|
|
45
|
+
spec.metadata["allowed_push_host"] = "https://rubygems.org"
|
|
46
|
+
spec.metadata["rubygems_mfa_required"] = "true"
|
|
47
|
+
|
|
48
|
+
spec.metadata["homepage_uri"] = "https://github.com/marcoroth/yerba"
|
|
49
|
+
spec.metadata["changelog_uri"] = "https://github.com/marcoroth/yerba/releases"
|
|
50
|
+
spec.metadata["source_code_uri"] = "https://github.com/marcoroth/yerba"
|
|
51
|
+
spec.metadata["bug_tracker_uri"] = "https://github.com/marcoroth/yerba/issues"
|
|
52
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: yerba
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.2.0
|
|
5
|
+
platform: arm-linux-gnu
|
|
6
|
+
authors:
|
|
7
|
+
- Marco Roth
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: exe
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2026-05-03 00:00:00.000000000 Z
|
|
12
|
+
dependencies: []
|
|
13
|
+
description: A CLI tool for editing YAML while preserving structure, comments, and
|
|
14
|
+
format.
|
|
15
|
+
email:
|
|
16
|
+
- marco.roth@intergga.ch
|
|
17
|
+
executables:
|
|
18
|
+
- yerba
|
|
19
|
+
extensions: []
|
|
20
|
+
extra_rdoc_files: []
|
|
21
|
+
files:
|
|
22
|
+
- LICENSE.txt
|
|
23
|
+
- README.md
|
|
24
|
+
- exe/yerba
|
|
25
|
+
- ext/yerba/extconf.rb
|
|
26
|
+
- ext/yerba/yerba.c
|
|
27
|
+
- lib/yerba.rb
|
|
28
|
+
- lib/yerba/3.2/yerba.so
|
|
29
|
+
- lib/yerba/3.3/yerba.so
|
|
30
|
+
- lib/yerba/3.4/yerba.so
|
|
31
|
+
- lib/yerba/4.0/yerba.so
|
|
32
|
+
- lib/yerba/collection.rb
|
|
33
|
+
- lib/yerba/document.rb
|
|
34
|
+
- lib/yerba/formatting.rb
|
|
35
|
+
- lib/yerba/location.rb
|
|
36
|
+
- lib/yerba/map.rb
|
|
37
|
+
- lib/yerba/scalar.rb
|
|
38
|
+
- lib/yerba/sequence.rb
|
|
39
|
+
- lib/yerba/version.rb
|
|
40
|
+
- rust/Cargo.lock
|
|
41
|
+
- rust/Cargo.toml
|
|
42
|
+
- rust/build.rs
|
|
43
|
+
- rust/cbindgen.toml
|
|
44
|
+
- rust/rustfmt.toml
|
|
45
|
+
- rust/src/commands/apply.rs
|
|
46
|
+
- rust/src/commands/blank_lines.rs
|
|
47
|
+
- rust/src/commands/check.rs
|
|
48
|
+
- rust/src/commands/delete.rs
|
|
49
|
+
- rust/src/commands/get.rs
|
|
50
|
+
- rust/src/commands/init.rs
|
|
51
|
+
- rust/src/commands/insert.rs
|
|
52
|
+
- rust/src/commands/mate.rs
|
|
53
|
+
- rust/src/commands/mod.rs
|
|
54
|
+
- rust/src/commands/move_item.rs
|
|
55
|
+
- rust/src/commands/move_key.rs
|
|
56
|
+
- rust/src/commands/quote_style.rs
|
|
57
|
+
- rust/src/commands/remove.rs
|
|
58
|
+
- rust/src/commands/rename.rs
|
|
59
|
+
- rust/src/commands/set.rs
|
|
60
|
+
- rust/src/commands/sort.rs
|
|
61
|
+
- rust/src/commands/sort_keys.rs
|
|
62
|
+
- rust/src/commands/version.rs
|
|
63
|
+
- rust/src/document.rs
|
|
64
|
+
- rust/src/error.rs
|
|
65
|
+
- rust/src/ffi.rs
|
|
66
|
+
- rust/src/json.rs
|
|
67
|
+
- rust/src/lib.rs
|
|
68
|
+
- rust/src/main.rs
|
|
69
|
+
- rust/src/quote_style.rs
|
|
70
|
+
- rust/src/selector.rs
|
|
71
|
+
- rust/src/syntax.rs
|
|
72
|
+
- rust/src/yaml_writer.rs
|
|
73
|
+
- rust/src/yerbafile.rs
|
|
74
|
+
- sig/yerba.rbs
|
|
75
|
+
- yerba.gemspec
|
|
76
|
+
homepage: https://github.com/marcoroth/yerba
|
|
77
|
+
licenses:
|
|
78
|
+
- MIT
|
|
79
|
+
metadata:
|
|
80
|
+
allowed_push_host: https://rubygems.org
|
|
81
|
+
rubygems_mfa_required: 'true'
|
|
82
|
+
homepage_uri: https://github.com/marcoroth/yerba
|
|
83
|
+
changelog_uri: https://github.com/marcoroth/yerba/releases
|
|
84
|
+
source_code_uri: https://github.com/marcoroth/yerba
|
|
85
|
+
bug_tracker_uri: https://github.com/marcoroth/yerba/issues
|
|
86
|
+
post_install_message:
|
|
87
|
+
rdoc_options: []
|
|
88
|
+
require_paths:
|
|
89
|
+
- lib
|
|
90
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
91
|
+
requirements:
|
|
92
|
+
- - ">="
|
|
93
|
+
- !ruby/object:Gem::Version
|
|
94
|
+
version: '3.2'
|
|
95
|
+
- - "<"
|
|
96
|
+
- !ruby/object:Gem::Version
|
|
97
|
+
version: 4.1.dev
|
|
98
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
99
|
+
requirements:
|
|
100
|
+
- - ">="
|
|
101
|
+
- !ruby/object:Gem::Version
|
|
102
|
+
version: 3.3.22
|
|
103
|
+
requirements: []
|
|
104
|
+
rubygems_version: 3.5.23
|
|
105
|
+
signing_key:
|
|
106
|
+
specification_version: 4
|
|
107
|
+
summary: YAML Editing and Refactoring with Better Accuracy
|
|
108
|
+
test_files: []
|