yerba 0.5.0-x86_64-linux-gnu → 0.6.0-x86_64-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 +4 -4
- data/README.md +2 -26
- data/exe/x86_64-linux-gnu/yerba +0 -0
- data/ext/yerba/include/yerba.h +12 -0
- data/ext/yerba/yerba.c +74 -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/formatting.rb +61 -0
- data/lib/yerba/map.rb +50 -6
- data/lib/yerba/sequence.rb +20 -0
- data/lib/yerba/version.rb +1 -1
- data/rust/Cargo.lock +21 -21
- data/rust/Cargo.toml +2 -2
- data/rust/src/commands/get.rs +1 -1
- data/rust/src/commands/selectors.rs +4 -4
- data/rust/src/commands/sort.rs +1 -1
- data/rust/src/document/condition.rs +2 -2
- data/rust/src/document/delete.rs +65 -8
- data/rust/src/document/get.rs +55 -30
- data/rust/src/document/insert.rs +204 -9
- data/rust/src/document/mod.rs +76 -26
- data/rust/src/document/sort.rs +2 -2
- data/rust/src/document/style.rs +609 -2
- data/rust/src/ffi.rs +46 -0
- data/rust/src/json.rs +16 -16
- data/rust/src/lib.rs +3 -2
- data/rust/src/selector.rs +16 -0
- data/rust/src/yaml_writer.rs +98 -0
- data/rust/src/yerbafile.rs +164 -72
- metadata +2 -2
data/rust/src/json.rs
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
use crate::selector::{Selector, SelectorSegment};
|
|
2
2
|
|
|
3
|
-
pub fn yaml_to_json(value: &
|
|
3
|
+
pub fn yaml_to_json(value: &yaml_serde::Value) -> serde_json::Value {
|
|
4
4
|
match value {
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
yaml_serde::Value::Null => serde_json::Value::Null,
|
|
6
|
+
yaml_serde::Value::Bool(boolean) => serde_json::Value::Bool(*boolean),
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
yaml_serde::Value::Number(number) => {
|
|
9
9
|
if let Some(integer) = number.as_i64() {
|
|
10
10
|
serde_json::Value::Number(integer.into())
|
|
11
11
|
} else if let Some(float) = number.as_f64() {
|
|
@@ -15,16 +15,16 @@ pub fn yaml_to_json(value: &serde_yaml::Value) -> serde_json::Value {
|
|
|
15
15
|
}
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
yaml_serde::Value::String(string) => serde_json::Value::String(string.clone()),
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
yaml_serde::Value::Sequence(sequence) => serde_json::Value::Array(sequence.iter().map(yaml_to_json).collect()),
|
|
21
21
|
|
|
22
|
-
|
|
22
|
+
yaml_serde::Value::Mapping(mapping) => {
|
|
23
23
|
let mut map = serde_json::Map::new();
|
|
24
24
|
|
|
25
25
|
for (key, yaml_value) in mapping {
|
|
26
26
|
let json_key = match key {
|
|
27
|
-
|
|
27
|
+
yaml_serde::Value::String(string) => string.clone(),
|
|
28
28
|
_ => format!("{:?}", key),
|
|
29
29
|
};
|
|
30
30
|
|
|
@@ -34,19 +34,19 @@ pub fn yaml_to_json(value: &serde_yaml::Value) -> serde_json::Value {
|
|
|
34
34
|
serde_json::Value::Object(map)
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
|
|
37
|
+
yaml_serde::Value::Tagged(tagged) => yaml_to_json(&tagged.value),
|
|
38
38
|
}
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
-
pub fn resolve_select_field(value: &
|
|
41
|
+
pub fn resolve_select_field(value: &yaml_serde::Value, field: &str) -> serde_json::Value {
|
|
42
42
|
let parsed = Selector::parse(field);
|
|
43
43
|
let segments = parsed.segments();
|
|
44
44
|
|
|
45
45
|
if segments.len() == 1 {
|
|
46
46
|
if let SelectorSegment::Key(key) = &segments[0] {
|
|
47
|
-
if let
|
|
47
|
+
if let yaml_serde::Value::Mapping(map) = value {
|
|
48
48
|
for (map_key, yaml_value) in map {
|
|
49
|
-
if let
|
|
49
|
+
if let yaml_serde::Value::String(key_string) = map_key {
|
|
50
50
|
if key_string == key {
|
|
51
51
|
return yaml_to_json(yaml_value);
|
|
52
52
|
}
|
|
@@ -66,13 +66,13 @@ pub fn resolve_select_field(value: &serde_yaml::Value, field: &str) -> serde_jso
|
|
|
66
66
|
for current in ¤t_values {
|
|
67
67
|
match segment {
|
|
68
68
|
SelectorSegment::AllItems => {
|
|
69
|
-
if let
|
|
69
|
+
if let yaml_serde::Value::Sequence(sequence) = current {
|
|
70
70
|
next_values.extend(sequence.iter().cloned());
|
|
71
71
|
}
|
|
72
72
|
}
|
|
73
73
|
|
|
74
74
|
SelectorSegment::Index(index) => {
|
|
75
|
-
if let
|
|
75
|
+
if let yaml_serde::Value::Sequence(sequence) = current {
|
|
76
76
|
if let Some(item) = sequence.get(*index) {
|
|
77
77
|
next_values.push(item.clone());
|
|
78
78
|
}
|
|
@@ -80,9 +80,9 @@ pub fn resolve_select_field(value: &serde_yaml::Value, field: &str) -> serde_jso
|
|
|
80
80
|
}
|
|
81
81
|
|
|
82
82
|
SelectorSegment::Key(key) => {
|
|
83
|
-
if let
|
|
83
|
+
if let yaml_serde::Value::Mapping(map) = current {
|
|
84
84
|
for (map_key, yaml_value) in map {
|
|
85
|
-
if let
|
|
85
|
+
if let yaml_serde::Value::String(key_string) = map_key {
|
|
86
86
|
if key_string == key {
|
|
87
87
|
next_values.push(yaml_value.clone());
|
|
88
88
|
}
|
data/rust/src/lib.rs
CHANGED
|
@@ -10,6 +10,7 @@ mod syntax;
|
|
|
10
10
|
mod yaml_writer;
|
|
11
11
|
pub mod yerbafile;
|
|
12
12
|
|
|
13
|
+
pub use document::style::StyleEnforcement;
|
|
13
14
|
pub use document::{collect_selectors, Document, DuplicateInfo, InsertPosition, LocatedNode, Location, NodeInfo, NodeType, SortField};
|
|
14
15
|
pub use error::YerbaError;
|
|
15
16
|
pub use quote_style::{KeyStyle, QuoteStyle};
|
|
@@ -88,10 +89,10 @@ pub fn glob_find(pattern: &str, selector: &str, condition: Option<&str>, select:
|
|
|
88
89
|
}
|
|
89
90
|
}
|
|
90
91
|
None => {
|
|
91
|
-
if let
|
|
92
|
+
if let yaml_serde::Value::Mapping(map) = value {
|
|
92
93
|
for (key, yaml_value) in map {
|
|
93
94
|
let json_key = match key {
|
|
94
|
-
|
|
95
|
+
yaml_serde::Value::String(string) => string.clone(),
|
|
95
96
|
_ => format!("{:?}", key),
|
|
96
97
|
};
|
|
97
98
|
|
data/rust/src/selector.rs
CHANGED
|
@@ -171,6 +171,22 @@ impl Selector {
|
|
|
171
171
|
|
|
172
172
|
result
|
|
173
173
|
}
|
|
174
|
+
|
|
175
|
+
pub fn parent_path(&self) -> String {
|
|
176
|
+
let segments = self.segments();
|
|
177
|
+
|
|
178
|
+
if segments.len() <= 1 {
|
|
179
|
+
return String::new();
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
let parent_segments = &segments[..segments.len() - 1];
|
|
183
|
+
let parent = match self {
|
|
184
|
+
Selector::Relative(_) => Selector::Relative(parent_segments.to_vec()),
|
|
185
|
+
Selector::Absolute(_) => Selector::Absolute(parent_segments.to_vec()),
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
parent.to_selector_string()
|
|
189
|
+
}
|
|
174
190
|
}
|
|
175
191
|
|
|
176
192
|
fn parse_segments(input: &str) -> Vec<SelectorSegment> {
|
data/rust/src/yaml_writer.rs
CHANGED
|
@@ -37,6 +37,23 @@ pub fn json_to_yaml_text(value: &Value, quote_style: &QuoteStyle, indent: usize)
|
|
|
37
37
|
.join("\n")
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
+
Value::Array(array) => {
|
|
41
|
+
let prefix = " ".repeat(indent);
|
|
42
|
+
|
|
43
|
+
array
|
|
44
|
+
.iter()
|
|
45
|
+
.map(|item| match item {
|
|
46
|
+
Value::Object(_) => {
|
|
47
|
+
let inner = json_to_yaml_text(item, quote_style, indent + 2);
|
|
48
|
+
format!("{}- {}", prefix, inner.trim_start())
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
_ => format!("{}- {}", prefix, format_yaml_scalar(item, quote_style)),
|
|
52
|
+
})
|
|
53
|
+
.collect::<Vec<_>>()
|
|
54
|
+
.join("\n")
|
|
55
|
+
}
|
|
56
|
+
|
|
40
57
|
_ => {
|
|
41
58
|
let prefix = " ".repeat(indent);
|
|
42
59
|
|
|
@@ -45,6 +62,87 @@ pub fn json_to_yaml_text(value: &Value, quote_style: &QuoteStyle, indent: usize)
|
|
|
45
62
|
}
|
|
46
63
|
}
|
|
47
64
|
|
|
65
|
+
pub fn yaml_value_to_flow_text(value: &yaml_serde::Value) -> String {
|
|
66
|
+
match value {
|
|
67
|
+
yaml_serde::Value::Null => "null".to_string(),
|
|
68
|
+
yaml_serde::Value::Bool(boolean) => boolean.to_string(),
|
|
69
|
+
yaml_serde::Value::Number(number) => number.to_string(),
|
|
70
|
+
|
|
71
|
+
yaml_serde::Value::String(string) => {
|
|
72
|
+
if crate::syntax::is_yaml_non_string(string) {
|
|
73
|
+
format!("\"{}\"", string.replace('"', "\\\""))
|
|
74
|
+
} else {
|
|
75
|
+
string.clone()
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
yaml_serde::Value::Sequence(sequence) => {
|
|
80
|
+
let items: Vec<String> = sequence.iter().map(yaml_value_to_flow_text).collect();
|
|
81
|
+
|
|
82
|
+
format!("[{}]", items.join(", "))
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
yaml_serde::Value::Mapping(mapping) => {
|
|
86
|
+
let pairs: Vec<String> = mapping
|
|
87
|
+
.iter()
|
|
88
|
+
.map(|(key, value)| {
|
|
89
|
+
let key_string = match key {
|
|
90
|
+
yaml_serde::Value::String(string) => string.clone(),
|
|
91
|
+
_ => format!("{:?}", key),
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
format!("{}: {}", key_string, yaml_value_to_flow_text(value))
|
|
95
|
+
})
|
|
96
|
+
.collect();
|
|
97
|
+
|
|
98
|
+
format!("{{{}}}", pairs.join(", "))
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
yaml_serde::Value::Tagged(tagged) => yaml_value_to_flow_text(&tagged.value),
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
pub fn yaml_value_to_block_text(value: &yaml_serde::Value, indent: usize) -> String {
|
|
106
|
+
let prefix = " ".repeat(indent);
|
|
107
|
+
|
|
108
|
+
match value {
|
|
109
|
+
yaml_serde::Value::Sequence(sequence) => sequence
|
|
110
|
+
.iter()
|
|
111
|
+
.map(|item| match item {
|
|
112
|
+
yaml_serde::Value::Mapping(_) => {
|
|
113
|
+
let inner = yaml_value_to_block_text(item, indent + 2);
|
|
114
|
+
format!("{}- {}", prefix, inner.trim_start())
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
_ => format!("{}- {}", prefix, yaml_value_to_flow_text(item)),
|
|
118
|
+
})
|
|
119
|
+
.collect::<Vec<_>>()
|
|
120
|
+
.join("\n"),
|
|
121
|
+
|
|
122
|
+
yaml_serde::Value::Mapping(mapping) => mapping
|
|
123
|
+
.iter()
|
|
124
|
+
.map(|(key, value)| {
|
|
125
|
+
let key_string = match key {
|
|
126
|
+
yaml_serde::Value::String(string) => string.clone(),
|
|
127
|
+
_ => format!("{:?}", key),
|
|
128
|
+
};
|
|
129
|
+
match value {
|
|
130
|
+
yaml_serde::Value::Sequence(_) | yaml_serde::Value::Mapping(_) => {
|
|
131
|
+
let inner = yaml_value_to_block_text(value, indent + 2);
|
|
132
|
+
|
|
133
|
+
format!("{}{}:\n{}", prefix, key_string, inner)
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
_ => format!("{}{}: {}", prefix, key_string, yaml_value_to_flow_text(value)),
|
|
137
|
+
}
|
|
138
|
+
})
|
|
139
|
+
.collect::<Vec<_>>()
|
|
140
|
+
.join("\n"),
|
|
141
|
+
|
|
142
|
+
_ => format!("{}{}", prefix, yaml_value_to_flow_text(value)),
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
48
146
|
fn format_yaml_scalar(value: &Value, quote_style: &QuoteStyle) -> String {
|
|
49
147
|
match value {
|
|
50
148
|
Value::Null => "null".to_string(),
|
data/rust/src/yerbafile.rs
CHANGED
|
@@ -7,6 +7,10 @@ use crate::{Document, QuoteStyle, YerbaError};
|
|
|
7
7
|
|
|
8
8
|
#[derive(Debug, Deserialize)]
|
|
9
9
|
pub struct Yerbafile {
|
|
10
|
+
#[serde(default)]
|
|
11
|
+
pub files: Option<String>,
|
|
12
|
+
#[serde(default)]
|
|
13
|
+
pub pipeline: Vec<PipelineStep>,
|
|
10
14
|
#[serde(default)]
|
|
11
15
|
pub rules: Vec<Rule>,
|
|
12
16
|
#[serde(skip)]
|
|
@@ -25,6 +29,8 @@ pub struct Rule {
|
|
|
25
29
|
pub enum PipelineStep {
|
|
26
30
|
SortKeys(SortKeysConfig),
|
|
27
31
|
QuoteStyle(QuoteStyleConfig),
|
|
32
|
+
CollectionStyle(CollectionStyleConfig),
|
|
33
|
+
SequenceIndent(SequenceIndentConfig),
|
|
28
34
|
Set(SetConfig),
|
|
29
35
|
Insert(InsertConfig),
|
|
30
36
|
Delete(DeleteConfig),
|
|
@@ -108,70 +114,80 @@ impl<'de> Deserialize<'de> for PipelineStep {
|
|
|
108
114
|
where
|
|
109
115
|
D: serde::Deserializer<'de>,
|
|
110
116
|
{
|
|
111
|
-
let mapping =
|
|
117
|
+
let mapping = yaml_serde::Mapping::deserialize(deserializer)?;
|
|
112
118
|
|
|
113
|
-
if let Some(value) = mapping.get(
|
|
114
|
-
let config: SortKeysConfig =
|
|
119
|
+
if let Some(value) = mapping.get(yaml_serde::Value::String("sort_keys".to_string())) {
|
|
120
|
+
let config: SortKeysConfig = yaml_serde::from_value(value.clone()).map_err(serde::de::Error::custom)?;
|
|
115
121
|
return Ok(PipelineStep::SortKeys(config));
|
|
116
122
|
}
|
|
117
123
|
|
|
118
|
-
if let Some(value) = mapping.get(
|
|
119
|
-
let config: QuoteStyleConfig =
|
|
124
|
+
if let Some(value) = mapping.get(yaml_serde::Value::String("quote_style".to_string())) {
|
|
125
|
+
let config: QuoteStyleConfig = yaml_serde::from_value(value.clone()).map_err(serde::de::Error::custom)?;
|
|
120
126
|
return Ok(PipelineStep::QuoteStyle(config));
|
|
121
127
|
}
|
|
122
128
|
|
|
123
|
-
if let Some(value) = mapping.get(
|
|
124
|
-
let config:
|
|
129
|
+
if let Some(value) = mapping.get(yaml_serde::Value::String("collection_style".to_string())) {
|
|
130
|
+
let config: CollectionStyleConfig = yaml_serde::from_value(value.clone()).map_err(serde::de::Error::custom)?;
|
|
131
|
+
return Ok(PipelineStep::CollectionStyle(config));
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
if let Some(value) = mapping.get(yaml_serde::Value::String("sequence_indent".to_string())) {
|
|
135
|
+
let config: SequenceIndentConfig = yaml_serde::from_value(value.clone()).map_err(serde::de::Error::custom)?;
|
|
136
|
+
return Ok(PipelineStep::SequenceIndent(config));
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if let Some(value) = mapping.get(yaml_serde::Value::String("set".to_string())) {
|
|
140
|
+
let config: SetConfig = yaml_serde::from_value(value.clone()).map_err(serde::de::Error::custom)?;
|
|
125
141
|
return Ok(PipelineStep::Set(config));
|
|
126
142
|
}
|
|
127
143
|
|
|
128
|
-
if let Some(value) = mapping.get(
|
|
129
|
-
let config: InsertConfig =
|
|
144
|
+
if let Some(value) = mapping.get(yaml_serde::Value::String("insert".to_string())) {
|
|
145
|
+
let config: InsertConfig = yaml_serde::from_value(value.clone()).map_err(serde::de::Error::custom)?;
|
|
130
146
|
return Ok(PipelineStep::Insert(config));
|
|
131
147
|
}
|
|
132
148
|
|
|
133
|
-
if let Some(value) = mapping.get(
|
|
134
|
-
let config: DeleteConfig =
|
|
149
|
+
if let Some(value) = mapping.get(yaml_serde::Value::String("delete".to_string())) {
|
|
150
|
+
let config: DeleteConfig = yaml_serde::from_value(value.clone()).map_err(serde::de::Error::custom)?;
|
|
135
151
|
return Ok(PipelineStep::Delete(config));
|
|
136
152
|
}
|
|
137
153
|
|
|
138
|
-
if let Some(value) = mapping.get(
|
|
139
|
-
let config: RenameConfig =
|
|
154
|
+
if let Some(value) = mapping.get(yaml_serde::Value::String("rename".to_string())) {
|
|
155
|
+
let config: RenameConfig = yaml_serde::from_value(value.clone()).map_err(serde::de::Error::custom)?;
|
|
140
156
|
return Ok(PipelineStep::Rename(config));
|
|
141
157
|
}
|
|
142
158
|
|
|
143
|
-
if let Some(value) = mapping.get(
|
|
144
|
-
let config: RemoveConfig =
|
|
159
|
+
if let Some(value) = mapping.get(yaml_serde::Value::String("remove".to_string())) {
|
|
160
|
+
let config: RemoveConfig = yaml_serde::from_value(value.clone()).map_err(serde::de::Error::custom)?;
|
|
145
161
|
return Ok(PipelineStep::Remove(config));
|
|
146
162
|
}
|
|
147
163
|
|
|
148
|
-
if let Some(value) = mapping.get(
|
|
149
|
-
let config: BlankLinesConfig =
|
|
164
|
+
if let Some(value) = mapping.get(yaml_serde::Value::String("blank_lines".to_string())) {
|
|
165
|
+
let config: BlankLinesConfig = yaml_serde::from_value(value.clone()).map_err(serde::de::Error::custom)?;
|
|
150
166
|
return Ok(PipelineStep::BlankLines(config));
|
|
151
167
|
}
|
|
152
168
|
|
|
153
|
-
if let Some(value) = mapping.get(
|
|
154
|
-
let config: DirectivesConfig =
|
|
169
|
+
if let Some(value) = mapping.get(yaml_serde::Value::String("directives".to_string())) {
|
|
170
|
+
let config: DirectivesConfig = yaml_serde::from_value(value.clone()).map_err(serde::de::Error::custom)?;
|
|
155
171
|
return Ok(PipelineStep::Directives(config));
|
|
156
172
|
}
|
|
157
173
|
|
|
158
|
-
if let Some(value) = mapping.get(
|
|
159
|
-
let config: SortConfig =
|
|
174
|
+
if let Some(value) = mapping.get(yaml_serde::Value::String("sort".to_string())) {
|
|
175
|
+
let config: SortConfig = yaml_serde::from_value(value.clone()).map_err(serde::de::Error::custom)?;
|
|
160
176
|
return Ok(PipelineStep::Sort(config));
|
|
161
177
|
}
|
|
162
178
|
|
|
163
|
-
if let Some(value) = mapping.get(
|
|
164
|
-
let config: UniqueConfig =
|
|
179
|
+
if let Some(value) = mapping.get(yaml_serde::Value::String("unique".to_string())) {
|
|
180
|
+
let config: UniqueConfig = yaml_serde::from_value(value.clone()).map_err(serde::de::Error::custom)?;
|
|
165
181
|
return Ok(PipelineStep::Unique(config));
|
|
166
182
|
}
|
|
167
183
|
|
|
168
|
-
if let Some(value) = mapping.get(
|
|
169
|
-
let config: SchemaConfig =
|
|
184
|
+
if let Some(value) = mapping.get(yaml_serde::Value::String("schema".to_string())) {
|
|
185
|
+
let config: SchemaConfig = yaml_serde::from_value(value.clone()).map_err(serde::de::Error::custom)?;
|
|
170
186
|
return Ok(PipelineStep::Schema(config));
|
|
171
187
|
}
|
|
172
188
|
|
|
173
189
|
Err(serde::de::Error::custom(
|
|
174
|
-
"unknown pipeline step: expected sort_keys, quote_style, set, insert, delete, rename, remove, blank_lines, sort, directives, unique, or schema",
|
|
190
|
+
"unknown pipeline step: expected sort_keys, quote_style, collection_style, sequence_indent, set, insert, delete, rename, remove, blank_lines, sort, directives, unique, or schema",
|
|
175
191
|
))
|
|
176
192
|
}
|
|
177
193
|
}
|
|
@@ -192,6 +208,20 @@ pub struct QuoteStyleConfig {
|
|
|
192
208
|
pub path: Option<String>,
|
|
193
209
|
}
|
|
194
210
|
|
|
211
|
+
#[derive(Debug, Clone, Deserialize)]
|
|
212
|
+
pub struct CollectionStyleConfig {
|
|
213
|
+
pub style: String,
|
|
214
|
+
#[serde(default)]
|
|
215
|
+
pub path: Option<String>,
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
#[derive(Debug, Clone, Deserialize)]
|
|
219
|
+
pub struct SequenceIndentConfig {
|
|
220
|
+
pub style: String,
|
|
221
|
+
#[serde(default)]
|
|
222
|
+
pub path: Option<String>,
|
|
223
|
+
}
|
|
224
|
+
|
|
195
225
|
#[derive(Debug, Clone, Deserialize)]
|
|
196
226
|
pub struct SetConfig {
|
|
197
227
|
pub path: String,
|
|
@@ -229,7 +259,7 @@ pub struct RuleResult {
|
|
|
229
259
|
impl Yerbafile {
|
|
230
260
|
pub fn load(path: impl AsRef<Path>) -> Result<Self, YerbaError> {
|
|
231
261
|
let content = fs::read_to_string(path.as_ref())?;
|
|
232
|
-
let mut yerbafile: Yerbafile =
|
|
262
|
+
let mut yerbafile: Yerbafile = yaml_serde::from_str(&content).map_err(|error| YerbaError::ParseError(format!("{}", error)))?;
|
|
233
263
|
yerbafile.directory = path.as_ref().parent().map(|p| p.to_path_buf());
|
|
234
264
|
Ok(yerbafile)
|
|
235
265
|
}
|
|
@@ -293,6 +323,7 @@ impl Yerbafile {
|
|
|
293
323
|
|
|
294
324
|
pub fn apply(&self, write: bool) -> Vec<RuleResult> {
|
|
295
325
|
let mut results = Vec::new();
|
|
326
|
+
let mut globally_processed: std::collections::HashSet<String> = std::collections::HashSet::new();
|
|
296
327
|
|
|
297
328
|
for rule in &self.rules {
|
|
298
329
|
let files = match glob::glob(&rule.files) {
|
|
@@ -311,59 +342,25 @@ impl Yerbafile {
|
|
|
311
342
|
|
|
312
343
|
let file_strings: Vec<String> = files.iter().map(|path| path.to_string_lossy().to_string()).collect();
|
|
313
344
|
|
|
314
|
-
let
|
|
345
|
+
let file_results: Vec<RuleResult> = file_strings
|
|
346
|
+
.par_iter()
|
|
347
|
+
.map(|file| {
|
|
348
|
+
let run_global = !globally_processed.contains(file.as_str()) && self.should_run_global_pipeline(file);
|
|
349
|
+
self.apply_pipeline_to_file(rule, file, write, run_global)
|
|
350
|
+
})
|
|
351
|
+
.collect();
|
|
315
352
|
|
|
316
|
-
for
|
|
317
|
-
|
|
318
|
-
let full_path = resolve_step_path(rule.path.as_deref(), config.path.as_deref());
|
|
319
|
-
let key_order: Vec<&str> = config.order.iter().map(|key| key.as_str()).collect();
|
|
320
|
-
|
|
321
|
-
let validation_results: Vec<RuleResult> = file_strings
|
|
322
|
-
.par_iter()
|
|
323
|
-
.filter_map(|file| {
|
|
324
|
-
let document = match Document::parse_file(file) {
|
|
325
|
-
Ok(document) => document,
|
|
326
|
-
Err(error) => {
|
|
327
|
-
return Some(RuleResult {
|
|
328
|
-
file: file.clone(),
|
|
329
|
-
changed: false,
|
|
330
|
-
error: Some(error),
|
|
331
|
-
});
|
|
332
|
-
}
|
|
333
|
-
};
|
|
334
|
-
|
|
335
|
-
if let Err(error) = document.validate_sort_keys(&full_path, &key_order) {
|
|
336
|
-
Some(RuleResult {
|
|
337
|
-
file: file.clone(),
|
|
338
|
-
changed: false,
|
|
339
|
-
error: Some(error),
|
|
340
|
-
})
|
|
341
|
-
} else {
|
|
342
|
-
None
|
|
343
|
-
}
|
|
344
|
-
})
|
|
345
|
-
.collect();
|
|
346
|
-
|
|
347
|
-
if !validation_results.is_empty() {
|
|
348
|
-
has_validation_error = true;
|
|
349
|
-
results.extend(validation_results);
|
|
350
|
-
}
|
|
351
|
-
}
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
if has_validation_error {
|
|
355
|
-
continue;
|
|
353
|
+
for result in &file_results {
|
|
354
|
+
globally_processed.insert(result.file.clone());
|
|
356
355
|
}
|
|
357
356
|
|
|
358
|
-
let file_results: Vec<RuleResult> = file_strings.par_iter().map(|file| self.apply_pipeline_to_file(rule, file, write)).collect();
|
|
359
|
-
|
|
360
357
|
results.extend(file_results);
|
|
361
358
|
}
|
|
362
359
|
|
|
363
360
|
results
|
|
364
361
|
}
|
|
365
362
|
|
|
366
|
-
fn apply_pipeline_to_file(&self, rule: &Rule, file: &str, write: bool) -> RuleResult {
|
|
363
|
+
fn apply_pipeline_to_file(&self, rule: &Rule, file: &str, write: bool, run_global: bool) -> RuleResult {
|
|
367
364
|
let mut document = match Document::parse_file(file) {
|
|
368
365
|
Ok(document) => document,
|
|
369
366
|
Err(error) => {
|
|
@@ -376,6 +373,17 @@ impl Yerbafile {
|
|
|
376
373
|
};
|
|
377
374
|
|
|
378
375
|
let original = document.to_string();
|
|
376
|
+
|
|
377
|
+
if run_global {
|
|
378
|
+
if let Err(error) = execute_pipeline(&mut document, &self.pipeline, None, file, self) {
|
|
379
|
+
return RuleResult {
|
|
380
|
+
file: file.to_string(),
|
|
381
|
+
changed: false,
|
|
382
|
+
error: Some(error),
|
|
383
|
+
};
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
|
|
379
387
|
let base_path = rule.path.as_deref();
|
|
380
388
|
|
|
381
389
|
for step in &rule.pipeline {
|
|
@@ -410,6 +418,7 @@ impl Yerbafile {
|
|
|
410
418
|
|
|
411
419
|
pub fn apply_file(&self, file: &str, write: bool) -> Vec<RuleResult> {
|
|
412
420
|
let mut results = Vec::new();
|
|
421
|
+
let mut ran_global = false;
|
|
413
422
|
|
|
414
423
|
for rule in &self.rules {
|
|
415
424
|
if let Ok(pattern) = glob::Pattern::new(&rule.files) {
|
|
@@ -420,15 +429,34 @@ impl Yerbafile {
|
|
|
420
429
|
continue;
|
|
421
430
|
}
|
|
422
431
|
|
|
423
|
-
|
|
432
|
+
let run_global = !ran_global && self.should_run_global_pipeline(file);
|
|
433
|
+
results.push(self.apply_pipeline_to_file(rule, file, write, run_global));
|
|
434
|
+
ran_global = true;
|
|
424
435
|
}
|
|
425
436
|
|
|
426
437
|
results
|
|
427
438
|
}
|
|
428
439
|
|
|
440
|
+
fn should_run_global_pipeline(&self, file_path: &str) -> bool {
|
|
441
|
+
if self.pipeline.is_empty() {
|
|
442
|
+
return false;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
match &self.files {
|
|
446
|
+
Some(glob_pattern) => glob::Pattern::new(glob_pattern)
|
|
447
|
+
.map(|pattern| pattern.matches(file_path) || pattern.matches_path(Path::new(file_path)))
|
|
448
|
+
.unwrap_or(false),
|
|
449
|
+
None => true,
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
|
|
429
453
|
pub fn apply_to_document(&self, document: &mut Document, file_path: &str) -> Result<bool, YerbaError> {
|
|
430
454
|
let original = document.to_string();
|
|
431
455
|
|
|
456
|
+
if self.should_run_global_pipeline(file_path) {
|
|
457
|
+
execute_pipeline(document, &self.pipeline, None, file_path, self)?;
|
|
458
|
+
}
|
|
459
|
+
|
|
432
460
|
for rule in &self.rules {
|
|
433
461
|
if !file_path.is_empty() {
|
|
434
462
|
if let Ok(pattern) = glob::Pattern::new(&rule.files) {
|
|
@@ -451,6 +479,55 @@ impl Yerbafile {
|
|
|
451
479
|
}
|
|
452
480
|
}
|
|
453
481
|
|
|
482
|
+
fn execute_pipeline(document: &mut Document, steps: &[PipelineStep], base_path: Option<&str>, file: &str, yerbafile: &Yerbafile) -> Result<(), YerbaError> {
|
|
483
|
+
use crate::document::style::StyleEnforcement;
|
|
484
|
+
|
|
485
|
+
let mut enforcement = StyleEnforcement {
|
|
486
|
+
collection_style: None,
|
|
487
|
+
sequence_indent: None,
|
|
488
|
+
key_style: None,
|
|
489
|
+
value_style: None,
|
|
490
|
+
};
|
|
491
|
+
|
|
492
|
+
let mut has_enforcement = false;
|
|
493
|
+
let mut remaining_steps: Vec<&PipelineStep> = Vec::new();
|
|
494
|
+
|
|
495
|
+
for step in steps {
|
|
496
|
+
match step {
|
|
497
|
+
PipelineStep::CollectionStyle(config) if config.path.is_none() && base_path.is_none() => {
|
|
498
|
+
enforcement.collection_style = Some(config.style.clone());
|
|
499
|
+
has_enforcement = true;
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
PipelineStep::SequenceIndent(config) if config.path.is_none() && base_path.is_none() => {
|
|
503
|
+
enforcement.sequence_indent = Some(config.style.clone());
|
|
504
|
+
has_enforcement = true;
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
PipelineStep::QuoteStyle(config) if config.path.is_none() && base_path.is_none() => {
|
|
508
|
+
let key_style = config.key_style.parse::<crate::KeyStyle>().ok();
|
|
509
|
+
let value_style = config.value_style.parse::<QuoteStyle>().ok();
|
|
510
|
+
|
|
511
|
+
enforcement.key_style = key_style;
|
|
512
|
+
enforcement.value_style = value_style;
|
|
513
|
+
has_enforcement = true;
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
_ => remaining_steps.push(step),
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
if has_enforcement {
|
|
521
|
+
document.enforce_styles(&enforcement)?;
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
for step in remaining_steps {
|
|
525
|
+
execute_step(document, step, base_path, file, yerbafile)?;
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
Ok(())
|
|
529
|
+
}
|
|
530
|
+
|
|
454
531
|
fn execute_step(document: &mut Document, step: &PipelineStep, base_path: Option<&str>, _file: &str, yerbafile: &Yerbafile) -> Result<(), YerbaError> {
|
|
455
532
|
match step {
|
|
456
533
|
PipelineStep::QuoteStyle(config) => {
|
|
@@ -470,10 +547,25 @@ fn execute_step(document: &mut Document, step: &PipelineStep, base_path: Option<
|
|
|
470
547
|
Ok(())
|
|
471
548
|
}
|
|
472
549
|
|
|
550
|
+
PipelineStep::CollectionStyle(config) => {
|
|
551
|
+
let dot_path = resolve_step_path(base_path, config.path.as_deref());
|
|
552
|
+
let path = if dot_path.is_empty() { None } else { Some(dot_path.as_str()) };
|
|
553
|
+
|
|
554
|
+
document.enforce_collection_style(&config.style, path)
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
PipelineStep::SequenceIndent(config) => {
|
|
558
|
+
let dot_path = resolve_step_path(base_path, config.path.as_deref());
|
|
559
|
+
let path = if dot_path.is_empty() { None } else { Some(dot_path.as_str()) };
|
|
560
|
+
|
|
561
|
+
document.enforce_sequence_indent(&config.style, path)
|
|
562
|
+
}
|
|
563
|
+
|
|
473
564
|
PipelineStep::SortKeys(config) => {
|
|
474
565
|
let full_path = resolve_step_path(base_path, config.path.as_deref());
|
|
475
566
|
let key_order: Vec<&str> = config.order.iter().map(|key| key.as_str()).collect();
|
|
476
567
|
|
|
568
|
+
document.validate_sort_keys(&full_path, &key_order)?;
|
|
477
569
|
document.sort_keys(&full_path, &key_order)
|
|
478
570
|
}
|
|
479
571
|
|
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.
|
|
4
|
+
version: 0.6.0
|
|
5
5
|
platform: x86_64-linux-gnu
|
|
6
6
|
authors:
|
|
7
7
|
- Marco Roth
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-
|
|
11
|
+
date: 2026-06-17 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.
|