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.
- checksums.yaml +4 -4
- data/README.md +53 -17
- data/exe/aarch64-linux-gnu/yerba +0 -0
- data/ext/yerba/include/yerba.h +58 -35
- data/ext/yerba/yerba.c +281 -187
- 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 +4 -4
- data/lib/yerba/document.rb +134 -0
- data/lib/yerba/formatting.rb +1 -0
- data/lib/yerba/location.rb +8 -1
- data/lib/yerba/map.rb +16 -6
- data/lib/yerba/node.rb +41 -0
- data/lib/yerba/query_result.rb +33 -10
- data/lib/yerba/scalar.rb +29 -0
- data/lib/yerba/selector.rb +3 -1
- data/lib/yerba/sequence.rb +38 -33
- data/lib/yerba/version.rb +1 -1
- data/rust/Cargo.lock +1 -1
- data/rust/Cargo.toml +1 -1
- data/rust/src/commands/get.rs +12 -12
- data/rust/src/commands/init.rs +8 -8
- data/rust/src/commands/insert.rs +4 -18
- data/rust/src/commands/location.rs +2 -0
- data/rust/src/commands/mod.rs +34 -6
- data/rust/src/commands/move_item.rs +1 -1
- data/rust/src/commands/move_key.rs +8 -8
- data/rust/src/commands/rename.rs +1 -1
- data/rust/src/commands/selectors.rs +1 -1
- data/rust/src/commands/set.rs +14 -6
- data/rust/src/commands/sort.rs +5 -1
- data/rust/src/document/condition.rs +25 -25
- data/rust/src/document/delete.rs +57 -43
- data/rust/src/document/get.rs +91 -82
- data/rust/src/document/insert.rs +77 -77
- data/rust/src/document/mod.rs +202 -76
- data/rust/src/document/nodes.rs +162 -0
- data/rust/src/document/schema.rs +8 -8
- data/rust/src/document/set.rs +49 -26
- data/rust/src/document/sort.rs +105 -60
- data/rust/src/document/style.rs +50 -49
- data/rust/src/document/unique.rs +5 -5
- data/rust/src/error.rs +8 -4
- data/rust/src/ffi.rs +234 -107
- data/rust/src/json.rs +62 -2
- data/rust/src/lib.rs +4 -2
- data/rust/src/main.rs +2 -1
- data/rust/src/selector.rs +48 -7
- data/rust/src/syntax.rs +449 -56
- data/rust/src/validation.rs +5 -3
- data/rust/src/yerbafile.rs +168 -94
- metadata +3 -2
data/rust/src/document/schema.rs
CHANGED
|
@@ -2,10 +2,10 @@ use super::*;
|
|
|
2
2
|
|
|
3
3
|
impl Document {
|
|
4
4
|
pub fn validate_schema(&self, schema: &serde_json::Value, items: bool, selector: Option<&str>) -> Vec<crate::schema::ValidationError> {
|
|
5
|
-
let
|
|
6
|
-
let has_wildcard = crate::selector::Selector::parse(
|
|
5
|
+
let selector = selector.unwrap_or("");
|
|
6
|
+
let has_wildcard = crate::selector::Selector::parse(selector).has_wildcard();
|
|
7
7
|
|
|
8
|
-
let value = match self.get_value(
|
|
8
|
+
let value = match self.get_value(selector) {
|
|
9
9
|
Some(value) => crate::json::yaml_to_json(&value),
|
|
10
10
|
None => return vec![],
|
|
11
11
|
};
|
|
@@ -62,12 +62,12 @@ fn json_pointer_to_selector(pointer: &str) -> String {
|
|
|
62
62
|
segment.to_string()
|
|
63
63
|
}
|
|
64
64
|
})
|
|
65
|
-
.fold(String::new(), |mut
|
|
66
|
-
if !
|
|
67
|
-
|
|
65
|
+
.fold(String::new(), |mut selector, segment| {
|
|
66
|
+
if !selector.is_empty() && !segment.starts_with('[') {
|
|
67
|
+
selector.push('.');
|
|
68
68
|
}
|
|
69
69
|
|
|
70
|
-
|
|
71
|
-
|
|
70
|
+
selector.push_str(&segment);
|
|
71
|
+
selector
|
|
72
72
|
})
|
|
73
73
|
}
|
data/rust/src/document/set.rs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
use super::*;
|
|
2
2
|
|
|
3
3
|
fn is_plain_writable(value: &str) -> bool {
|
|
4
|
-
crate::syntax::is_plain_safe(value) && !crate::syntax::is_yaml_non_string(value)
|
|
4
|
+
crate::syntax::is_plain_safe(value) && !crate::syntax::is_yaml_non_string(value) && !crate::syntax::resolves_to_non_string_in_psych(value)
|
|
5
5
|
}
|
|
6
6
|
|
|
7
7
|
fn scalar_replacement_text(value: &str, kind: SyntaxKind) -> String {
|
|
@@ -112,23 +112,23 @@ fn replacement_text(span: &ValueSpan, value: &str) -> String {
|
|
|
112
112
|
}
|
|
113
113
|
|
|
114
114
|
impl Document {
|
|
115
|
-
pub fn set(&mut self,
|
|
116
|
-
self.set_with(
|
|
115
|
+
pub fn set(&mut self, selector: &str, value: &str) -> Result<(), YerbaError> {
|
|
116
|
+
self.set_with(selector, value, false)
|
|
117
117
|
}
|
|
118
118
|
|
|
119
|
-
pub fn set_all(&mut self,
|
|
120
|
-
self.set_all_with(
|
|
119
|
+
pub fn set_all(&mut self, selector: &str, value: &str) -> Result<(), YerbaError> {
|
|
120
|
+
self.set_all_with(selector, value, false)
|
|
121
121
|
}
|
|
122
122
|
|
|
123
|
-
pub fn set_all_plain(&mut self,
|
|
124
|
-
self.set_all_with(
|
|
123
|
+
pub fn set_all_plain(&mut self, selector: &str, value: &str) -> Result<(), YerbaError> {
|
|
124
|
+
self.set_all_with(selector, value, true)
|
|
125
125
|
}
|
|
126
126
|
|
|
127
|
-
fn set_all_with(&mut self,
|
|
128
|
-
let nodes = self.navigate_all_compact(
|
|
127
|
+
fn set_all_with(&mut self, selector: &str, value: &str, plain: bool) -> Result<(), YerbaError> {
|
|
128
|
+
let nodes = self.navigate_all_compact(selector);
|
|
129
129
|
|
|
130
130
|
if nodes.is_empty() {
|
|
131
|
-
return Err(YerbaError::SelectorNotFound(
|
|
131
|
+
return Err(YerbaError::SelectorNotFound(selector.to_string()));
|
|
132
132
|
}
|
|
133
133
|
|
|
134
134
|
let source = self.source_text();
|
|
@@ -137,13 +137,21 @@ impl Document {
|
|
|
137
137
|
self.apply_edits(edits)
|
|
138
138
|
}
|
|
139
139
|
|
|
140
|
-
pub fn set_where(
|
|
140
|
+
pub fn set_where(
|
|
141
|
+
&mut self,
|
|
142
|
+
container_selector: &str,
|
|
143
|
+
relative_selector: &str,
|
|
144
|
+
value: &str,
|
|
145
|
+
condition: &str,
|
|
146
|
+
all: bool,
|
|
147
|
+
plain: bool,
|
|
148
|
+
) -> Result<usize, YerbaError> {
|
|
141
149
|
validate_item_condition(condition)?;
|
|
142
150
|
|
|
143
|
-
let items = self.navigate_all_compact(
|
|
151
|
+
let items = self.navigate_all_compact(container_selector);
|
|
144
152
|
|
|
145
153
|
if items.is_empty() {
|
|
146
|
-
return Err(YerbaError::SelectorNotFound(
|
|
154
|
+
return Err(YerbaError::SelectorNotFound(container_selector.to_string()));
|
|
147
155
|
}
|
|
148
156
|
|
|
149
157
|
let source = self.source_text();
|
|
@@ -154,7 +162,7 @@ impl Document {
|
|
|
154
162
|
continue;
|
|
155
163
|
}
|
|
156
164
|
|
|
157
|
-
targets.extend(navigate_from_node(item,
|
|
165
|
+
targets.extend(navigate_from_node(item, relative_selector));
|
|
158
166
|
}
|
|
159
167
|
|
|
160
168
|
if targets.is_empty() {
|
|
@@ -162,10 +170,10 @@ impl Document {
|
|
|
162
170
|
}
|
|
163
171
|
|
|
164
172
|
if !all && targets.len() > 1 {
|
|
165
|
-
let selector = if
|
|
166
|
-
|
|
173
|
+
let selector = if container_selector.is_empty() {
|
|
174
|
+
relative_selector.to_string()
|
|
167
175
|
} else {
|
|
168
|
-
format!("{}.{}",
|
|
176
|
+
format!("{}.{}", container_selector, relative_selector)
|
|
169
177
|
};
|
|
170
178
|
|
|
171
179
|
return Err(YerbaError::AmbiguousSelector(selector, targets.len()));
|
|
@@ -179,9 +187,9 @@ impl Document {
|
|
|
179
187
|
Ok(count)
|
|
180
188
|
}
|
|
181
189
|
|
|
182
|
-
pub fn set_scalar_style(&mut self,
|
|
183
|
-
let current_node = self.navigate(
|
|
184
|
-
let scalar_token = find_scalar_token(¤t_node).ok_or_else(|| YerbaError::SelectorNotFound(
|
|
190
|
+
pub fn set_scalar_style(&mut self, selector: &str, style: &QuoteStyle) -> Result<(), YerbaError> {
|
|
191
|
+
let current_node = self.navigate(selector)?;
|
|
192
|
+
let scalar_token = find_scalar_token(¤t_node).ok_or_else(|| YerbaError::SelectorNotFound(selector.to_string()))?;
|
|
185
193
|
|
|
186
194
|
let current_kind = scalar_token.kind();
|
|
187
195
|
let target_kind = style.to_syntax_kind();
|
|
@@ -200,12 +208,15 @@ impl Document {
|
|
|
200
208
|
self.replace_token(&scalar_token, &new_text)
|
|
201
209
|
}
|
|
202
210
|
|
|
203
|
-
pub fn set_plain(&mut self,
|
|
204
|
-
self.set_with(
|
|
211
|
+
pub fn set_plain(&mut self, selector: &str, value: &str) -> Result<(), YerbaError> {
|
|
212
|
+
self.set_with(selector, value, true)
|
|
205
213
|
}
|
|
206
214
|
|
|
207
|
-
fn set_with(&mut self,
|
|
208
|
-
let current_node = self.navigate(
|
|
215
|
+
fn set_with(&mut self, selector: &str, value: &str, plain: bool) -> Result<(), YerbaError> {
|
|
216
|
+
let current_node = match self.navigate(selector) {
|
|
217
|
+
Ok(node) => node,
|
|
218
|
+
Err(error) => return self.set_valueless_entry(selector, value, plain).unwrap_or(Err(error)),
|
|
219
|
+
};
|
|
209
220
|
|
|
210
221
|
if let Some(block_scalar) = current_node.descendants().find(|node| node.kind() == SyntaxKind::BLOCK_SCALAR) {
|
|
211
222
|
if plain {
|
|
@@ -219,13 +230,13 @@ impl Document {
|
|
|
219
230
|
}
|
|
220
231
|
|
|
221
232
|
if holds_collection(¤t_node) {
|
|
222
|
-
let span = value_span(¤t_node).ok_or_else(|| YerbaError::SelectorNotFound(
|
|
233
|
+
let span = value_span(¤t_node).ok_or_else(|| YerbaError::SelectorNotFound(selector.to_string()))?;
|
|
223
234
|
let replacement = if plain { value.to_string() } else { crate::syntax::quote_scalar(value) };
|
|
224
235
|
|
|
225
236
|
return self.apply_edit(span.range, &replacement_text(&span, &replacement));
|
|
226
237
|
}
|
|
227
238
|
|
|
228
|
-
let scalar_token = find_scalar_token(¤t_node).ok_or_else(|| YerbaError::SelectorNotFound(
|
|
239
|
+
let scalar_token = find_scalar_token(¤t_node).ok_or_else(|| YerbaError::SelectorNotFound(selector.to_string()))?;
|
|
229
240
|
|
|
230
241
|
if plain {
|
|
231
242
|
return self.replace_token(&scalar_token, value);
|
|
@@ -236,6 +247,18 @@ impl Document {
|
|
|
236
247
|
self.replace_token(&scalar_token, &new_text)
|
|
237
248
|
}
|
|
238
249
|
|
|
250
|
+
fn set_valueless_entry(&mut self, selector: &str, value: &str, plain: bool) -> Option<Result<(), YerbaError>> {
|
|
251
|
+
let entry = self.find_entry(selector)?;
|
|
252
|
+
let colon = entry.children_with_tokens().find(|element| element.kind() == SyntaxKind::COLON)?;
|
|
253
|
+
let text = if plain {
|
|
254
|
+
value.to_string()
|
|
255
|
+
} else {
|
|
256
|
+
scalar_replacement_text(value, SyntaxKind::PLAIN_SCALAR)
|
|
257
|
+
};
|
|
258
|
+
|
|
259
|
+
Some(self.apply_edit(TextRange::empty(colon.text_range().end()), &format!(" {}", text)))
|
|
260
|
+
}
|
|
261
|
+
|
|
239
262
|
fn block_scalar_replacement(source: &str, block_scalar: &SyntaxNode, value: &str) -> String {
|
|
240
263
|
if value.is_empty() {
|
|
241
264
|
return "\"\"".to_string();
|
data/rust/src/document/sort.rs
CHANGED
|
@@ -1,38 +1,38 @@
|
|
|
1
1
|
use super::*;
|
|
2
2
|
|
|
3
3
|
impl Document {
|
|
4
|
-
pub fn move_item(&mut self,
|
|
4
|
+
pub fn move_item(&mut self, selector: &str, from: usize, to: usize) -> Result<(), YerbaError> {
|
|
5
5
|
if from == to {
|
|
6
6
|
return Ok(());
|
|
7
7
|
}
|
|
8
8
|
|
|
9
|
-
let current_node = self.navigate(
|
|
9
|
+
let current_node = self.navigate(selector)?;
|
|
10
10
|
|
|
11
|
-
let sequence = find_block_sequence(¤t_node).ok_or_else(|| YerbaError::NotASequence(
|
|
11
|
+
let sequence = find_block_sequence(¤t_node).ok_or_else(|| YerbaError::NotASequence(selector.to_string()))?;
|
|
12
12
|
|
|
13
13
|
let entries: Vec<_> = sequence.entries().collect();
|
|
14
14
|
|
|
15
15
|
self.reorder_entries(sequence.syntax(), &entries, from, to)
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
pub fn move_key(&mut self,
|
|
18
|
+
pub fn move_key(&mut self, selector: &str, from: usize, to: usize) -> Result<(), YerbaError> {
|
|
19
19
|
if from == to {
|
|
20
20
|
return Ok(());
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
let current_node = self.navigate(
|
|
23
|
+
let current_node = self.navigate(selector)?;
|
|
24
24
|
|
|
25
|
-
let map = find_block_map(¤t_node).ok_or_else(|| YerbaError::SelectorNotFound(
|
|
25
|
+
let map = find_block_map(¤t_node).ok_or_else(|| YerbaError::SelectorNotFound(selector.to_string()))?;
|
|
26
26
|
|
|
27
27
|
let entries: Vec<_> = map.entries().collect();
|
|
28
28
|
|
|
29
29
|
self.reorder_entries(map.syntax(), &entries, from, to)
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
pub fn resolve_key_index(&self,
|
|
33
|
-
let current_node = self.navigate(
|
|
32
|
+
pub fn resolve_key_index(&self, selector: &str, reference: &str) -> Result<usize, YerbaError> {
|
|
33
|
+
let current_node = self.navigate(selector)?;
|
|
34
34
|
|
|
35
|
-
let map = find_block_map(¤t_node).ok_or_else(|| YerbaError::SelectorNotFound(
|
|
35
|
+
let map = find_block_map(¤t_node).ok_or_else(|| YerbaError::SelectorNotFound(selector.to_string()))?;
|
|
36
36
|
|
|
37
37
|
if let Ok(index) = reference.parse::<usize>() {
|
|
38
38
|
let length = map.entries().count();
|
|
@@ -55,13 +55,13 @@ impl Document {
|
|
|
55
55
|
.unwrap_or(false)
|
|
56
56
|
})
|
|
57
57
|
.map(|(index, _entry)| index)
|
|
58
|
-
.ok_or_else(|| YerbaError::SelectorNotFound(format!("{} key '{}'",
|
|
58
|
+
.ok_or_else(|| YerbaError::SelectorNotFound(format!("{} key '{}'", selector, reference)))
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
-
pub fn resolve_sequence_index(&self,
|
|
62
|
-
let current_node = self.navigate(
|
|
61
|
+
pub fn resolve_sequence_index(&self, selector: &str, reference: &str) -> Result<usize, YerbaError> {
|
|
62
|
+
let current_node = self.navigate(selector)?;
|
|
63
63
|
|
|
64
|
-
let sequence = find_block_sequence(¤t_node).ok_or_else(|| YerbaError::NotASequence(
|
|
64
|
+
let sequence = find_block_sequence(¤t_node).ok_or_else(|| YerbaError::NotASequence(selector.to_string()))?;
|
|
65
65
|
|
|
66
66
|
if let Ok(index) = reference.parse::<usize>() {
|
|
67
67
|
let length = sequence.entries().count();
|
|
@@ -79,7 +79,7 @@ impl Document {
|
|
|
79
79
|
.enumerate()
|
|
80
80
|
.find(|(_index, entry)| self.evaluate_condition_on_node(entry.syntax(), reference))
|
|
81
81
|
.map(|(index, _entry)| index)
|
|
82
|
-
.ok_or_else(|| YerbaError::SelectorNotFound(format!("{} condition '{}'",
|
|
82
|
+
.ok_or_else(|| YerbaError::SelectorNotFound(format!("{} condition '{}'", selector, reference)));
|
|
83
83
|
}
|
|
84
84
|
|
|
85
85
|
sequence
|
|
@@ -93,15 +93,15 @@ impl Document {
|
|
|
93
93
|
.unwrap_or(false)
|
|
94
94
|
})
|
|
95
95
|
.map(|(index, _entry)| index)
|
|
96
|
-
.ok_or_else(|| YerbaError::SelectorNotFound(format!("{} item '{}'",
|
|
96
|
+
.ok_or_else(|| YerbaError::SelectorNotFound(format!("{} item '{}'", selector, reference)))
|
|
97
97
|
}
|
|
98
98
|
|
|
99
|
-
pub fn validate_sort_keys(&self,
|
|
100
|
-
if let Some(
|
|
101
|
-
return self.validate_each_sort_keys(
|
|
99
|
+
pub fn validate_sort_keys(&self, selector: &str, key_order: &[&str]) -> Result<(), YerbaError> {
|
|
100
|
+
if let Some(sequence_selector) = strip_bracket_suffix(selector) {
|
|
101
|
+
return self.validate_each_sort_keys(sequence_selector, key_order);
|
|
102
102
|
}
|
|
103
103
|
|
|
104
|
-
let current_node = match self.navigate(
|
|
104
|
+
let current_node = match self.navigate(selector) {
|
|
105
105
|
Ok(node) => node,
|
|
106
106
|
Err(_) => return Ok(()),
|
|
107
107
|
};
|
|
@@ -124,12 +124,12 @@ impl Document {
|
|
|
124
124
|
}
|
|
125
125
|
}
|
|
126
126
|
|
|
127
|
-
pub fn sort_keys(&mut self,
|
|
128
|
-
if let Some(
|
|
129
|
-
return self.sort_each_keys(
|
|
127
|
+
pub fn sort_keys(&mut self, selector: &str, key_order: &[&str]) -> Result<(), YerbaError> {
|
|
128
|
+
if let Some(sequence_selector) = strip_bracket_suffix(selector) {
|
|
129
|
+
return self.sort_each_keys(sequence_selector, key_order);
|
|
130
130
|
}
|
|
131
131
|
|
|
132
|
-
let current_node = match self.navigate(
|
|
132
|
+
let current_node = match self.navigate(selector) {
|
|
133
133
|
Ok(node) => node,
|
|
134
134
|
Err(_) => return Ok(()),
|
|
135
135
|
};
|
|
@@ -145,14 +145,14 @@ impl Document {
|
|
|
145
145
|
}
|
|
146
146
|
}
|
|
147
147
|
|
|
148
|
-
pub fn sort_each_keys(&mut self,
|
|
149
|
-
let nodes = if
|
|
150
|
-
match self.navigate(
|
|
148
|
+
pub fn sort_each_keys(&mut self, selector: &str, key_order: &[&str]) -> Result<(), YerbaError> {
|
|
149
|
+
let nodes = if selector.is_empty() {
|
|
150
|
+
match self.navigate(selector) {
|
|
151
151
|
Ok(node) => vec![node],
|
|
152
152
|
Err(_) => return Ok(()),
|
|
153
153
|
}
|
|
154
154
|
} else {
|
|
155
|
-
let found = self.navigate_all_compact(
|
|
155
|
+
let found = self.navigate_all_compact(selector);
|
|
156
156
|
if found.is_empty() {
|
|
157
157
|
return Ok(());
|
|
158
158
|
}
|
|
@@ -182,14 +182,14 @@ impl Document {
|
|
|
182
182
|
self.apply_edits(edits)
|
|
183
183
|
}
|
|
184
184
|
|
|
185
|
-
pub fn validate_each_sort_keys(&self,
|
|
186
|
-
let nodes = if
|
|
187
|
-
match self.navigate(
|
|
185
|
+
pub fn validate_each_sort_keys(&self, selector: &str, key_order: &[&str]) -> Result<(), YerbaError> {
|
|
186
|
+
let nodes = if selector.is_empty() {
|
|
187
|
+
match self.navigate(selector) {
|
|
188
188
|
Ok(node) => vec![node],
|
|
189
189
|
Err(_) => return Ok(()),
|
|
190
190
|
}
|
|
191
191
|
} else {
|
|
192
|
-
let found = self.navigate_all_compact(
|
|
192
|
+
let found = self.navigate_all_compact(selector);
|
|
193
193
|
if found.is_empty() {
|
|
194
194
|
return Ok(());
|
|
195
195
|
}
|
|
@@ -224,39 +224,43 @@ impl Document {
|
|
|
224
224
|
}
|
|
225
225
|
}
|
|
226
226
|
|
|
227
|
-
pub fn sort_items(&mut self,
|
|
228
|
-
|
|
229
|
-
|
|
227
|
+
pub fn sort_items(&mut self, selector: &str, sort_fields: &[SortField], case_sensitive: bool) -> Result<(), YerbaError> {
|
|
228
|
+
self.sort_items_with(selector, sort_fields, case_sensitive, None)
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
pub fn sort_items_with(&mut self, selector: &str, sort_fields: &[SortField], case_sensitive: bool, missing: Option<SortMissing>) -> Result<(), YerbaError> {
|
|
232
|
+
if selector.contains("[].") {
|
|
233
|
+
return self.sort_each_items(selector, sort_fields, case_sensitive, missing);
|
|
230
234
|
}
|
|
231
235
|
|
|
232
|
-
let current_node = self.navigate(
|
|
236
|
+
let current_node = self.navigate(selector)?;
|
|
233
237
|
|
|
234
238
|
let sequence = match find_block_sequence(¤t_node) {
|
|
235
239
|
Some(sequence) => sequence,
|
|
236
240
|
None => return Ok(()),
|
|
237
241
|
};
|
|
238
242
|
|
|
239
|
-
match sort_sequence_edit(&sequence, sort_fields, case_sensitive) {
|
|
243
|
+
match sort_sequence_edit(&sequence, sort_fields, case_sensitive, missing) {
|
|
240
244
|
Some(edit) => self.apply_edits(vec![edit]),
|
|
241
245
|
None => Ok(()),
|
|
242
246
|
}
|
|
243
247
|
}
|
|
244
248
|
|
|
245
|
-
fn sort_each_items(&mut self,
|
|
246
|
-
let (
|
|
247
|
-
(&
|
|
249
|
+
fn sort_each_items(&mut self, selector: &str, sort_fields: &[SortField], case_sensitive: bool, missing: Option<SortMissing>) -> Result<(), YerbaError> {
|
|
250
|
+
let (parent_selector, child_selector) = if let Some(last_bracket) = selector.rfind("[].") {
|
|
251
|
+
(&selector[..last_bracket + 2], &selector[last_bracket + 3..])
|
|
248
252
|
} else {
|
|
249
|
-
(
|
|
253
|
+
(selector, "")
|
|
250
254
|
};
|
|
251
255
|
|
|
252
|
-
let parent_nodes = self.navigate_all_compact(
|
|
256
|
+
let parent_nodes = self.navigate_all_compact(parent_selector);
|
|
253
257
|
let mut edits: Vec<(TextRange, String)> = Vec::new();
|
|
254
258
|
|
|
255
259
|
for parent_node in &parent_nodes {
|
|
256
|
-
let child_nodes = if
|
|
260
|
+
let child_nodes = if child_selector.is_empty() {
|
|
257
261
|
vec![parent_node.clone()]
|
|
258
262
|
} else {
|
|
259
|
-
navigate_from_node(parent_node,
|
|
263
|
+
navigate_from_node(parent_node, child_selector)
|
|
260
264
|
};
|
|
261
265
|
|
|
262
266
|
for child_node in &child_nodes {
|
|
@@ -265,7 +269,7 @@ impl Document {
|
|
|
265
269
|
None => continue,
|
|
266
270
|
};
|
|
267
271
|
|
|
268
|
-
if let Some(edit) = sort_sequence_edit(&sequence, sort_fields, case_sensitive) {
|
|
272
|
+
if let Some(edit) = sort_sequence_edit(&sequence, sort_fields, case_sensitive, missing) {
|
|
269
273
|
edits.push(edit);
|
|
270
274
|
}
|
|
271
275
|
}
|
|
@@ -274,20 +278,20 @@ impl Document {
|
|
|
274
278
|
self.apply_edits(edits)
|
|
275
279
|
}
|
|
276
280
|
|
|
277
|
-
pub fn reorder_items(&mut self,
|
|
281
|
+
pub fn reorder_items(&mut self, selector: &str, by: &str, desired_order: &[&str]) -> Result<(), YerbaError> {
|
|
278
282
|
let field = by.strip_prefix('.').unwrap_or(by);
|
|
279
283
|
let is_scalar = field.is_empty() || field == ".";
|
|
280
284
|
|
|
281
285
|
let items_selector = if is_scalar {
|
|
282
|
-
if
|
|
286
|
+
if selector.is_empty() {
|
|
283
287
|
"[]".to_string()
|
|
284
288
|
} else {
|
|
285
|
-
format!("{}[]",
|
|
289
|
+
format!("{}[]", selector)
|
|
286
290
|
}
|
|
287
|
-
} else if
|
|
291
|
+
} else if selector.is_empty() {
|
|
288
292
|
format!("[].{}", field)
|
|
289
293
|
} else {
|
|
290
|
-
format!("{}[].{}",
|
|
294
|
+
format!("{}[].{}", selector, field)
|
|
291
295
|
};
|
|
292
296
|
|
|
293
297
|
let labels: Vec<String> = match self.get_value(&items_selector) {
|
|
@@ -332,7 +336,7 @@ impl Document {
|
|
|
332
336
|
let source = moves[target];
|
|
333
337
|
|
|
334
338
|
if source != target {
|
|
335
|
-
self.move_item(
|
|
339
|
+
self.move_item(selector, source, target)?;
|
|
336
340
|
|
|
337
341
|
for item in moves.iter_mut().skip(target + 1) {
|
|
338
342
|
if *item >= target && *item < source {
|
|
@@ -398,7 +402,7 @@ fn sort_map_edit(map: &BlockMap, key_order: &[&str]) -> Option<(TextRange, Strin
|
|
|
398
402
|
Some((range, rebuild_from_groups(&sorted_groups, &indent, false)))
|
|
399
403
|
}
|
|
400
404
|
|
|
401
|
-
fn sort_sequence_edit(sequence: &BlockSeq, sort_fields: &[SortField], case_sensitive: bool) -> Option<(TextRange, String)> {
|
|
405
|
+
fn sort_sequence_edit(sequence: &BlockSeq, sort_fields: &[SortField], case_sensitive: bool, missing: Option<SortMissing>) -> Option<(TextRange, String)> {
|
|
402
406
|
let entries: Vec<_> = sequence.entries().collect();
|
|
403
407
|
|
|
404
408
|
if entries.len() <= 1 {
|
|
@@ -415,7 +419,30 @@ fn sort_sequence_edit(sequence: &BlockSeq, sort_fields: &[SortField], case_sensi
|
|
|
415
419
|
|
|
416
420
|
let original_bodies: Vec<String> = sortable.iter().map(|(_, group)| group.body.clone()).collect();
|
|
417
421
|
|
|
418
|
-
|
|
422
|
+
let compare = |(values_a, _): &(Vec<String>, EntryGroup), (values_b, _): &(Vec<String>, EntryGroup)| {
|
|
423
|
+
compare_sort_values(values_a, values_b, sort_fields, case_sensitive, missing)
|
|
424
|
+
};
|
|
425
|
+
|
|
426
|
+
let is_absent = |(values, _): &(Vec<String>, EntryGroup)| values.iter().any(|value| value.is_empty());
|
|
427
|
+
|
|
428
|
+
match missing {
|
|
429
|
+
Some(SortMissing::Skip) if sortable.iter().any(is_absent) => return None,
|
|
430
|
+
Some(SortMissing::Keep) => {
|
|
431
|
+
let absent_positions: Vec<bool> = sortable.iter().map(is_absent).collect();
|
|
432
|
+
let (absent, mut present): (Vec<_>, Vec<_>) = sortable.into_iter().partition(is_absent);
|
|
433
|
+
|
|
434
|
+
present.sort_by(compare);
|
|
435
|
+
|
|
436
|
+
let mut present = present.into_iter();
|
|
437
|
+
let mut absent = absent.into_iter();
|
|
438
|
+
|
|
439
|
+
sortable = absent_positions
|
|
440
|
+
.iter()
|
|
441
|
+
.filter_map(|is_absent| if *is_absent { absent.next() } else { present.next() })
|
|
442
|
+
.collect();
|
|
443
|
+
}
|
|
444
|
+
_ => sortable.sort_by(compare),
|
|
445
|
+
}
|
|
419
446
|
|
|
420
447
|
let sorted_bodies: Vec<String> = sortable.iter().map(|(_, group)| group.body.clone()).collect();
|
|
421
448
|
|
|
@@ -439,17 +466,23 @@ fn entry_sort_values(entry: &yaml_parser::ast::BlockSeqEntry, sort_fields: &[Sor
|
|
|
439
466
|
sort_fields
|
|
440
467
|
.iter()
|
|
441
468
|
.map(|field| {
|
|
442
|
-
if field.
|
|
469
|
+
if field.selector.is_empty() {
|
|
443
470
|
scalar_value()
|
|
444
471
|
} else {
|
|
445
|
-
let nodes = navigate_from_node(entry.syntax(), &field.
|
|
472
|
+
let nodes = navigate_from_node(entry.syntax(), &field.selector);
|
|
446
473
|
nodes.first().and_then(extract_scalar_text).unwrap_or_default()
|
|
447
474
|
}
|
|
448
475
|
})
|
|
449
476
|
.collect()
|
|
450
477
|
}
|
|
451
478
|
|
|
452
|
-
fn compare_sort_values(
|
|
479
|
+
fn compare_sort_values(
|
|
480
|
+
values_a: &[String],
|
|
481
|
+
values_b: &[String],
|
|
482
|
+
sort_fields: &[SortField],
|
|
483
|
+
case_sensitive: bool,
|
|
484
|
+
missing: Option<SortMissing>,
|
|
485
|
+
) -> std::cmp::Ordering {
|
|
453
486
|
let compare = |value_a: &String, value_b: &String| {
|
|
454
487
|
if case_sensitive {
|
|
455
488
|
value_a.cmp(value_b)
|
|
@@ -458,7 +491,19 @@ fn compare_sort_values(values_a: &[String], values_b: &[String], sort_fields: &[
|
|
|
458
491
|
}
|
|
459
492
|
};
|
|
460
493
|
|
|
494
|
+
let missing_ordering = |value_a: &String, value_b: &String| match (missing, value_a.is_empty(), value_b.is_empty()) {
|
|
495
|
+
(Some(SortMissing::First), true, false) => Some(std::cmp::Ordering::Less),
|
|
496
|
+
(Some(SortMissing::First), false, true) => Some(std::cmp::Ordering::Greater),
|
|
497
|
+
(Some(SortMissing::Last), true, false) => Some(std::cmp::Ordering::Greater),
|
|
498
|
+
(Some(SortMissing::Last), false, true) => Some(std::cmp::Ordering::Less),
|
|
499
|
+
_ => None,
|
|
500
|
+
};
|
|
501
|
+
|
|
461
502
|
for (index, field) in sort_fields.iter().enumerate().take(values_a.len()) {
|
|
503
|
+
if let Some(ordering) = missing_ordering(&values_a[index], &values_b[index]) {
|
|
504
|
+
return ordering;
|
|
505
|
+
}
|
|
506
|
+
|
|
462
507
|
let ordering = compare(&values_a[index], &values_b[index]);
|
|
463
508
|
let ordering = if field.ascending { ordering } else { ordering.reverse() };
|
|
464
509
|
|
|
@@ -468,18 +513,18 @@ fn compare_sort_values(values_a: &[String], values_b: &[String], sort_fields: &[
|
|
|
468
513
|
}
|
|
469
514
|
|
|
470
515
|
if sort_fields.is_empty() && !values_a.is_empty() && !values_b.is_empty() {
|
|
471
|
-
return compare(&values_a[0], &values_b[0]);
|
|
516
|
+
return missing_ordering(&values_a[0], &values_b[0]).unwrap_or_else(|| compare(&values_a[0], &values_b[0]));
|
|
472
517
|
}
|
|
473
518
|
|
|
474
519
|
std::cmp::Ordering::Equal
|
|
475
520
|
}
|
|
476
521
|
|
|
477
|
-
fn strip_bracket_suffix(
|
|
478
|
-
if
|
|
522
|
+
fn strip_bracket_suffix(selector: &str) -> Option<&str> {
|
|
523
|
+
if selector == "[]" {
|
|
479
524
|
Some("")
|
|
480
|
-
} else if let Some(stripped) =
|
|
525
|
+
} else if let Some(stripped) = selector.strip_suffix(".[]") {
|
|
481
526
|
Some(stripped)
|
|
482
527
|
} else {
|
|
483
|
-
|
|
528
|
+
selector.strip_suffix("[]")
|
|
484
529
|
}
|
|
485
530
|
}
|