yerba 0.7.6-arm-linux-gnu → 0.8.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.
@@ -31,6 +31,7 @@ impl Document {
31
31
 
32
32
  pub fn delete(&mut self, dot_path: &str) -> Result<(), YerbaError> {
33
33
  Self::validate_path(dot_path)?;
34
+ self.refuse_flow_target(dot_path)?;
34
35
 
35
36
  let selector = crate::selector::Selector::parse(dot_path);
36
37
  let segments = selector.segments();
@@ -88,13 +89,17 @@ impl Document {
88
89
  }
89
90
 
90
91
  crate::selector::SelectorSegment::Index(index) => self.remove_at(&parent_path, *index),
91
- crate::selector::SelectorSegment::AllItems => Err(YerbaError::SelectorNotFound(dot_path.to_string())),
92
+ crate::selector::SelectorSegment::AllItems | crate::selector::SelectorSegment::AllKeys => Err(YerbaError::SelectorNotFound(dot_path.to_string())),
92
93
  }
93
94
  }
94
95
 
95
96
  pub fn remove(&mut self, dot_path: &str, value: &str) -> Result<(), YerbaError> {
96
97
  let current_node = self.navigate(dot_path)?;
97
98
 
99
+ if find_flow_sequence(&current_node).is_some() {
100
+ return Err(YerbaError::FlowCollectionNotWritable(dot_path.to_string()));
101
+ }
102
+
98
103
  let sequence = find_block_sequence(&current_node).ok_or_else(|| YerbaError::NotASequence(dot_path.to_string()))?;
99
104
 
100
105
  let target_entry = sequence
@@ -1,5 +1,26 @@
1
1
  use super::*;
2
2
 
3
+ fn key_of(node: &SyntaxNode, source: &str) -> (Option<String>, Location) {
4
+ node
5
+ .parent()
6
+ .and_then(|parent| {
7
+ use yaml_parser::ast::{BlockMapEntry, FlowMapEntry};
8
+
9
+ let key_node = match BlockMapEntry::cast(parent.clone()) {
10
+ Some(entry) => entry.key().map(|key| key.syntax().clone()),
11
+ None => FlowMapEntry::cast(parent).and_then(|entry| entry.key().map(|key| key.syntax().clone())),
12
+ }?;
13
+
14
+ let key_text = extract_scalar_text(&key_node)?;
15
+ let key_range = key_node.text_range();
16
+ let key_location = compute_location(source, key_range.start().into(), key_range.end().into());
17
+
18
+ Some((key_text, key_location))
19
+ })
20
+ .map(|(name, location)| (Some(name), location))
21
+ .unwrap_or((None, Location::default()))
22
+ }
23
+
3
24
  impl Document {
4
25
  pub fn is_valid_selector(&self, dot_path: &str) -> bool {
5
26
  if dot_path.is_empty() {
@@ -12,6 +33,10 @@ impl Document {
12
33
  return true;
13
34
  }
14
35
 
36
+ if crate::selector::Selector::parse(dot_path).has_wildcard() && !self.navigate_all_compact(dot_path).is_empty() {
37
+ return true;
38
+ }
39
+
15
40
  let wildcard_version = dot_path.replace(|c: char| c.is_ascii_digit(), "").replace("[.", "[");
16
41
 
17
42
  let mut normalized = String::new();
@@ -82,6 +107,49 @@ impl Document {
82
107
  self.navigate_all_compact(dot_path).iter().map(node_selector).collect()
83
108
  }
84
109
 
110
+ pub fn keys_at(&self, dot_path: &str) -> Vec<String> {
111
+ let node = if dot_path.is_empty() {
112
+ self.root.clone()
113
+ } else {
114
+ match self.navigate_all_compact(dot_path).into_iter().next() {
115
+ Some(node) => node,
116
+ None => return Vec::new(),
117
+ }
118
+ };
119
+
120
+ let collection = node.descendants().find(|descendant| {
121
+ matches!(
122
+ descendant.kind(),
123
+ SyntaxKind::BLOCK_MAP | SyntaxKind::BLOCK_SEQ | SyntaxKind::FLOW_MAP | SyntaxKind::FLOW_SEQ
124
+ )
125
+ });
126
+
127
+ let Some(collection) = collection else {
128
+ return Vec::new();
129
+ };
130
+
131
+ if let Some(map) = BlockMap::cast(collection.clone()) {
132
+ return map
133
+ .entries()
134
+ .filter_map(|entry| entry.key().and_then(|key| extract_scalar_text(key.syntax())))
135
+ .collect();
136
+ }
137
+
138
+ if let Some(map) = yaml_parser::ast::FlowMap::cast(collection) {
139
+ return map
140
+ .entries()
141
+ .map(|entries| {
142
+ entries
143
+ .entries()
144
+ .filter_map(|entry| entry.key().and_then(|key| extract_scalar_text(key.syntax())))
145
+ .collect()
146
+ })
147
+ .unwrap_or_default();
148
+ }
149
+
150
+ Vec::new()
151
+ }
152
+
85
153
  pub fn get_all_located(&self, dot_path: &str) -> Vec<LocatedNode> {
86
154
  let source = self.source_text();
87
155
  let file_path = self.path.as_ref().map(|p| p.to_string_lossy().to_string());
@@ -116,6 +184,8 @@ impl Document {
116
184
  }
117
185
  };
118
186
 
187
+ let (key_name, key_location) = key_of(node, &source);
188
+
119
189
  LocatedNode {
120
190
  node_type: node_type.to_string(),
121
191
  text,
@@ -124,6 +194,8 @@ impl Document {
124
194
  selector,
125
195
  line,
126
196
  location,
197
+ key_name,
198
+ key_location,
127
199
  }
128
200
  })
129
201
  .collect()
@@ -214,23 +286,7 @@ impl Document {
214
286
  let range = node.text_range();
215
287
  let location = compute_location(source, range.start().into(), range.end().into());
216
288
 
217
- let (key_name, key_location) = node
218
- .parent()
219
- .and_then(|parent| {
220
- use yaml_parser::ast::BlockMapEntry;
221
-
222
- BlockMapEntry::cast(parent).and_then(|entry| {
223
- entry.key().and_then(|key_node| {
224
- let key_text = extract_scalar_text(key_node.syntax())?;
225
- let key_range = key_node.syntax().text_range();
226
- let key_location = compute_location(source, key_range.start().into(), key_range.end().into());
227
-
228
- Some((key_text, key_location))
229
- })
230
- })
231
- })
232
- .map(|(name, location)| (Some(name), location))
233
- .unwrap_or((None, Location::default()));
289
+ let (key_name, key_location) = key_of(&node, source);
234
290
 
235
291
  (location, key_name, key_location)
236
292
  }
@@ -238,15 +294,15 @@ impl Document {
238
294
  }
239
295
  }
240
296
 
241
- pub fn find_items(&self, dot_path: &str, condition: Option<&str>, select: Option<&str>) -> Vec<serde_json::Value> {
297
+ pub fn find_items(&self, dot_path: &str, condition: Option<&str>, select: Option<&str>) -> Result<Vec<serde_json::Value>, YerbaError> {
242
298
  let values = match condition {
243
- Some(cond) => self.filter(dot_path, cond),
299
+ Some(cond) => self.filter(dot_path, cond)?,
244
300
  None => self.get_values(dot_path),
245
301
  };
246
302
 
247
303
  let select_fields: Option<Vec<&str>> = select.map(|s| s.split(',').collect());
248
304
 
249
- values
305
+ let items = values
250
306
  .iter()
251
307
  .map(|value| match &select_fields {
252
308
  Some(fields) => {
@@ -262,7 +318,9 @@ impl Document {
262
318
  }
263
319
  None => crate::json::yaml_to_json(value),
264
320
  })
265
- .collect()
321
+ .collect();
322
+
323
+ Ok(items)
266
324
  }
267
325
 
268
326
  pub fn get_value(&self, dot_path: &str) -> Option<yaml_serde::Value> {
@@ -337,7 +395,7 @@ impl Document {
337
395
  }
338
396
 
339
397
  pub fn exists(&self, dot_path: &str) -> bool {
340
- if dot_path.contains('[') {
398
+ if crate::selector::Selector::parse(dot_path).has_wildcard() {
341
399
  if !self.navigate_all_compact(dot_path).is_empty() {
342
400
  return true;
343
401
  }
@@ -455,11 +513,13 @@ pub(crate) fn node_selector(node: &SyntaxNode) -> String {
455
513
  let mut parts: Vec<String> = Vec::new();
456
514
  let mut current = node.clone();
457
515
 
458
- if current.kind() == SyntaxKind::BLOCK_SEQ_ENTRY {
516
+ if matches!(current.kind(), SyntaxKind::BLOCK_SEQ_ENTRY | SyntaxKind::FLOW_SEQ_ENTRY) {
459
517
  if let Some(parent) = current.parent() {
518
+ let kind = current.kind();
519
+
460
520
  let index = parent
461
521
  .children()
462
- .filter(|child| child.kind() == SyntaxKind::BLOCK_SEQ_ENTRY)
522
+ .filter(|child| child.kind() == kind)
463
523
  .position(|child| child == current)
464
524
  .unwrap_or(0);
465
525
 
@@ -474,11 +534,11 @@ pub(crate) fn node_selector(node: &SyntaxNode) -> String {
474
534
  };
475
535
 
476
536
  match parent.kind() {
477
- SyntaxKind::BLOCK_SEQ_ENTRY => {
537
+ SyntaxKind::BLOCK_SEQ_ENTRY | SyntaxKind::FLOW_SEQ_ENTRY => {
478
538
  if let Some(grandparent) = parent.parent() {
479
539
  let index = grandparent
480
540
  .children()
481
- .filter(|child| child.kind() == SyntaxKind::BLOCK_SEQ_ENTRY)
541
+ .filter(|child| child.kind() == parent.kind())
482
542
  .position(|child| child == parent)
483
543
  .unwrap_or(0);
484
544
 
@@ -494,6 +554,14 @@ pub(crate) fn node_selector(node: &SyntaxNode) -> String {
494
554
  }
495
555
  }
496
556
 
557
+ SyntaxKind::FLOW_MAP_ENTRY => {
558
+ if let Some(key_node) = parent.children().find(|child| child.kind() == SyntaxKind::FLOW_MAP_KEY) {
559
+ if let Some(key_text) = extract_scalar_text(&key_node) {
560
+ parts.push(key_text);
561
+ }
562
+ }
563
+ }
564
+
497
565
  _ => {}
498
566
  }
499
567
 
@@ -90,6 +90,28 @@ impl Document {
90
90
 
91
91
  pub fn insert_into(&mut self, dot_path: &str, value: &str, position: InsertPosition) -> Result<(), YerbaError> {
92
92
  Self::validate_path(dot_path)?;
93
+ self.refuse_flow_target(dot_path)?;
94
+
95
+ if let Some((parent_path, _)) = dot_path.rsplit_once('.') {
96
+ if let Ok(parent) = self.navigate(parent_path) {
97
+ let occupied_flow = find_flow_map(&parent).map(|map| !flow_map_entries(&map).is_empty()).unwrap_or(false)
98
+ || find_flow_sequence(&parent)
99
+ .map(|sequence| !flow_sequence_entries(&sequence).is_empty())
100
+ .unwrap_or(false);
101
+
102
+ if occupied_flow {
103
+ return Err(YerbaError::FlowCollectionNotWritable(dot_path.to_string()));
104
+ }
105
+ }
106
+ }
107
+
108
+ if crate::selector::Selector::parse(dot_path)
109
+ .segments()
110
+ .iter()
111
+ .any(|segment| matches!(segment, crate::selector::SelectorSegment::AllKeys))
112
+ {
113
+ return Err(YerbaError::SelectorNotFound(dot_path.to_string()));
114
+ }
93
115
 
94
116
  if let Ok(current_node) = self.navigate(dot_path) {
95
117
  if find_block_sequence(&current_node).is_some()
@@ -1,4 +1,5 @@
1
1
  mod condition;
2
+ pub use condition::{validate_condition, validate_item_condition};
2
3
  mod delete;
3
4
  mod get;
4
5
  mod insert;
@@ -20,6 +21,8 @@ pub struct LocatedNode {
20
21
  pub selector: String,
21
22
  pub line: usize,
22
23
  pub location: Location,
24
+ pub key_name: Option<String>,
25
+ pub key_location: Location,
23
26
  }
24
27
 
25
28
  use std::fs;
@@ -35,9 +38,10 @@ use crate::error::YerbaError;
35
38
  use crate::QuoteStyle;
36
39
 
37
40
  use crate::syntax::{
38
- column_at, dedent_block_scalar, extract_scalar, extract_scalar_text, find_block_map, find_block_sequence, find_entry_by_key, find_scalar_token,
39
- first_collection, format_scalar_value, is_map_key, is_yaml_non_string, line_at, line_start_at, preceding_whitespace_indent, preceding_whitespace_token,
40
- raw_scalar_value, removal_range, FirstCollection, ScalarValue,
41
+ column_at, dedent_block_scalar, extract_scalar, extract_scalar_text, find_block_map, find_block_sequence, find_entry_by_key, find_flow_entry_by_key,
42
+ find_flow_map, find_flow_sequence, find_scalar_token, first_collection, flow_map_entries, flow_sequence_entries, format_scalar_value, in_flow_collection,
43
+ is_map_key, is_yaml_non_string, line_at, line_start_at, preceding_whitespace_indent, preceding_whitespace_token, raw_scalar_value, removal_range,
44
+ FirstCollection, ScalarValue,
41
45
  };
42
46
 
43
47
  #[derive(Debug, Clone)]
@@ -190,6 +194,14 @@ impl Document {
190
194
  self.navigate_all(dot_path).into_iter().flatten().collect()
191
195
  }
192
196
 
197
+ pub(crate) fn refuse_flow_target(&self, dot_path: &str) -> Result<(), YerbaError> {
198
+ if self.navigate_all_compact(dot_path).iter().any(in_flow_collection) {
199
+ return Err(YerbaError::FlowCollectionNotWritable(dot_path.to_string()));
200
+ }
201
+
202
+ Ok(())
203
+ }
204
+
193
205
  pub fn navigate_all(&self, dot_path: &str) -> Vec<Option<SyntaxNode>> {
194
206
  if Document::validate_path(dot_path).is_err() {
195
207
  return Vec::new();
@@ -220,7 +232,7 @@ impl Document {
220
232
  let segments = parsed.segments();
221
233
 
222
234
  for (i, segment) in segments.iter().enumerate() {
223
- let is_wildcard = matches!(segment, crate::selector::SelectorSegment::AllItems);
235
+ let is_wildcard = matches!(segment, crate::selector::SelectorSegment::AllItems | crate::selector::SelectorSegment::AllKeys);
224
236
  let has_remaining = i + 1 < segments.len();
225
237
  let mut next_nodes: Vec<Option<SyntaxNode>> = Vec::new();
226
238
 
@@ -335,22 +347,28 @@ impl Document {
335
347
  return self.remove_node(entry_node);
336
348
  }
337
349
 
338
- let end = if let Some(next_sibling) = entry_node.next_sibling() {
339
- next_sibling.text_range().start()
340
- } else {
341
- entry_node.parent().map(|parent| parent.text_range().end()).unwrap_or(entry_range.end())
342
- };
350
+ let source = self.source_text();
343
351
 
344
- let start = if let Some(whitespace_token) = preceding_whitespace_token(entry_node) {
345
- let whitespace_text = whitespace_token.text();
346
- let whitespace_start = whitespace_token.text_range().start();
352
+ let (start, end) = match preceding_whitespace_token(entry_node) {
353
+ Some(whitespace_token) => {
354
+ let whitespace_text = whitespace_token.text();
355
+ let whitespace_start = whitespace_token.text_range().start();
347
356
 
348
- whitespace_text
349
- .rfind('\n')
350
- .map(|offset| whitespace_start + TextSize::from(offset as u32))
351
- .unwrap_or(whitespace_start)
352
- } else {
353
- entry_range.start()
357
+ let start = whitespace_text
358
+ .rfind('\n')
359
+ .map(|offset| whitespace_start + TextSize::from(offset as u32))
360
+ .unwrap_or(whitespace_start);
361
+
362
+ (start, entry_range.end())
363
+ }
364
+
365
+ None => {
366
+ let entry_end: usize = entry_range.end().into();
367
+ let line_start = line_start_at(&source, entry_range.start().into());
368
+ let after_entry = source[entry_end..].find('\n').map(|offset| entry_end + offset + 1).unwrap_or(entry_end);
369
+
370
+ (TextSize::from(line_start as u32), TextSize::from(after_entry as u32))
371
+ }
354
372
  };
355
373
 
356
374
  self.apply_edit(TextRange::new(start, end), "")
@@ -673,6 +691,8 @@ fn resolve_segment(node: &SyntaxNode, segment: &crate::selector::SelectorSegment
673
691
  SelectorSegment::AllItems => {
674
692
  if let Some(sequence) = find_block_sequence(node) {
675
693
  sequence.entries().map(|entry| entry.syntax().clone()).collect()
694
+ } else if let Some(sequence) = find_flow_sequence(node) {
695
+ flow_sequence_entries(&sequence)
676
696
  } else {
677
697
  Vec::new()
678
698
  }
@@ -681,6 +701,25 @@ fn resolve_segment(node: &SyntaxNode, segment: &crate::selector::SelectorSegment
681
701
  SelectorSegment::Index(index) => {
682
702
  if let Some(sequence) = find_block_sequence(node) {
683
703
  sequence.entries().nth(*index).map(|entry| vec![entry.syntax().clone()]).unwrap_or_default()
704
+ } else if let Some(sequence) = find_flow_sequence(node) {
705
+ flow_sequence_entries(&sequence)
706
+ .into_iter()
707
+ .nth(*index)
708
+ .map(|entry| vec![entry])
709
+ .unwrap_or_default()
710
+ } else {
711
+ Vec::new()
712
+ }
713
+ }
714
+
715
+ SelectorSegment::AllKeys => {
716
+ if let Some(map) = find_block_map(node) {
717
+ map.entries().filter_map(|entry| entry.value().map(|value| value.syntax().clone())).collect()
718
+ } else if let Some(map) = find_flow_map(node) {
719
+ flow_map_entries(&map)
720
+ .into_iter()
721
+ .filter_map(|entry| entry.value().map(|value| value.syntax().clone()))
722
+ .collect()
684
723
  } else {
685
724
  Vec::new()
686
725
  }
@@ -693,6 +732,16 @@ fn resolve_segment(node: &SyntaxNode, segment: &crate::selector::SelectorSegment
693
732
  return vec![value.syntax().clone()];
694
733
  }
695
734
  }
735
+
736
+ return Vec::new();
737
+ }
738
+
739
+ if let Some(map) = find_flow_map(node) {
740
+ if let Some(entry) = find_flow_entry_by_key(&map, key) {
741
+ if let Some(value) = entry.value() {
742
+ return vec![value.syntax().clone()];
743
+ }
744
+ }
696
745
  }
697
746
 
698
747
  Vec::new()
@@ -1,5 +1,70 @@
1
1
  use super::*;
2
2
 
3
+ fn holds_collection(node: &SyntaxNode) -> bool {
4
+ node.descendants().any(|descendant| {
5
+ matches!(
6
+ descendant.kind(),
7
+ SyntaxKind::BLOCK_MAP | SyntaxKind::BLOCK_SEQ | SyntaxKind::FLOW_MAP | SyntaxKind::FLOW_SEQ
8
+ )
9
+ })
10
+ }
11
+
12
+ struct ValueSpan {
13
+ range: TextRange,
14
+ separated: bool,
15
+ comment: Option<String>,
16
+ }
17
+
18
+ fn value_span(node: &SyntaxNode) -> Option<ValueSpan> {
19
+ let (container, separator_kind) = match node.kind() {
20
+ SyntaxKind::BLOCK_MAP_VALUE | SyntaxKind::FLOW_MAP_VALUE => (node.parent()?, SyntaxKind::COLON),
21
+ SyntaxKind::BLOCK_SEQ_ENTRY => (node.clone(), SyntaxKind::MINUS),
22
+
23
+ SyntaxKind::FLOW_SEQ_ENTRY => {
24
+ return Some(ValueSpan {
25
+ range: node.text_range(),
26
+ separated: false,
27
+ comment: None,
28
+ })
29
+ }
30
+
31
+ _ => return None,
32
+ };
33
+
34
+ let mut elements = container
35
+ .children_with_tokens()
36
+ .skip_while(|element| element.as_token().map(|token| token.kind() != separator_kind).unwrap_or(true));
37
+
38
+ let separator = elements.next()?.into_token()?;
39
+ let mut comment = None;
40
+
41
+ for element in elements {
42
+ match element.into_token() {
43
+ Some(token) if token.kind() == SyntaxKind::COMMENT => comment = Some(token.text().trim_end().to_string()),
44
+ Some(token) if token.kind() == SyntaxKind::WHITESPACE && token.text().contains('\n') => break,
45
+ Some(_) => {}
46
+ None => break,
47
+ }
48
+ }
49
+
50
+ Some(ValueSpan {
51
+ range: TextRange::new(separator.text_range().end(), node.text_range().end()),
52
+ separated: true,
53
+ comment,
54
+ })
55
+ }
56
+
57
+ fn replacement_text(span: &ValueSpan, value: &str) -> String {
58
+ let mut text = if span.separated { format!(" {}", value) } else { value.to_string() };
59
+
60
+ if let Some(comment) = &span.comment {
61
+ text.push(' ');
62
+ text.push_str(comment);
63
+ }
64
+
65
+ text
66
+ }
67
+
3
68
  impl Document {
4
69
  pub fn set(&mut self, dot_path: &str, value: &str) -> Result<(), YerbaError> {
5
70
  let current_node = self.navigate(dot_path)?;
@@ -11,6 +76,12 @@ impl Document {
11
76
  return self.apply_edit(block_scalar.text_range(), &new_text);
12
77
  }
13
78
 
79
+ if holds_collection(&current_node) {
80
+ let span = value_span(&current_node).ok_or_else(|| YerbaError::SelectorNotFound(dot_path.to_string()))?;
81
+
82
+ return self.apply_edit(span.range, &replacement_text(&span, value));
83
+ }
84
+
14
85
  let scalar_token = find_scalar_token(&current_node).ok_or_else(|| YerbaError::SelectorNotFound(dot_path.to_string()))?;
15
86
 
16
87
  let new_text = format_scalar_value(value, scalar_token.kind());
@@ -31,6 +102,10 @@ impl Document {
31
102
  for node in nodes {
32
103
  if let Some(block_scalar) = node.descendants().find(|child| child.kind() == SyntaxKind::BLOCK_SCALAR) {
33
104
  edits.push((block_scalar.text_range(), Self::block_scalar_replacement(&source, &block_scalar, value)));
105
+ } else if holds_collection(&node) {
106
+ if let Some(span) = value_span(&node) {
107
+ edits.push((span.range, replacement_text(&span, value)));
108
+ }
34
109
  } else if let Some(scalar_token) = find_scalar_token(&node) {
35
110
  edits.push((scalar_token.text_range(), format_scalar_value(value, scalar_token.kind())));
36
111
  }
@@ -68,6 +143,12 @@ impl Document {
68
143
  return self.apply_edit(range, value);
69
144
  }
70
145
 
146
+ if holds_collection(&current_node) {
147
+ let span = value_span(&current_node).ok_or_else(|| YerbaError::SelectorNotFound(dot_path.to_string()))?;
148
+
149
+ return self.apply_edit(span.range, &replacement_text(&span, value));
150
+ }
151
+
71
152
  let scalar_token = find_scalar_token(&current_node).ok_or_else(|| YerbaError::SelectorNotFound(dot_path.to_string()))?;
72
153
 
73
154
  self.replace_token(&scalar_token, value)
data/rust/src/error.rs CHANGED
@@ -4,7 +4,9 @@ pub enum YerbaError {
4
4
  IoError(std::io::Error),
5
5
  SelectorNotFound(String),
6
6
  AmbiguousSelector(String, usize),
7
+ InvalidCondition(String, String),
7
8
  NotASequence(String),
9
+ FlowCollectionNotWritable(String),
8
10
  IndexOutOfBounds(usize, usize),
9
11
  UnknownKeys(Vec<String>),
10
12
  DuplicateValues(Vec<crate::DuplicateInfo>),
@@ -25,6 +27,14 @@ impl std::fmt::Display for YerbaError {
25
27
  YerbaError::SelectorNotFound(selector) => write!(f, "selector not found: {}", selector),
26
28
  YerbaError::NotASequence(path) => write!(f, "not a sequence: {}", path),
27
29
 
30
+ YerbaError::FlowCollectionNotWritable(path) => {
31
+ write!(
32
+ f,
33
+ "cannot write to \"{}\": flow collections are not writable yet. Convert it to block style first, with `collection_style` or `yerba collection-style`",
34
+ path
35
+ )
36
+ }
37
+
28
38
  YerbaError::AmbiguousSelector(selector, count) => {
29
39
  write!(
30
40
  f,
@@ -33,6 +43,10 @@ impl std::fmt::Display for YerbaError {
33
43
  )
34
44
  }
35
45
 
46
+ YerbaError::InvalidCondition(condition, reason) => {
47
+ write!(f, "invalid condition \"{}\": {}", condition, reason)
48
+ }
49
+
36
50
  YerbaError::DuplicateKey {
37
51
  key,
38
52
  first_line,
data/rust/src/ffi.rs CHANGED
@@ -330,6 +330,24 @@ pub unsafe extern "C" fn yerba_document_resolve_selectors(document: *const Docum
330
330
  CString::new(json_string).unwrap_or_default().into_raw()
331
331
  }
332
332
 
333
+ #[no_mangle]
334
+ pub unsafe extern "C" fn yerba_selector_has_wildcard(path: *const c_char) -> bool {
335
+ let selector_string = CStr::from_ptr(path).to_str().unwrap_or("");
336
+
337
+ crate::selector::Selector::parse(selector_string).has_wildcard()
338
+ }
339
+
340
+ #[no_mangle]
341
+ pub unsafe extern "C" fn yerba_document_keys(document: *const Document, path: *const c_char) -> *mut c_char {
342
+ let document = &*document;
343
+ let selector_string = CStr::from_ptr(path).to_str().unwrap_or("");
344
+
345
+ let keys = document.keys_at(selector_string);
346
+ let json_string = serde_json::to_string(&keys).unwrap_or_else(|_| "[]".to_string());
347
+
348
+ CString::new(json_string).unwrap_or_default().into_raw()
349
+ }
350
+
333
351
  #[no_mangle]
334
352
  pub unsafe extern "C" fn yerba_document_get_quote_style(document: *const Document, path: *const c_char) -> *mut c_char {
335
353
  let document = &*document;
@@ -409,12 +427,33 @@ pub unsafe extern "C" fn yerba_document_set_sequence_indent(document: *mut Docum
409
427
  }
410
428
  }
411
429
 
430
+ #[no_mangle]
431
+ pub unsafe extern "C" fn yerba_validate_condition(condition: *const c_char, item_context: bool) -> *mut c_char {
432
+ if condition.is_null() {
433
+ return ptr::null_mut();
434
+ }
435
+
436
+ let cond = CStr::from_ptr(condition).to_str().unwrap_or("");
437
+
438
+ let result = if item_context {
439
+ crate::validate_item_condition(cond)
440
+ } else {
441
+ crate::validate_condition(cond)
442
+ };
443
+
444
+ match result {
445
+ Ok(()) => ptr::null_mut(),
446
+ Err(error) => CString::new(error.to_string()).unwrap_or_default().into_raw(),
447
+ }
448
+ }
449
+
412
450
  #[no_mangle]
413
451
  pub unsafe extern "C" fn yerba_document_evaluate_condition(document: *const Document, parent_path: *const c_char, condition: *const c_char) -> bool {
414
452
  let document = &*document;
415
453
  let parent = CStr::from_ptr(parent_path).to_str().unwrap_or("");
416
454
  let cond = CStr::from_ptr(condition).to_str().unwrap_or("");
417
- document.evaluate_condition(parent, cond)
455
+
456
+ document.evaluate_condition(parent, cond).unwrap_or(false)
418
457
  }
419
458
 
420
459
  #[no_mangle]
@@ -440,7 +479,7 @@ pub unsafe extern "C" fn yerba_document_find(document: *const Document, path: *c
440
479
 
441
480
  let select_string = if select.is_null() { None } else { CStr::from_ptr(select).to_str().ok() };
442
481
 
443
- let results = document.find_items(selector_string, condition_string, select_string);
482
+ let results = document.find_items(selector_string, condition_string, select_string).unwrap_or_default();
444
483
  let json = serde_json::to_string_pretty(&results).unwrap_or_else(|_| "[]".to_string());
445
484
 
446
485
  CString::new(json).unwrap_or_default().into_raw()
@@ -830,12 +869,7 @@ pub unsafe extern "C" fn yerba_get_result_free(result: YerbaGetResult) {
830
869
  }
831
870
  }
832
871
 
833
- #[no_mangle]
834
- pub unsafe extern "C" fn yerba_glob_get(glob_pattern: *const c_char, path: *const c_char) -> YerbaTypedList {
835
- let pattern = CStr::from_ptr(glob_pattern).to_str().unwrap_or("");
836
- let selector_string = CStr::from_ptr(path).to_str().unwrap_or("");
837
- let nodes = crate::glob_get(pattern, selector_string);
838
-
872
+ fn located_nodes_to_list(nodes: &[crate::LocatedNode]) -> YerbaTypedList {
839
873
  let results: Vec<serde_json::Value> = nodes
840
874
  .iter()
841
875
  .map(|node| {
@@ -865,6 +899,18 @@ pub unsafe extern "C" fn yerba_glob_get(glob_pattern: *const c_char, path: *cons
865
899
  value["type"] = serde_json::json!(vt as u8);
866
900
  }
867
901
 
902
+ if let Some(key_name) = &node.key_name {
903
+ value["key_name"] = serde_json::json!(key_name);
904
+ value["key_location"] = serde_json::json!({
905
+ "start_line": node.key_location.start_line,
906
+ "start_column": node.key_location.start_column,
907
+ "end_line": node.key_location.end_line,
908
+ "end_column": node.key_location.end_column,
909
+ "start_offset": node.key_location.start_offset,
910
+ "end_offset": node.key_location.end_offset,
911
+ });
912
+ }
913
+
868
914
  value
869
915
  })
870
916
  .collect();
@@ -878,6 +924,22 @@ pub unsafe extern "C" fn yerba_glob_get(glob_pattern: *const c_char, path: *cons
878
924
  }
879
925
  }
880
926
 
927
+ #[no_mangle]
928
+ pub unsafe extern "C" fn yerba_glob_get(glob_pattern: *const c_char, path: *const c_char) -> YerbaTypedList {
929
+ let pattern = CStr::from_ptr(glob_pattern).to_str().unwrap_or("");
930
+ let selector_string = CStr::from_ptr(path).to_str().unwrap_or("");
931
+
932
+ located_nodes_to_list(&crate::glob_get(pattern, selector_string))
933
+ }
934
+
935
+ #[no_mangle]
936
+ pub unsafe extern "C" fn yerba_document_get_all(document: *const Document, path: *const c_char) -> YerbaTypedList {
937
+ let document = &*document;
938
+ let selector_string = CStr::from_ptr(path).to_str().unwrap_or("");
939
+
940
+ located_nodes_to_list(&document.get_all_located(selector_string))
941
+ }
942
+
881
943
  #[no_mangle]
882
944
  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 {
883
945
  let pattern = CStr::from_ptr(glob_pattern).to_str().unwrap_or("");
@@ -885,7 +947,7 @@ pub unsafe extern "C" fn yerba_glob_find(glob_pattern: *const c_char, path: *con
885
947
  let condition_string = if condition.is_null() { None } else { CStr::from_ptr(condition).to_str().ok() };
886
948
  let select_string = if select.is_null() { None } else { CStr::from_ptr(select).to_str().ok() };
887
949
 
888
- let all_results = crate::glob_find(pattern, selector_string, condition_string, select_string);
950
+ let all_results = crate::glob_find(pattern, selector_string, condition_string, select_string).unwrap_or_default();
889
951
  let length = all_results.len();
890
952
  let json = serde_json::to_string_pretty(&all_results).unwrap_or_else(|_| "[]".to_string());
891
953