yerba 0.8.0 → 0.8.1
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/lib/yerba/version.rb +1 -1
- data/rust/Cargo.toml +15 -6
- data/rust/src/commands/blank_lines.rs +2 -2
- data/rust/src/commands/directives.rs +4 -4
- data/rust/src/commands/get.rs +7 -10
- data/rust/src/commands/init.rs +4 -4
- data/rust/src/commands/insert.rs +6 -6
- data/rust/src/commands/location.rs +2 -2
- data/rust/src/commands/mate.rs +7 -7
- data/rust/src/commands/mod.rs +119 -90
- data/rust/src/commands/move_key.rs +9 -6
- data/rust/src/commands/quote_style.rs +4 -5
- data/rust/src/commands/schema.rs +6 -6
- data/rust/src/commands/selectors.rs +2 -2
- data/rust/src/commands/set.rs +3 -2
- data/rust/src/commands/sort.rs +36 -42
- data/rust/src/commands/sort_keys.rs +2 -2
- data/rust/src/commands/ui.rs +157 -0
- data/rust/src/commands/unique.rs +7 -7
- data/rust/src/commands/version.rs +13 -3
- data/rust/src/document/insert.rs +4 -0
- data/rust/src/document/mod.rs +7 -3
- data/rust/src/document/set.rs +10 -2
- data/rust/src/error.rs +3 -0
- data/rust/src/ffi.rs +5 -0
- data/rust/src/lib.rs +9 -1
- data/rust/src/quote_style.rs +12 -11
- data/rust/src/syntax.rs +132 -1
- data/rust/src/yaml_writer.rs +4 -4
- metadata +2 -1
data/rust/src/document/set.rs
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
use super::*;
|
|
2
2
|
|
|
3
|
+
fn scalar_replacement_text(value: &str, kind: SyntaxKind) -> String {
|
|
4
|
+
if kind == SyntaxKind::PLAIN_SCALAR && !crate::syntax::is_inline_scalar_safe(value) {
|
|
5
|
+
return format_scalar_value(value, SyntaxKind::DOUBLE_QUOTED_SCALAR);
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
format_scalar_value(value, kind)
|
|
9
|
+
}
|
|
10
|
+
|
|
3
11
|
fn holds_collection(node: &SyntaxNode) -> bool {
|
|
4
12
|
node.descendants().any(|descendant| {
|
|
5
13
|
matches!(
|
|
@@ -84,7 +92,7 @@ impl Document {
|
|
|
84
92
|
|
|
85
93
|
let scalar_token = find_scalar_token(¤t_node).ok_or_else(|| YerbaError::SelectorNotFound(dot_path.to_string()))?;
|
|
86
94
|
|
|
87
|
-
let new_text =
|
|
95
|
+
let new_text = scalar_replacement_text(value, scalar_token.kind());
|
|
88
96
|
|
|
89
97
|
self.replace_token(&scalar_token, &new_text)
|
|
90
98
|
}
|
|
@@ -107,7 +115,7 @@ impl Document {
|
|
|
107
115
|
edits.push((span.range, replacement_text(&span, value)));
|
|
108
116
|
}
|
|
109
117
|
} else if let Some(scalar_token) = find_scalar_token(&node) {
|
|
110
|
-
edits.push((scalar_token.text_range(),
|
|
118
|
+
edits.push((scalar_token.text_range(), scalar_replacement_text(value, scalar_token.kind())));
|
|
111
119
|
}
|
|
112
120
|
}
|
|
113
121
|
|
data/rust/src/error.rs
CHANGED
|
@@ -10,6 +10,7 @@ pub enum YerbaError {
|
|
|
10
10
|
IndexOutOfBounds(usize, usize),
|
|
11
11
|
UnknownKeys(Vec<String>),
|
|
12
12
|
DuplicateValues(Vec<crate::DuplicateInfo>),
|
|
13
|
+
#[cfg(feature = "schema")]
|
|
13
14
|
SchemaValidation(Vec<crate::schema::ValidationError>),
|
|
14
15
|
DuplicateKey {
|
|
15
16
|
key: String,
|
|
@@ -78,6 +79,7 @@ impl std::fmt::Display for YerbaError {
|
|
|
78
79
|
write!(f, "index {} out of bounds (length {})", index, length)
|
|
79
80
|
}
|
|
80
81
|
|
|
82
|
+
#[cfg(feature = "schema")]
|
|
81
83
|
YerbaError::SchemaValidation(errors) => {
|
|
82
84
|
let details: Vec<String> = errors.iter().map(|error| error.to_string()).collect();
|
|
83
85
|
|
|
@@ -137,6 +139,7 @@ impl GitHubAnnotations for YerbaError {
|
|
|
137
139
|
})
|
|
138
140
|
.collect(),
|
|
139
141
|
|
|
142
|
+
#[cfg(feature = "schema")]
|
|
140
143
|
YerbaError::SchemaValidation(errors) => errors
|
|
141
144
|
.iter()
|
|
142
145
|
.map(|error| GitHubAnnotation {
|
data/rust/src/ffi.rs
CHANGED
|
@@ -746,6 +746,7 @@ pub unsafe extern "C" fn yerba_document_blank_lines(document: *mut Document, pat
|
|
|
746
746
|
}
|
|
747
747
|
|
|
748
748
|
/// Caller must free with yerba_string_free.
|
|
749
|
+
#[cfg(feature = "schema")]
|
|
749
750
|
#[no_mangle]
|
|
750
751
|
pub unsafe extern "C" fn yerba_document_validate_schema(document: *const Document, schema_json: *const c_char, selector: *const c_char) -> *mut c_char {
|
|
751
752
|
let document = &*document;
|
|
@@ -784,6 +785,7 @@ pub unsafe extern "C" fn yerba_document_validate_schema(document: *const Documen
|
|
|
784
785
|
}
|
|
785
786
|
|
|
786
787
|
/// Caller must free with yerba_string_free.
|
|
788
|
+
#[cfg(feature = "cli")]
|
|
787
789
|
#[no_mangle]
|
|
788
790
|
pub unsafe extern "C" fn yerba_yerbafile_find(directory: *const c_char) -> *mut c_char {
|
|
789
791
|
let start = if directory.is_null() {
|
|
@@ -803,6 +805,7 @@ pub unsafe extern "C" fn yerba_yerbafile_find(directory: *const c_char) -> *mut
|
|
|
803
805
|
}
|
|
804
806
|
}
|
|
805
807
|
|
|
808
|
+
#[cfg(feature = "cli")]
|
|
806
809
|
#[no_mangle]
|
|
807
810
|
pub unsafe extern "C" fn yerba_document_apply_yerbafile(document: *mut Document, file_path: *const c_char, yerbafile_path: *const c_char) -> YerbaResult {
|
|
808
811
|
let document = &mut *document;
|
|
@@ -924,6 +927,7 @@ fn located_nodes_to_list(nodes: &[crate::LocatedNode]) -> YerbaTypedList {
|
|
|
924
927
|
}
|
|
925
928
|
}
|
|
926
929
|
|
|
930
|
+
#[cfg(feature = "glob")]
|
|
927
931
|
#[no_mangle]
|
|
928
932
|
pub unsafe extern "C" fn yerba_glob_get(glob_pattern: *const c_char, path: *const c_char) -> YerbaTypedList {
|
|
929
933
|
let pattern = CStr::from_ptr(glob_pattern).to_str().unwrap_or("");
|
|
@@ -940,6 +944,7 @@ pub unsafe extern "C" fn yerba_document_get_all(document: *const Document, path:
|
|
|
940
944
|
located_nodes_to_list(&document.get_all_located(selector_string))
|
|
941
945
|
}
|
|
942
946
|
|
|
947
|
+
#[cfg(feature = "glob")]
|
|
943
948
|
#[no_mangle]
|
|
944
949
|
pub unsafe extern "C" fn yerba_glob_find(glob_pattern: *const c_char, path: *const c_char, condition: *const c_char, select: *const c_char) -> YerbaTypedList {
|
|
945
950
|
let pattern = CStr::from_ptr(glob_pattern).to_str().unwrap_or("");
|
data/rust/src/lib.rs
CHANGED
|
@@ -4,10 +4,12 @@ pub mod error;
|
|
|
4
4
|
pub mod ffi;
|
|
5
5
|
pub mod json;
|
|
6
6
|
mod quote_style;
|
|
7
|
+
#[cfg(feature = "schema")]
|
|
7
8
|
pub mod schema;
|
|
8
9
|
pub mod selector;
|
|
9
10
|
mod syntax;
|
|
10
11
|
mod yaml_writer;
|
|
12
|
+
#[cfg(feature = "cli")]
|
|
11
13
|
pub mod yerbafile;
|
|
12
14
|
|
|
13
15
|
pub use document::style::StyleEnforcement;
|
|
@@ -17,8 +19,12 @@ pub use document::{
|
|
|
17
19
|
pub use error::YerbaError;
|
|
18
20
|
pub use quote_style::{KeyStyle, QuoteStyle};
|
|
19
21
|
pub use selector::Selector;
|
|
20
|
-
pub use syntax::{
|
|
22
|
+
pub use syntax::{
|
|
23
|
+
detect_yaml_type, is_flow_collection, is_inline_scalar_safe, is_plain_safe, is_plain_safe_in_flow, is_quoted_scalar, is_raw_yaml_text, is_valid_inline_value,
|
|
24
|
+
needs_quoting, needs_quoting_in_flow, quote_if_needed, ScalarValue, YerbaValueType,
|
|
25
|
+
};
|
|
21
26
|
pub use yaml_writer::json_to_yaml_text;
|
|
27
|
+
#[cfg(feature = "cli")]
|
|
22
28
|
pub use yerbafile::Yerbafile;
|
|
23
29
|
|
|
24
30
|
pub fn version() -> &'static str {
|
|
@@ -33,6 +39,7 @@ pub fn parse_file(path: impl AsRef<std::path::Path>) -> Result<Document, YerbaEr
|
|
|
33
39
|
Document::parse_file(path)
|
|
34
40
|
}
|
|
35
41
|
|
|
42
|
+
#[cfg(feature = "glob")]
|
|
36
43
|
pub fn glob_get(pattern: &str, selector: &str) -> Vec<document::LocatedNode> {
|
|
37
44
|
use rayon::prelude::*;
|
|
38
45
|
|
|
@@ -55,6 +62,7 @@ pub fn glob_get(pattern: &str, selector: &str) -> Vec<document::LocatedNode> {
|
|
|
55
62
|
.collect()
|
|
56
63
|
}
|
|
57
64
|
|
|
65
|
+
#[cfg(feature = "glob")]
|
|
58
66
|
pub fn glob_find(pattern: &str, selector: &str, condition: Option<&str>, select: Option<&str>) -> Result<Vec<serde_json::Value>, YerbaError> {
|
|
59
67
|
use rayon::prelude::*;
|
|
60
68
|
|
data/rust/src/quote_style.rs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
use clap::ValueEnum;
|
|
2
1
|
use yaml_parser::SyntaxKind;
|
|
3
2
|
|
|
4
|
-
#[derive(Debug, Clone, PartialEq
|
|
3
|
+
#[derive(Debug, Clone, PartialEq)]
|
|
4
|
+
#[cfg_attr(feature = "cli", derive(clap::ValueEnum))]
|
|
5
5
|
pub enum KeyStyle {
|
|
6
6
|
/// Unquoted key (host:)
|
|
7
7
|
Plain,
|
|
@@ -34,33 +34,34 @@ impl KeyStyle {
|
|
|
34
34
|
}
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
#[derive(Debug, Clone, PartialEq
|
|
37
|
+
#[derive(Debug, Clone, PartialEq)]
|
|
38
|
+
#[cfg_attr(feature = "cli", derive(clap::ValueEnum))]
|
|
38
39
|
pub enum QuoteStyle {
|
|
39
40
|
/// Unquoted value (host: localhost)
|
|
40
41
|
Plain,
|
|
41
42
|
/// Single-quoted value (host: 'localhost')
|
|
42
|
-
#[value(alias = "single-quoted")]
|
|
43
|
+
#[cfg_attr(feature = "cli", value(alias = "single-quoted"))]
|
|
43
44
|
Single,
|
|
44
45
|
/// Double-quoted value (host: "localhost")
|
|
45
|
-
#[value(alias = "double-quoted")]
|
|
46
|
+
#[cfg_attr(feature = "cli", value(alias = "double-quoted"))]
|
|
46
47
|
Double,
|
|
47
48
|
/// Literal block scalar, strip trailing newline (|-)
|
|
48
|
-
#[value(alias = "block-literal", alias = "|-")]
|
|
49
|
+
#[cfg_attr(feature = "cli", value(alias = "block-literal", alias = "|-"))]
|
|
49
50
|
Literal,
|
|
50
51
|
/// Literal block scalar, keep one trailing newline (|)
|
|
51
|
-
#[value(alias = "|")]
|
|
52
|
+
#[cfg_attr(feature = "cli", value(alias = "|"))]
|
|
52
53
|
LiteralClip,
|
|
53
54
|
/// Literal block scalar, keep all trailing newlines (|+)
|
|
54
|
-
#[value(alias = "|+")]
|
|
55
|
+
#[cfg_attr(feature = "cli", value(alias = "|+"))]
|
|
55
56
|
LiteralKeep,
|
|
56
57
|
/// Folded block scalar, strip trailing newline (>-)
|
|
57
|
-
#[value(alias = "block-folded", alias = ">-")]
|
|
58
|
+
#[cfg_attr(feature = "cli", value(alias = "block-folded", alias = ">-"))]
|
|
58
59
|
Folded,
|
|
59
60
|
/// Folded block scalar, keep one trailing newline (>)
|
|
60
|
-
#[value(alias = ">")]
|
|
61
|
+
#[cfg_attr(feature = "cli", value(alias = ">"))]
|
|
61
62
|
FoldedClip,
|
|
62
63
|
/// Folded block scalar, keep all trailing newlines (>+)
|
|
63
|
-
#[value(alias = ">+")]
|
|
64
|
+
#[cfg_attr(feature = "cli", value(alias = ">+"))]
|
|
64
65
|
FoldedKeep,
|
|
65
66
|
}
|
|
66
67
|
|
data/rust/src/syntax.rs
CHANGED
|
@@ -182,8 +182,139 @@ pub fn format_scalar_value(value: &str, kind: SyntaxKind) -> String {
|
|
|
182
182
|
}
|
|
183
183
|
}
|
|
184
184
|
|
|
185
|
+
const LEADING_INDICATORS: [char; 16] = ['#', '&', '*', '!', '|', '>', '\'', '"', '%', '@', '`', ',', '[', ']', '{', '}'];
|
|
186
|
+
const FLOW_INDICATORS: [char; 5] = [',', '[', ']', '{', '}'];
|
|
187
|
+
|
|
188
|
+
pub fn is_plain_safe(value: &str) -> bool {
|
|
189
|
+
if value.is_empty() || value != value.trim() {
|
|
190
|
+
return false;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
if value.contains('\n') || value.contains('\t') {
|
|
194
|
+
return false;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
if value.contains(": ") || value.ends_with(':') {
|
|
198
|
+
return false;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
if value.contains(" #") {
|
|
202
|
+
return false;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
if value.starts_with("---") || value.starts_with("...") {
|
|
206
|
+
return false;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
let mut characters = value.chars();
|
|
210
|
+
let first = characters.next().expect("value is non-empty");
|
|
211
|
+
|
|
212
|
+
if LEADING_INDICATORS.contains(&first) {
|
|
213
|
+
return false;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
if matches!(first, '-' | '?' | ':') {
|
|
217
|
+
return matches!(characters.next(), Some(next) if next != ' ');
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
true
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
pub fn is_plain_safe_in_flow(value: &str) -> bool {
|
|
224
|
+
is_plain_safe(value) && !value.contains(FLOW_INDICATORS)
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
pub fn is_quoted_scalar(value: &str) -> bool {
|
|
228
|
+
let bytes = value.as_bytes();
|
|
229
|
+
|
|
230
|
+
if bytes.len() < 2 {
|
|
231
|
+
return false;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
match (bytes[0], bytes[bytes.len() - 1]) {
|
|
235
|
+
(b'"', b'"') => {
|
|
236
|
+
let interior = &value[1..value.len() - 1];
|
|
237
|
+
let mut escaped = false;
|
|
238
|
+
|
|
239
|
+
for character in interior.chars() {
|
|
240
|
+
if escaped {
|
|
241
|
+
escaped = false;
|
|
242
|
+
continue;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
match character {
|
|
246
|
+
'\\' => escaped = true,
|
|
247
|
+
'"' => return false,
|
|
248
|
+
_ => {}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
!escaped
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
(b'\'', b'\'') => {
|
|
256
|
+
let interior = &value[1..value.len() - 1];
|
|
257
|
+
let mut characters = interior.chars().peekable();
|
|
258
|
+
|
|
259
|
+
while let Some(character) = characters.next() {
|
|
260
|
+
if character == '\'' {
|
|
261
|
+
if characters.peek() == Some(&'\'') {
|
|
262
|
+
characters.next();
|
|
263
|
+
} else {
|
|
264
|
+
return false;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
true
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
_ => false,
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
pub fn is_flow_collection(value: &str) -> bool {
|
|
277
|
+
(value.starts_with('[') && value.ends_with(']')) || (value.starts_with('{') && value.ends_with('}'))
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
pub fn is_raw_yaml_text(value: &str) -> bool {
|
|
281
|
+
value.contains('\n') || value.starts_with("- ") || is_quoted_scalar(value) || is_flow_collection(value)
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
pub fn is_valid_inline_value(value: &str) -> bool {
|
|
285
|
+
if value.is_empty() {
|
|
286
|
+
return true;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
is_raw_yaml_text(value) || is_plain_safe(value)
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
pub fn is_inline_scalar_safe(value: &str) -> bool {
|
|
293
|
+
if value.is_empty() {
|
|
294
|
+
return true;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
if value.contains('\n') || value.starts_with("- ") {
|
|
298
|
+
return false;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
is_quoted_scalar(value) || is_flow_collection(value) || is_plain_safe(value)
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
pub fn needs_quoting(value: &str) -> bool {
|
|
305
|
+
is_yaml_non_string(value) || !is_plain_safe(value)
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
pub fn needs_quoting_in_flow(value: &str) -> bool {
|
|
309
|
+
is_yaml_non_string(value) || !is_plain_safe_in_flow(value)
|
|
310
|
+
}
|
|
311
|
+
|
|
185
312
|
pub fn quote_if_needed(value: &str) -> String {
|
|
186
|
-
if
|
|
313
|
+
if is_raw_yaml_text(value) {
|
|
314
|
+
return value.to_string();
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
if needs_quoting(value) {
|
|
187
318
|
format_scalar_value(value, SyntaxKind::DOUBLE_QUOTED_SCALAR)
|
|
188
319
|
} else {
|
|
189
320
|
value.to_string()
|
data/rust/src/yaml_writer.rs
CHANGED
|
@@ -69,8 +69,8 @@ pub fn yaml_value_to_flow_text(value: &yaml_serde::Value) -> String {
|
|
|
69
69
|
yaml_serde::Value::Number(number) => number.to_string(),
|
|
70
70
|
|
|
71
71
|
yaml_serde::Value::String(string) => {
|
|
72
|
-
if crate::syntax::
|
|
73
|
-
|
|
72
|
+
if crate::syntax::needs_quoting_in_flow(string) {
|
|
73
|
+
crate::syntax::format_scalar_value(string, yaml_parser::SyntaxKind::DOUBLE_QUOTED_SCALAR)
|
|
74
74
|
} else {
|
|
75
75
|
string.clone()
|
|
76
76
|
}
|
|
@@ -162,8 +162,8 @@ fn format_yaml_scalar(value: &Value, quote_style: &QuoteStyle) -> String {
|
|
|
162
162
|
}
|
|
163
163
|
|
|
164
164
|
QuoteStyle::Plain => {
|
|
165
|
-
if crate::syntax::
|
|
166
|
-
|
|
165
|
+
if crate::syntax::needs_quoting(string) {
|
|
166
|
+
crate::syntax::format_scalar_value(string, yaml_parser::SyntaxKind::DOUBLE_QUOTED_SCALAR)
|
|
167
167
|
} else {
|
|
168
168
|
string.clone()
|
|
169
169
|
}
|
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.8.
|
|
4
|
+
version: 0.8.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Marco Roth
|
|
@@ -63,6 +63,7 @@ files:
|
|
|
63
63
|
- rust/src/commands/set.rs
|
|
64
64
|
- rust/src/commands/sort.rs
|
|
65
65
|
- rust/src/commands/sort_keys.rs
|
|
66
|
+
- rust/src/commands/ui.rs
|
|
66
67
|
- rust/src/commands/unique.rs
|
|
67
68
|
- rust/src/commands/version.rs
|
|
68
69
|
- rust/src/didyoumean.rs
|