yerba 0.9.2-aarch64-linux-gnu → 0.10.0-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 +34 -17
- data/exe/aarch64-linux-gnu/yerba +0 -0
- data/ext/yerba/include/yerba.h +56 -34
- data/ext/yerba/yerba.c +268 -186
- 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/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 +138 -64
- 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 +54 -54
- 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 +218 -106
- data/rust/src/json.rs +62 -2
- data/rust/src/lib.rs +2 -1
- 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 +124 -76
- metadata +3 -2
data/rust/src/json.rs
CHANGED
|
@@ -1,5 +1,63 @@
|
|
|
1
1
|
use crate::selector::{Selector, SelectorSegment};
|
|
2
2
|
|
|
3
|
+
/// Serializes `value` as JSON, writing non-finite floats as `NaN`, `Infinity` and `-Infinity`
|
|
4
|
+
/// (which Ruby's `JSON.parse` reads with `allow_nan: true`) instead of dropping them to `null`.
|
|
5
|
+
pub fn yaml_to_json_string_allowing_nan(value: &yaml_serde::Value) -> String {
|
|
6
|
+
let mut output = String::new();
|
|
7
|
+
write_json_allowing_nan(value, &mut output);
|
|
8
|
+
output
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
fn write_json_allowing_nan(value: &yaml_serde::Value, output: &mut String) {
|
|
12
|
+
match value {
|
|
13
|
+
yaml_serde::Value::Number(number) if number.is_nan() => output.push_str("NaN"),
|
|
14
|
+
yaml_serde::Value::Number(number) if number.is_infinite() => output.push_str(if number.as_f64().is_some_and(f64::is_sign_negative) {
|
|
15
|
+
"-Infinity"
|
|
16
|
+
} else {
|
|
17
|
+
"Infinity"
|
|
18
|
+
}),
|
|
19
|
+
|
|
20
|
+
yaml_serde::Value::Sequence(sequence) => {
|
|
21
|
+
output.push('[');
|
|
22
|
+
|
|
23
|
+
for (index, item) in sequence.iter().enumerate() {
|
|
24
|
+
if index > 0 {
|
|
25
|
+
output.push(',');
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
write_json_allowing_nan(item, output);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
output.push(']');
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
yaml_serde::Value::Mapping(mapping) => {
|
|
35
|
+
output.push('{');
|
|
36
|
+
|
|
37
|
+
for (index, (key, item)) in mapping.iter().enumerate() {
|
|
38
|
+
if index > 0 {
|
|
39
|
+
output.push(',');
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
let key = match key {
|
|
43
|
+
yaml_serde::Value::String(string) => string.clone(),
|
|
44
|
+
_ => format!("{:?}", key),
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
output.push_str(&serde_json::Value::String(key).to_string());
|
|
48
|
+
output.push(':');
|
|
49
|
+
write_json_allowing_nan(item, output);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
output.push('}');
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
yaml_serde::Value::Tagged(tagged) => write_json_allowing_nan(&tagged.value, output),
|
|
56
|
+
|
|
57
|
+
_ => output.push_str(&yaml_to_json(value).to_string()),
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
3
61
|
pub fn yaml_to_json(value: &yaml_serde::Value) -> serde_json::Value {
|
|
4
62
|
match value {
|
|
5
63
|
yaml_serde::Value::Null => serde_json::Value::Null,
|
|
@@ -8,6 +66,8 @@ pub fn yaml_to_json(value: &yaml_serde::Value) -> serde_json::Value {
|
|
|
8
66
|
yaml_serde::Value::Number(number) => {
|
|
9
67
|
if let Some(integer) = number.as_i64() {
|
|
10
68
|
serde_json::Value::Number(integer.into())
|
|
69
|
+
} else if let Some(integer) = number.as_u64() {
|
|
70
|
+
serde_json::Value::Number(integer.into())
|
|
11
71
|
} else if let Some(float) = number.as_f64() {
|
|
12
72
|
serde_json::json!(float)
|
|
13
73
|
} else {
|
|
@@ -77,9 +137,9 @@ pub fn resolve_select_field(value: &yaml_serde::Value, field: &str) -> serde_jso
|
|
|
77
137
|
}
|
|
78
138
|
}
|
|
79
139
|
|
|
80
|
-
SelectorSegment::Index(
|
|
140
|
+
SelectorSegment::Index(_) | SelectorSegment::IndexFromEnd(_) => {
|
|
81
141
|
if let yaml_serde::Value::Sequence(sequence) = current {
|
|
82
|
-
if let Some(item) = sequence.get(
|
|
142
|
+
if let Some(item) = segment.sequence_index(sequence.len()).and_then(|index| sequence.get(index)) {
|
|
83
143
|
next_values.push(item.clone());
|
|
84
144
|
}
|
|
85
145
|
}
|
data/rust/src/lib.rs
CHANGED
|
@@ -22,7 +22,8 @@ pub use quote_style::{KeyStyle, QuoteStyle};
|
|
|
22
22
|
pub use selector::Selector;
|
|
23
23
|
pub use syntax::{
|
|
24
24
|
detect_yaml_type, escape_double_quoted, is_control_character, is_flow_collection, is_inline_scalar_safe, is_plain_safe, is_plain_safe_in_flow,
|
|
25
|
-
is_quoted_scalar, is_single_quotable, needs_quoting, needs_quoting_in_flow, quote_scalar, quote_scalar_styled,
|
|
25
|
+
is_quoted_scalar, is_single_quotable, needs_quoting, needs_quoting_in_flow, quote_scalar, quote_scalar_styled, resolve_plain_scalar, PlainScalar,
|
|
26
|
+
ScalarValue, YerbaValueType,
|
|
26
27
|
};
|
|
27
28
|
pub use validation::{find_control_characters, ControlCharacter};
|
|
28
29
|
pub use yaml_writer::json_to_yaml_text;
|
data/rust/src/main.rs
CHANGED
|
@@ -17,9 +17,10 @@ static HELP: LazyLock<String> = LazyLock::new(|| {
|
|
|
17
17
|
commands::colorize_help(indoc! {r#"
|
|
18
18
|
Selectors:
|
|
19
19
|
key A single key "database.host"
|
|
20
|
-
key.nested Nested
|
|
20
|
+
key.nested Nested keys "database.settings.pool"
|
|
21
21
|
[] All items in array "[].title"
|
|
22
22
|
[N] Item at index "[0].title"
|
|
23
|
+
[-N] Item from the end "[-1].title"
|
|
23
24
|
* All values in a map "database.*"
|
|
24
25
|
[].key[].nested Nested array access "[].speakers[].name"
|
|
25
26
|
|
data/rust/src/selector.rs
CHANGED
|
@@ -23,6 +23,17 @@ pub enum SelectorSegment {
|
|
|
23
23
|
AllItems,
|
|
24
24
|
AllKeys,
|
|
25
25
|
Index(usize),
|
|
26
|
+
IndexFromEnd(usize),
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
impl SelectorSegment {
|
|
30
|
+
pub(crate) fn sequence_index(&self, length: usize) -> Option<usize> {
|
|
31
|
+
match self {
|
|
32
|
+
SelectorSegment::Index(index) => (*index < length).then_some(*index),
|
|
33
|
+
SelectorSegment::IndexFromEnd(offset) => (*offset <= length).then(|| length - offset),
|
|
34
|
+
_ => None,
|
|
35
|
+
}
|
|
36
|
+
}
|
|
26
37
|
}
|
|
27
38
|
|
|
28
39
|
impl Selector {
|
|
@@ -59,14 +70,17 @@ impl Selector {
|
|
|
59
70
|
}
|
|
60
71
|
|
|
61
72
|
pub fn ends_with_bracket(&self) -> bool {
|
|
62
|
-
matches!(
|
|
73
|
+
matches!(
|
|
74
|
+
self.segments().last(),
|
|
75
|
+
Some(SelectorSegment::AllItems | SelectorSegment::Index(_) | SelectorSegment::IndexFromEnd(_))
|
|
76
|
+
)
|
|
63
77
|
}
|
|
64
78
|
|
|
65
79
|
pub fn has_brackets(&self) -> bool {
|
|
66
80
|
self
|
|
67
81
|
.segments()
|
|
68
82
|
.iter()
|
|
69
|
-
.any(|s| matches!(s, SelectorSegment::AllItems | SelectorSegment::Index(_)))
|
|
83
|
+
.any(|s| matches!(s, SelectorSegment::AllItems | SelectorSegment::Index(_) | SelectorSegment::IndexFromEnd(_)))
|
|
70
84
|
}
|
|
71
85
|
|
|
72
86
|
pub fn has_wildcard(&self) -> bool {
|
|
@@ -88,7 +102,7 @@ impl Selector {
|
|
|
88
102
|
|
|
89
103
|
let last_bracket = segments
|
|
90
104
|
.iter()
|
|
91
|
-
.rposition(|s| matches!(s, SelectorSegment::AllItems | SelectorSegment::Index(_)));
|
|
105
|
+
.rposition(|s| matches!(s, SelectorSegment::AllItems | SelectorSegment::Index(_) | SelectorSegment::IndexFromEnd(_)));
|
|
92
106
|
|
|
93
107
|
match last_bracket {
|
|
94
108
|
Some(position) => {
|
|
@@ -110,7 +124,9 @@ impl Selector {
|
|
|
110
124
|
pub fn split_at_first_bracket(&self) -> (Selector, Selector) {
|
|
111
125
|
let segments = self.segments();
|
|
112
126
|
|
|
113
|
-
let first_bracket = segments
|
|
127
|
+
let first_bracket = segments
|
|
128
|
+
.iter()
|
|
129
|
+
.position(|s| matches!(s, SelectorSegment::AllItems | SelectorSegment::Index(_) | SelectorSegment::IndexFromEnd(_)));
|
|
114
130
|
|
|
115
131
|
match first_bracket {
|
|
116
132
|
Some(position) => {
|
|
@@ -178,13 +194,17 @@ impl Selector {
|
|
|
178
194
|
SelectorSegment::Index(i) => {
|
|
179
195
|
result.push_str(&format!("[{}]", i));
|
|
180
196
|
}
|
|
197
|
+
|
|
198
|
+
SelectorSegment::IndexFromEnd(i) => {
|
|
199
|
+
result.push_str(&format!("[-{}]", i));
|
|
200
|
+
}
|
|
181
201
|
}
|
|
182
202
|
}
|
|
183
203
|
|
|
184
204
|
result
|
|
185
205
|
}
|
|
186
206
|
|
|
187
|
-
pub fn
|
|
207
|
+
pub fn parent_selector(&self) -> String {
|
|
188
208
|
let segments = self.segments();
|
|
189
209
|
|
|
190
210
|
if segments.len() <= 1 {
|
|
@@ -212,8 +232,10 @@ fn parse_segments(input: &str) -> Vec<SelectorSegment> {
|
|
|
212
232
|
|
|
213
233
|
if inner.is_empty() {
|
|
214
234
|
segments.push(SelectorSegment::AllItems);
|
|
215
|
-
} else if let
|
|
216
|
-
segments.push(
|
|
235
|
+
} else if let Some(index) = parse_index(inner) {
|
|
236
|
+
segments.push(index);
|
|
237
|
+
} else {
|
|
238
|
+
segments.push(SelectorSegment::Key(rest[..=close].to_string()));
|
|
217
239
|
}
|
|
218
240
|
|
|
219
241
|
rest = &rest[close + 1..];
|
|
@@ -222,6 +244,8 @@ fn parse_segments(input: &str) -> Vec<SelectorSegment> {
|
|
|
222
244
|
rest = &rest[1..];
|
|
223
245
|
}
|
|
224
246
|
} else {
|
|
247
|
+
segments.push(SelectorSegment::Key(rest.to_string()));
|
|
248
|
+
|
|
225
249
|
break;
|
|
226
250
|
}
|
|
227
251
|
} else {
|
|
@@ -266,3 +290,20 @@ fn parse_segments(input: &str) -> Vec<SelectorSegment> {
|
|
|
266
290
|
|
|
267
291
|
segments
|
|
268
292
|
}
|
|
293
|
+
|
|
294
|
+
fn parse_index(input: &str) -> Option<SelectorSegment> {
|
|
295
|
+
if input.bytes().all(|byte| byte.is_ascii_digit()) {
|
|
296
|
+
return Some(SelectorSegment::Index(input.parse::<usize>().unwrap_or(usize::MAX)));
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
let digits = input.strip_prefix('-')?;
|
|
300
|
+
|
|
301
|
+
if digits.is_empty() || !digits.bytes().all(|byte| byte.is_ascii_digit()) {
|
|
302
|
+
return None;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
match digits.parse::<usize>().unwrap_or(usize::MAX) {
|
|
306
|
+
0 => Some(SelectorSegment::Index(0)),
|
|
307
|
+
offset => Some(SelectorSegment::IndexFromEnd(offset)),
|
|
308
|
+
}
|
|
309
|
+
}
|