yerba 0.3.0-aarch64-linux-gnu → 0.4.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.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +93 -8
  3. data/exe/aarch64-linux-gnu/yerba +0 -0
  4. data/ext/yerba/extconf.rb +22 -3
  5. data/ext/yerba/include/yerba.h +32 -9
  6. data/ext/yerba/yerba.c +133 -25
  7. data/lib/yerba/3.2/yerba.so +0 -0
  8. data/lib/yerba/3.3/yerba.so +0 -0
  9. data/lib/yerba/3.4/yerba.so +0 -0
  10. data/lib/yerba/4.0/yerba.so +0 -0
  11. data/lib/yerba/collection.rb +35 -0
  12. data/lib/yerba/document.rb +45 -3
  13. data/lib/yerba/query_result.rb +50 -0
  14. data/lib/yerba/sequence.rb +187 -1
  15. data/lib/yerba/version.rb +1 -1
  16. data/lib/yerba/yerbafile.rb +45 -0
  17. data/lib/yerba.rb +2 -0
  18. data/rust/Cargo.lock +3 -1
  19. data/rust/Cargo.toml +3 -2
  20. data/rust/cbindgen.toml +1 -0
  21. data/rust/rustfmt.toml +1 -1
  22. data/rust/src/commands/apply.rs +11 -2
  23. data/rust/src/commands/blank_lines.rs +1 -4
  24. data/rust/src/commands/check.rs +11 -2
  25. data/rust/src/commands/directives.rs +61 -0
  26. data/rust/src/commands/get.rs +5 -22
  27. data/rust/src/commands/mod.rs +16 -18
  28. data/rust/src/commands/quote_style.rs +12 -6
  29. data/rust/src/commands/selectors.rs +3 -19
  30. data/rust/src/commands/sort.rs +22 -157
  31. data/rust/src/didyoumean.rs +2 -4
  32. data/rust/src/document/condition.rs +139 -0
  33. data/rust/src/document/delete.rs +91 -0
  34. data/rust/src/document/get.rs +262 -0
  35. data/rust/src/document/insert.rs +384 -0
  36. data/rust/src/document/mod.rs +784 -0
  37. data/rust/src/document/set.rs +100 -0
  38. data/rust/src/document/sort.rs +639 -0
  39. data/rust/src/document/style.rs +473 -0
  40. data/rust/src/error.rs +24 -6
  41. data/rust/src/ffi.rs +272 -518
  42. data/rust/src/json.rs +1 -7
  43. data/rust/src/lib.rs +88 -2
  44. data/rust/src/main.rs +2 -0
  45. data/rust/src/quote_style.rs +83 -7
  46. data/rust/src/selector.rs +2 -7
  47. data/rust/src/syntax.rs +41 -21
  48. data/rust/src/yerbafile.rs +86 -19
  49. metadata +13 -3
  50. data/rust/src/document.rs +0 -2304
data/rust/src/ffi.rs CHANGED
@@ -12,10 +12,8 @@ use std::ffi::{CStr, CString};
12
12
  use std::os::raw::c_char;
13
13
  use std::ptr;
14
14
 
15
- use yaml_parser::SyntaxKind;
16
-
17
- use crate::selector::Selector;
18
15
  use crate::syntax::{detect_yaml_type, YerbaValueType};
16
+ use crate::NodeType;
19
17
  use crate::{Document, InsertPosition, QuoteStyle};
20
18
 
21
19
  #[repr(C)]
@@ -52,15 +50,6 @@ pub struct YerbaTypedList {
52
50
  pub length: usize,
53
51
  }
54
52
 
55
- #[repr(C)]
56
- #[derive(Debug, Clone, Copy, PartialEq)]
57
- pub enum YerbaNodeType {
58
- Scalar = 0,
59
- Map = 1,
60
- Sequence = 2,
61
- NotFound = 3,
62
- }
63
-
64
53
  #[repr(C)]
65
54
  pub struct YerbaParseResult {
66
55
  pub document: *mut Document,
@@ -80,7 +69,7 @@ pub struct YerbaLocation {
80
69
  #[repr(C)]
81
70
  pub struct YerbaGetResult {
82
71
  pub is_list: bool,
83
- pub node_type: YerbaNodeType,
72
+ pub node_type: NodeType,
84
73
  pub single: YerbaTypedValue,
85
74
  pub list: YerbaTypedList,
86
75
  pub location: YerbaLocation,
@@ -89,6 +78,72 @@ pub struct YerbaGetResult {
89
78
  pub error: *mut c_char,
90
79
  }
91
80
 
81
+ impl YerbaGetResult {
82
+ fn empty() -> Self {
83
+ YerbaGetResult {
84
+ is_list: false,
85
+ node_type: NodeType::NotFound,
86
+ single: YerbaTypedValue {
87
+ text: ptr::null_mut(),
88
+ value_type: YerbaValueType::Null,
89
+ },
90
+ list: YerbaTypedList {
91
+ json: ptr::null_mut(),
92
+ length: 0,
93
+ },
94
+ location: EMPTY_LOCATION,
95
+ key_name: ptr::null_mut(),
96
+ key_location: EMPTY_LOCATION,
97
+ error: ptr::null_mut(),
98
+ }
99
+ }
100
+
101
+ fn with_error(error: &str) -> Self {
102
+ let mut result = Self::empty();
103
+
104
+ result.error = CString::new(error).unwrap_or_default().into_raw();
105
+
106
+ result
107
+ }
108
+
109
+ fn with_location(mut self, location: YerbaLocation, key_name: *mut c_char, key_location: YerbaLocation) -> Self {
110
+ self.location = location;
111
+ self.key_name = key_name;
112
+ self.key_location = key_location;
113
+
114
+ self
115
+ }
116
+
117
+ fn scalar(mut self, text: &str, value_type: YerbaValueType) -> Self {
118
+ self.node_type = NodeType::Scalar;
119
+
120
+ self.single = YerbaTypedValue {
121
+ text: CString::new(text).unwrap_or_default().into_raw(),
122
+ value_type,
123
+ };
124
+
125
+ self
126
+ }
127
+
128
+ fn list(mut self, json: &str, length: usize) -> Self {
129
+ self.is_list = true;
130
+ self.node_type = NodeType::Sequence;
131
+
132
+ self.list = YerbaTypedList {
133
+ json: CString::new(json).unwrap_or_default().into_raw(),
134
+ length,
135
+ };
136
+
137
+ self
138
+ }
139
+
140
+ fn with_node_type(mut self, node_type: NodeType) -> Self {
141
+ self.node_type = node_type;
142
+
143
+ self
144
+ }
145
+ }
146
+
92
147
  #[no_mangle]
93
148
  pub unsafe extern "C" fn yerba_document_parse_file(path: *const c_char) -> YerbaParseResult {
94
149
  if path.is_null() {
@@ -98,19 +153,17 @@ pub unsafe extern "C" fn yerba_document_parse_file(path: *const c_char) -> Yerba
98
153
  };
99
154
  }
100
155
 
101
- let path_string = match CStr::from_ptr(path).to_str() {
156
+ let file_path = match CStr::from_ptr(path).to_str() {
102
157
  Ok(string) => string,
103
158
  Err(e) => {
104
159
  return YerbaParseResult {
105
160
  document: ptr::null_mut(),
106
- error: CString::new(format!("Invalid UTF-8 in path: {}", e))
107
- .unwrap_or_default()
108
- .into_raw(),
161
+ error: CString::new(format!("Invalid UTF-8 in path: {}", e)).unwrap_or_default().into_raw(),
109
162
  }
110
163
  }
111
164
  };
112
165
 
113
- match Document::parse_file(path_string) {
166
+ match Document::parse_file(file_path) {
114
167
  Ok(document) => YerbaParseResult {
115
168
  document: Box::into_raw(Box::new(document)),
116
169
  error: ptr::null_mut(),
@@ -137,9 +190,7 @@ pub unsafe extern "C" fn yerba_document_parse(content: *const c_char) -> YerbaPa
137
190
  Err(e) => {
138
191
  return YerbaParseResult {
139
192
  document: ptr::null_mut(),
140
- error: CString::new(format!("Invalid UTF-8: {}", e))
141
- .unwrap_or_default()
142
- .into_raw(),
193
+ error: CString::new(format!("Invalid UTF-8: {}", e)).unwrap_or_default().into_raw(),
143
194
  }
144
195
  }
145
196
  };
@@ -157,28 +208,6 @@ pub unsafe extern "C" fn yerba_document_parse(content: *const c_char) -> YerbaPa
157
208
  }
158
209
  }
159
210
 
160
- fn compute_location(source: &str, start_offset: usize, end_offset: usize) -> YerbaLocation {
161
- let start = start_offset.min(source.len());
162
- let end = end_offset.min(source.len());
163
-
164
- let before_start = &source[..start];
165
- let start_line = before_start.chars().filter(|c| *c == '\n').count() + 1;
166
- let start_column = start - before_start.rfind('\n').map(|p| p + 1).unwrap_or(0);
167
-
168
- let before_end = &source[..end];
169
- let end_line = before_end.chars().filter(|c| *c == '\n').count() + 1;
170
- let end_column = end - before_end.rfind('\n').map(|p| p + 1).unwrap_or(0);
171
-
172
- YerbaLocation {
173
- start_offset: start,
174
- end_offset: end,
175
- start_line,
176
- start_column,
177
- end_line,
178
- end_column,
179
- }
180
- }
181
-
182
211
  const EMPTY_LOCATION: YerbaLocation = YerbaLocation {
183
212
  start_offset: 0,
184
213
  end_offset: 0,
@@ -198,173 +227,71 @@ pub unsafe extern "C" fn yerba_document_free(document: *mut Document) {
198
227
  #[no_mangle]
199
228
  pub unsafe extern "C" fn yerba_document_get(document: *const Document, path: *const c_char) -> YerbaGetResult {
200
229
  let document = &*document;
201
- let path_string = CStr::from_ptr(path).to_str().unwrap_or("");
202
-
203
- let selector = Selector::parse(path_string);
230
+ let selector_string = CStr::from_ptr(path).to_str().unwrap_or("");
204
231
 
205
- if let Err(e) = Document::validate_path(path_string) {
206
- return YerbaGetResult {
207
- is_list: false,
208
- node_type: YerbaNodeType::NotFound,
209
- single: YerbaTypedValue {
210
- text: ptr::null_mut(),
211
- value_type: YerbaValueType::Null,
212
- },
213
- list: YerbaTypedList {
214
- json: ptr::null_mut(),
215
- length: 0,
216
- },
217
- key_name: ptr::null_mut(),
218
- key_location: EMPTY_LOCATION,
219
- location: EMPTY_LOCATION,
220
- error: CString::new(e.to_string()).unwrap_or_default().into_raw(),
221
- };
232
+ if let Err(e) = Document::validate_path(selector_string) {
233
+ return YerbaGetResult::with_error(&e.to_string());
222
234
  }
223
235
 
224
- let source = document.to_string();
225
-
226
- let (location, key_text, key_location) = match document.navigate(path_string) {
227
- Ok(node) => {
228
- let range = node.text_range();
229
- let location = compute_location(&source, range.start().into(), range.end().into());
230
-
231
- let (key_text, key_location) = node
232
- .parent()
233
- .and_then(|parent| {
234
- use rowan::ast::AstNode;
235
- use yaml_parser::ast::BlockMapEntry;
236
-
237
- BlockMapEntry::cast(parent).and_then(|entry| {
238
- entry.key().and_then(|key_node| {
239
- let key_text = crate::syntax::extract_scalar_text(key_node.syntax())?;
240
- let key_range = key_node.syntax().text_range();
241
- let key_location = compute_location(&source, key_range.start().into(), key_range.end().into());
242
-
243
- Some((key_text, key_location))
244
- })
245
- })
246
- })
247
- .map(|(name, location)| (Some(name), location))
248
- .unwrap_or((None, EMPTY_LOCATION));
236
+ let info = document.get_node_info(selector_string);
237
+ let location = location_to_ffi(info.location);
238
+ let key_location = location_to_ffi(info.key_location);
249
239
 
250
- (location, key_text, key_location)
251
- }
252
- Err(_) => (EMPTY_LOCATION, None, EMPTY_LOCATION),
253
- };
254
-
255
- let key_name_pointer = key_text
240
+ let key_name = info
241
+ .key_name
256
242
  .map(|name| CString::new(name).unwrap_or_default().into_raw())
257
243
  .unwrap_or(ptr::null_mut());
258
244
 
259
- if selector.has_wildcard() {
260
- let values = document.get_all_typed(path_string);
245
+ let base = YerbaGetResult::empty().with_location(location, key_name, key_location);
261
246
 
262
- let typed: Vec<serde_json::Value> = values
247
+ if info.is_list {
248
+ let typed: Vec<serde_json::Value> = info
249
+ .list_values
263
250
  .iter()
264
- .map(|serde_value| {
251
+ .map(|value| {
265
252
  serde_json::json!({
266
- "text": serde_value.text,
267
- "type": detect_yaml_type(serde_value) as u8
253
+ "text": value.text,
254
+ "type": detect_yaml_type(value) as u8
268
255
  })
269
256
  })
270
257
  .collect();
271
258
 
272
259
  let json = serde_json::to_string(&typed).unwrap_or_else(|_| "[]".to_string());
273
- let length = values.len();
274
260
 
275
- YerbaGetResult {
276
- is_list: true,
277
- node_type: YerbaNodeType::Sequence,
278
- single: YerbaTypedValue {
279
- text: ptr::null_mut(),
280
- value_type: YerbaValueType::Null,
281
- },
282
- list: YerbaTypedList {
283
- json: CString::new(json).unwrap_or_default().into_raw(),
284
- length,
285
- },
286
- location,
287
- key_name: key_name_pointer,
288
- key_location,
289
- error: ptr::null_mut(),
290
- }
261
+ base.list(&json, info.list_values.len())
291
262
  } else {
292
- match document.get_typed(path_string) {
293
- Some(scalar) => {
294
- let vtype = detect_yaml_type(&scalar);
295
-
296
- YerbaGetResult {
297
- is_list: false,
298
- node_type: YerbaNodeType::Scalar,
299
- single: YerbaTypedValue {
300
- text: CString::new(scalar.text).unwrap_or_default().into_raw(),
301
- value_type: vtype,
302
- },
303
- list: YerbaTypedList {
304
- json: ptr::null_mut(),
305
- length: 0,
306
- },
307
- location,
308
- key_name: key_name_pointer,
309
- key_location,
310
- error: ptr::null_mut(),
311
- }
312
- }
313
-
314
- None => {
315
- use rowan::ast::AstNode;
316
- use yaml_parser::ast::{BlockMap, BlockSeq};
317
-
318
- let node_type = match document.navigate(path_string) {
319
- Ok(node) => {
320
- if node.children().any(|child| BlockSeq::can_cast(child.kind())) {
321
- YerbaNodeType::Sequence
322
- } else if node.children().any(|child| BlockMap::can_cast(child.kind())) {
323
- YerbaNodeType::Map
324
- } else if node.descendants().any(|child| BlockSeq::can_cast(child.kind())) {
325
- YerbaNodeType::Sequence
326
- } else if node.descendants().any(|child| BlockMap::can_cast(child.kind())) {
327
- YerbaNodeType::Map
328
- } else {
329
- YerbaNodeType::NotFound
330
- }
331
- }
332
- Err(_) => YerbaNodeType::NotFound,
333
- };
334
-
335
- YerbaGetResult {
336
- is_list: false,
337
- node_type,
338
- single: YerbaTypedValue {
339
- text: ptr::null_mut(),
340
- value_type: YerbaValueType::Null,
341
- },
342
- list: YerbaTypedList {
343
- json: ptr::null_mut(),
344
- length: 0,
345
- },
346
- location,
347
- key_name: key_name_pointer,
348
- key_location,
349
- error: ptr::null_mut(),
350
- }
351
- }
263
+ match info.value {
264
+ Some(scalar) => base.scalar(&scalar.text, detect_yaml_type(&scalar)),
265
+ None => base.with_node_type(info.node_type),
352
266
  }
353
267
  }
354
268
  }
355
269
 
270
+ fn location_to_ffi(location: crate::Location) -> YerbaLocation {
271
+ YerbaLocation {
272
+ start_offset: location.start_offset,
273
+ end_offset: location.end_offset,
274
+ start_line: location.start_line,
275
+ start_column: location.start_column,
276
+ end_line: location.end_line,
277
+ end_column: location.end_column,
278
+ }
279
+ }
280
+
356
281
  /// Caller must free with yerba_string_free.
357
282
  #[no_mangle]
358
283
  pub unsafe extern "C" fn yerba_document_get_value(document: *const Document, path: *const c_char) -> *mut c_char {
359
284
  let document = &*document;
360
- let path_string = CStr::from_ptr(path).to_str().unwrap_or("");
285
+ let selector_string = CStr::from_ptr(path).to_str().unwrap_or("");
361
286
 
362
- match document.get_value(path_string) {
287
+ match document.get_value(selector_string) {
363
288
  Some(value) => {
364
289
  let json = crate::json::yaml_to_json(&value);
365
290
  let json_string = serde_json::to_string(&json).unwrap_or_else(|_| "null".to_string());
291
+
366
292
  CString::new(json_string).unwrap_or_default().into_raw()
367
293
  }
294
+
368
295
  None => ptr::null_mut(),
369
296
  }
370
297
  }
@@ -373,9 +300,9 @@ pub unsafe extern "C" fn yerba_document_get_value(document: *const Document, pat
373
300
  #[no_mangle]
374
301
  pub unsafe extern "C" fn yerba_document_get_values(document: *const Document, path: *const c_char) -> *mut c_char {
375
302
  let document = &*document;
376
- let path_string = CStr::from_ptr(path).to_str().unwrap_or("");
303
+ let selector_string = CStr::from_ptr(path).to_str().unwrap_or("");
377
304
 
378
- let values = document.get_values(path_string);
305
+ let values = document.get_values(selector_string);
379
306
  let json_values: Vec<serde_json::Value> = values.iter().map(crate::json::yaml_to_json).collect();
380
307
  let json_string = serde_json::to_string(&json_values).unwrap_or_else(|_| "[]".to_string());
381
308
 
@@ -383,50 +310,40 @@ pub unsafe extern "C" fn yerba_document_get_values(document: *const Document, pa
383
310
  }
384
311
 
385
312
  #[no_mangle]
386
- pub unsafe extern "C" fn yerba_document_get_quote_style(document: *const Document, path: *const c_char) -> i32 {
313
+ pub unsafe extern "C" fn yerba_document_get_quote_style(document: *const Document, path: *const c_char) -> *mut c_char {
387
314
  let document = &*document;
388
- let path_string = CStr::from_ptr(path).to_str().unwrap_or("");
389
-
390
- match document.get_typed(path_string) {
391
- Some(scalar) => match scalar.kind {
392
- SyntaxKind::PLAIN_SCALAR => 0,
393
- SyntaxKind::SINGLE_QUOTED_SCALAR => 1,
394
- SyntaxKind::DOUBLE_QUOTED_SCALAR => 2,
395
- _ => -1,
396
- },
315
+ let selector_string = CStr::from_ptr(path).to_str().unwrap_or("");
397
316
 
398
- None => -1,
317
+ match document.get_quote_style(selector_string) {
318
+ Some(style) => {
319
+ let ruby_style = style.replace('-', "_");
320
+
321
+ CString::new(ruby_style).map(|s| s.into_raw()).unwrap_or(std::ptr::null_mut())
322
+ }
323
+
324
+ None => std::ptr::null_mut(),
399
325
  }
400
326
  }
401
327
 
402
328
  #[no_mangle]
403
- pub unsafe extern "C" fn yerba_document_set_quote_style(
404
- document: *mut Document,
405
- path: *const c_char,
406
- style: i32,
407
- ) -> YerbaResult {
329
+ pub unsafe extern "C" fn yerba_document_set_quote_style(document: *mut Document, path: *const c_char, style: *const c_char) -> YerbaResult {
408
330
  let document = &mut *document;
409
- let path_string = CStr::from_ptr(path).to_str().unwrap_or("");
331
+ let selector_string = CStr::from_ptr(path).to_str().unwrap_or("");
332
+ let style_string = &CStr::from_ptr(style).to_str().unwrap_or("").replace('_', "-");
410
333
 
411
- let quote_style = match style {
412
- 0 => QuoteStyle::Plain,
413
- 1 => QuoteStyle::Single,
414
- 2 => QuoteStyle::Double,
415
- _ => return YerbaResult::err("Invalid quote style (use 0=plain, 1=single, 2=double)"),
334
+ let quote_style = match style_string.parse::<QuoteStyle>() {
335
+ Ok(style) => style,
336
+ Err(e) => return YerbaResult::err(&e),
416
337
  };
417
338
 
418
- match document.set_scalar_style(path_string, &quote_style) {
419
- Ok(()) => YerbaResult::ok(),
339
+ match document.enforce_quotes_at(&quote_style, Some(selector_string)) {
340
+ Ok(_warnings) => YerbaResult::ok(),
420
341
  Err(e) => YerbaResult::err(&e.to_string()),
421
342
  }
422
343
  }
423
344
 
424
345
  #[no_mangle]
425
- pub unsafe extern "C" fn yerba_document_evaluate_condition(
426
- document: *const Document,
427
- parent_path: *const c_char,
428
- condition: *const c_char,
429
- ) -> bool {
346
+ pub unsafe extern "C" fn yerba_document_evaluate_condition(document: *const Document, parent_path: *const c_char, condition: *const c_char) -> bool {
430
347
  let document = &*document;
431
348
  let parent = CStr::from_ptr(parent_path).to_str().unwrap_or("");
432
349
  let cond = CStr::from_ptr(condition).to_str().unwrap_or("");
@@ -436,62 +353,22 @@ pub unsafe extern "C" fn yerba_document_evaluate_condition(
436
353
  #[no_mangle]
437
354
  pub unsafe extern "C" fn yerba_document_exists(document: *const Document, path: *const c_char) -> bool {
438
355
  let document = &*document;
439
- let path_string = CStr::from_ptr(path).to_str().unwrap_or("");
440
- document.exists(path_string)
356
+ let selector_string = CStr::from_ptr(path).to_str().unwrap_or("");
357
+ document.exists(selector_string)
441
358
  }
442
359
 
443
360
  #[no_mangle]
444
- pub unsafe extern "C" fn yerba_document_find(
445
- document: *const Document,
446
- path: *const c_char,
447
- condition: *const c_char,
448
- select: *const c_char,
449
- ) -> *mut c_char {
361
+ pub unsafe extern "C" fn yerba_document_find(document: *const Document, path: *const c_char, condition: *const c_char, select: *const c_char) -> *mut c_char {
450
362
  let document = &*document;
451
- let path_string = CStr::from_ptr(path).to_str().unwrap_or("");
452
-
453
- let condition_str = if condition.is_null() {
454
- None
455
- } else {
456
- CStr::from_ptr(condition).to_str().ok()
457
- };
458
-
459
- let _select_string = if select.is_null() {
460
- None
461
- } else {
462
- CStr::from_ptr(select).to_str().ok()
463
- };
464
-
465
- let values = match condition_str {
466
- Some(cond) => document.filter(path_string, cond),
467
- None => document.get_values(path_string),
468
- };
469
-
470
- let select_fields: Option<Vec<&str>> = _select_string.map(|s| s.split(',').collect());
363
+ let selector_string = CStr::from_ptr(path).to_str().unwrap_or("");
471
364
 
472
- let mut results: Vec<serde_json::Value> = Vec::new();
365
+ let condition_string = if condition.is_null() { None } else { CStr::from_ptr(condition).to_str().ok() };
473
366
 
474
- for value in &values {
475
- match &select_fields {
476
- Some(fields) => {
477
- let mut result = serde_json::Map::new();
478
-
479
- for field in fields {
480
- let json_value = crate::json::resolve_select_field(value, field);
481
- let json_key = crate::json::select_field_key(field);
482
-
483
- result.insert(json_key, json_value);
484
- }
485
-
486
- results.push(serde_json::Value::Object(result));
487
- }
488
- None => {
489
- results.push(crate::json::yaml_to_json(value));
490
- }
491
- }
492
- }
367
+ let select_string = if select.is_null() { None } else { CStr::from_ptr(select).to_str().ok() };
493
368
 
369
+ let results = document.find_items(selector_string, condition_string, select_string);
494
370
  let json = serde_json::to_string_pretty(&results).unwrap_or_else(|_| "[]".to_string());
371
+
495
372
  CString::new(json).unwrap_or_default().into_raw()
496
373
  }
497
374
 
@@ -504,15 +381,15 @@ pub unsafe extern "C" fn yerba_document_set(
504
381
  all: bool,
505
382
  ) -> YerbaResult {
506
383
  let document = &mut *document;
507
- let path_string = CStr::from_ptr(path).to_str().unwrap_or("");
384
+ let selector_string = CStr::from_ptr(path).to_str().unwrap_or("");
508
385
  let value_string = CStr::from_ptr(value).to_str().unwrap_or("");
509
386
 
510
387
  let result = if all {
511
- document.set_all(path_string, value_string)
388
+ document.set_all(selector_string, value_string)
512
389
  } else {
513
390
  match value_type {
514
- YerbaValueType::String => document.set(path_string, value_string),
515
- _ => document.set_plain(path_string, value_string),
391
+ YerbaValueType::String => document.set(selector_string, value_string),
392
+ _ => document.set_plain(selector_string, value_string),
516
393
  }
517
394
  };
518
395
 
@@ -532,7 +409,7 @@ pub unsafe extern "C" fn yerba_document_insert(
532
409
  at: i64,
533
410
  ) -> YerbaResult {
534
411
  let document = &mut *document;
535
- let path_string = CStr::from_ptr(path).to_str().unwrap_or("");
412
+ let selector_string = CStr::from_ptr(path).to_str().unwrap_or("");
536
413
  let value_string = CStr::from_ptr(value).to_str().unwrap_or("");
537
414
 
538
415
  let position = if at >= 0 {
@@ -547,7 +424,7 @@ pub unsafe extern "C" fn yerba_document_insert(
547
424
  InsertPosition::Last
548
425
  };
549
426
 
550
- match document.insert_into(path_string, value_string, position) {
427
+ match document.insert_into(selector_string, value_string, position) {
551
428
  Ok(()) => YerbaResult::ok(),
552
429
  Err(e) => YerbaResult::err(&e.to_string()),
553
430
  }
@@ -563,60 +440,14 @@ pub unsafe extern "C" fn yerba_document_insert_object(
563
440
  at: i64,
564
441
  ) -> YerbaResult {
565
442
  let document = &mut *document;
566
- let path_string = CStr::from_ptr(path).to_str().unwrap_or("");
443
+ let selector_string = CStr::from_ptr(path).to_str().unwrap_or("");
567
444
  let json_string = CStr::from_ptr(json).to_str().unwrap_or("");
568
445
 
569
446
  let json_value: serde_json::Value = match serde_json::from_str(json_string) {
570
- Ok(v) => v,
447
+ Ok(value) => value,
571
448
  Err(e) => return YerbaResult::err(&format!("Invalid JSON: {}", e)),
572
449
  };
573
450
 
574
- let try_paths = if path_string.is_empty() {
575
- vec!["[]".to_string(), "[0]".to_string()]
576
- } else {
577
- vec![format!("{}[]", path_string), format!("{}[0]", path_string)]
578
- };
579
-
580
- let mut quote_style = QuoteStyle::Plain;
581
-
582
- 'outer: for try_path in &try_paths {
583
- for scalar in document.get_all_typed(try_path) {
584
- if scalar.kind == SyntaxKind::DOUBLE_QUOTED_SCALAR {
585
- quote_style = QuoteStyle::Double;
586
- break 'outer;
587
- } else if scalar.kind == SyntaxKind::SINGLE_QUOTED_SCALAR {
588
- quote_style = QuoteStyle::Single;
589
- break 'outer;
590
- }
591
- }
592
- }
593
-
594
- if quote_style == QuoteStyle::Plain {
595
- if let Some(serde_yaml::Value::Sequence(seq)) = document.get_value(path_string).as_ref() {
596
- if let Some(serde_yaml::Value::Mapping(map)) = seq.first() {
597
- if let Some((serde_yaml::Value::String(key_name), _)) = map.iter().next() {
598
- let deep_path = if path_string.is_empty() {
599
- format!("[].{}", key_name)
600
- } else {
601
- format!("{}[].{}", path_string, key_name)
602
- };
603
-
604
- for scalar in document.get_all_typed(&deep_path) {
605
- if scalar.kind == SyntaxKind::DOUBLE_QUOTED_SCALAR {
606
- quote_style = QuoteStyle::Double;
607
- break;
608
- } else if scalar.kind == SyntaxKind::SINGLE_QUOTED_SCALAR {
609
- quote_style = QuoteStyle::Single;
610
- break;
611
- }
612
- }
613
- }
614
- }
615
- }
616
- }
617
-
618
- let yaml_text = crate::yaml_writer::json_to_yaml_text(&json_value, &quote_style, 0);
619
-
620
451
  let position = if at >= 0 {
621
452
  InsertPosition::At(at as usize)
622
453
  } else if !before.is_null() {
@@ -629,7 +460,7 @@ pub unsafe extern "C" fn yerba_document_insert_object(
629
460
  InsertPosition::Last
630
461
  };
631
462
 
632
- match document.insert_into(path_string, &yaml_text, position) {
463
+ match document.insert_object(selector_string, &json_value, position) {
633
464
  Ok(()) => YerbaResult::ok(),
634
465
  Err(e) => YerbaResult::err(&e.to_string()),
635
466
  }
@@ -638,51 +469,67 @@ pub unsafe extern "C" fn yerba_document_insert_object(
638
469
  #[no_mangle]
639
470
  pub unsafe extern "C" fn yerba_document_delete(document: *mut Document, path: *const c_char) -> YerbaResult {
640
471
  let document = &mut *document;
641
- let path_string = CStr::from_ptr(path).to_str().unwrap_or("");
472
+ let selector_string = CStr::from_ptr(path).to_str().unwrap_or("");
642
473
 
643
- match document.delete(path_string) {
474
+ match document.delete(selector_string) {
644
475
  Ok(()) => YerbaResult::ok(),
645
476
  Err(e) => YerbaResult::err(&e.to_string()),
646
477
  }
647
478
  }
648
479
 
649
480
  #[no_mangle]
650
- pub unsafe extern "C" fn yerba_document_remove(
651
- document: *mut Document,
652
- path: *const c_char,
653
- value: *const c_char,
654
- ) -> YerbaResult {
481
+ pub unsafe extern "C" fn yerba_document_insert_objects(document: *mut Document, path: *const c_char, json: *const c_char) -> YerbaResult {
655
482
  let document = &mut *document;
656
- let path_string = CStr::from_ptr(path).to_str().unwrap_or("");
483
+ let selector_string = CStr::from_ptr(path).to_str().unwrap_or("");
484
+ let json_string = CStr::from_ptr(json).to_str().unwrap_or("");
485
+
486
+ let json_values: Vec<serde_json::Value> = match serde_json::from_str(json_string) {
487
+ Ok(values) => values,
488
+ Err(e) => return YerbaResult::err(&format!("Invalid JSON: {}", e)),
489
+ };
490
+
491
+ match document.insert_objects(selector_string, &json_values) {
492
+ Ok(()) => YerbaResult::ok(),
493
+ Err(e) => YerbaResult::err(&e.to_string()),
494
+ }
495
+ }
496
+
497
+ #[no_mangle]
498
+ pub unsafe extern "C" fn yerba_document_remove(document: *mut Document, path: *const c_char, value: *const c_char) -> YerbaResult {
499
+ let document = &mut *document;
500
+ let selector_string = CStr::from_ptr(path).to_str().unwrap_or("");
657
501
  let value_string = CStr::from_ptr(value).to_str().unwrap_or("");
658
502
 
659
- match document.remove(path_string, value_string) {
503
+ match document.remove(selector_string, value_string) {
660
504
  Ok(()) => YerbaResult::ok(),
661
505
  Err(e) => YerbaResult::err(&e.to_string()),
662
506
  }
663
507
  }
664
508
 
665
509
  #[no_mangle]
666
- pub unsafe extern "C" fn yerba_document_remove_at(
667
- document: *mut Document,
668
- path: *const c_char,
669
- index: usize,
670
- ) -> YerbaResult {
510
+ pub unsafe extern "C" fn yerba_document_remove_at(document: *mut Document, path: *const c_char, index: usize) -> YerbaResult {
671
511
  let document = &mut *document;
672
- let path_string = CStr::from_ptr(path).to_str().unwrap_or("");
512
+ let selector_string = CStr::from_ptr(path).to_str().unwrap_or("");
673
513
 
674
- match document.remove_at(path_string, index) {
514
+ match document.remove_at(selector_string, index) {
675
515
  Ok(()) => YerbaResult::ok(),
676
516
  Err(e) => YerbaResult::err(&e.to_string()),
677
517
  }
678
518
  }
679
519
 
680
520
  #[no_mangle]
681
- pub unsafe extern "C" fn yerba_document_rename(
682
- document: *mut Document,
683
- source: *const c_char,
684
- dest: *const c_char,
685
- ) -> YerbaResult {
521
+ pub unsafe extern "C" fn yerba_document_move_item(document: *mut Document, path: *const c_char, from: usize, to: usize) -> YerbaResult {
522
+ let document = &mut *document;
523
+ let selector_string = CStr::from_ptr(path).to_str().unwrap_or("");
524
+
525
+ match document.move_item(selector_string, from, to) {
526
+ Ok(()) => YerbaResult::ok(),
527
+ Err(e) => YerbaResult::err(&e.to_string()),
528
+ }
529
+ }
530
+
531
+ #[no_mangle]
532
+ pub unsafe extern "C" fn yerba_document_rename(document: *mut Document, source: *const c_char, dest: *const c_char) -> YerbaResult {
686
533
  let document = &mut *document;
687
534
  let source_string = CStr::from_ptr(source).to_str().unwrap_or("");
688
535
  let dest_string = CStr::from_ptr(dest).to_str().unwrap_or("");
@@ -694,59 +541,46 @@ pub unsafe extern "C" fn yerba_document_rename(
694
541
  }
695
542
 
696
543
  #[no_mangle]
697
- pub unsafe extern "C" fn yerba_document_sort(
698
- document: *mut Document,
699
- path: *const c_char,
700
- by: *const c_char,
701
- case_sensitive: bool,
702
- ) -> YerbaResult {
544
+ pub unsafe extern "C" fn yerba_document_sort(document: *mut Document, path: *const c_char, by: *const c_char, case_sensitive: bool) -> YerbaResult {
703
545
  let document = &mut *document;
704
- let path_string = CStr::from_ptr(path).to_str().unwrap_or("");
546
+ let selector_string = CStr::from_ptr(path).to_str().unwrap_or("");
705
547
 
706
- let by_string = if by.is_null() {
707
- None
708
- } else {
709
- CStr::from_ptr(by).to_str().ok()
710
- };
548
+ let by_string = if by.is_null() { None } else { CStr::from_ptr(by).to_str().ok() };
711
549
 
712
550
  let sort_fields: Vec<crate::SortField> = match by_string {
713
- Some(fields) => fields
714
- .split(',')
715
- .map(|field| {
716
- if let Some(name) = field.strip_suffix(":desc") {
717
- crate::SortField {
718
- path: name.to_string(),
719
- ascending: false,
720
- }
721
- } else {
722
- crate::SortField {
723
- path: field.to_string(),
724
- ascending: true,
725
- }
726
- }
727
- })
728
- .collect(),
551
+ Some(fields) => crate::SortField::parse_list(fields),
729
552
  None => vec![],
730
553
  };
731
554
 
732
- match document.sort_items(path_string, &sort_fields, case_sensitive) {
555
+ match document.sort_items(selector_string, &sort_fields, case_sensitive) {
733
556
  Ok(()) => YerbaResult::ok(),
734
557
  Err(e) => YerbaResult::err(&e.to_string()),
735
558
  }
736
559
  }
737
560
 
738
561
  #[no_mangle]
739
- pub unsafe extern "C" fn yerba_document_sort_keys(
740
- document: *mut Document,
741
- path: *const c_char,
742
- order: *const c_char,
743
- ) -> YerbaResult {
562
+ pub unsafe extern "C" fn yerba_document_reorder(document: *mut Document, path: *const c_char, by: *const c_char, order_csv: *const c_char) -> YerbaResult {
744
563
  let document = &mut *document;
745
- let path_string = CStr::from_ptr(path).to_str().unwrap_or("");
564
+ let selector_string = CStr::from_ptr(path).to_str().unwrap_or("");
565
+ let by_string = CStr::from_ptr(by).to_str().unwrap_or("");
566
+ let order_string = CStr::from_ptr(order_csv).to_str().unwrap_or("");
567
+
568
+ let desired_order: Vec<&str> = order_string.split(',').map(|s| s.trim()).collect();
569
+
570
+ match document.reorder_items(selector_string, by_string, &desired_order) {
571
+ Ok(()) => YerbaResult::ok(),
572
+ Err(e) => YerbaResult::err(&e.to_string()),
573
+ }
574
+ }
575
+
576
+ #[no_mangle]
577
+ pub unsafe extern "C" fn yerba_document_sort_keys(document: *mut Document, path: *const c_char, order: *const c_char) -> YerbaResult {
578
+ let document = &mut *document;
579
+ let selector_string = CStr::from_ptr(path).to_str().unwrap_or("");
746
580
  let order_string = CStr::from_ptr(order).to_str().unwrap_or("");
747
581
  let key_order: Vec<&str> = order_string.split(',').collect();
748
582
 
749
- match document.sort_keys(path_string, &key_order) {
583
+ match document.sort_keys(selector_string, &key_order) {
750
584
  Ok(()) => YerbaResult::ok(),
751
585
  Err(e) => YerbaResult::err(&e.to_string()),
752
586
  }
@@ -761,56 +595,82 @@ pub unsafe extern "C" fn yerba_document_quote_style(
761
595
  ) -> YerbaResult {
762
596
  let document = &mut *document;
763
597
 
764
- let path_string = if path.is_null() {
765
- None
766
- } else {
767
- CStr::from_ptr(path).to_str().ok()
768
- };
598
+ let selector = if path.is_null() { None } else { CStr::from_ptr(path).to_str().ok() };
769
599
 
770
- let key_quote_style = if key_style.is_null() {
600
+ let key = if key_style.is_null() {
771
601
  None
772
602
  } else {
773
- CStr::from_ptr(key_style)
774
- .to_str()
775
- .ok()
776
- .and_then(|s| s.parse::<QuoteStyle>().ok())
603
+ CStr::from_ptr(key_style).to_str().ok().and_then(|s| s.parse::<crate::KeyStyle>().ok())
777
604
  };
778
605
 
779
- let value_quote_style = if value_style.is_null() {
606
+ let value = if value_style.is_null() {
780
607
  None
781
608
  } else {
782
- CStr::from_ptr(value_style)
783
- .to_str()
784
- .ok()
785
- .and_then(|s| s.parse::<QuoteStyle>().ok())
609
+ CStr::from_ptr(value_style).to_str().ok().and_then(|s| s.parse::<QuoteStyle>().ok())
786
610
  };
787
611
 
788
- if let Some(ref key_style) = key_quote_style {
789
- if let Err(e) = document.enforce_key_style(key_style, path_string) {
790
- return YerbaResult::err(&e.to_string());
791
- }
612
+ match document.enforce_quote_style(key.as_ref(), value.as_ref(), selector) {
613
+ Ok(()) => YerbaResult::ok(),
614
+ Err(e) => YerbaResult::err(&e.to_string()),
792
615
  }
616
+ }
793
617
 
794
- if let Some(ref value_style) = value_quote_style {
795
- if let Err(e) = document.enforce_quotes_at(value_style, path_string) {
796
- return YerbaResult::err(&e.to_string());
797
- }
618
+ #[no_mangle]
619
+ pub unsafe extern "C" fn yerba_document_blank_lines(document: *mut Document, path: *const c_char, count: usize) -> YerbaResult {
620
+ let document = &mut *document;
621
+ let selector_string = CStr::from_ptr(path).to_str().unwrap_or("");
622
+
623
+ match document.enforce_blank_lines(selector_string, count) {
624
+ Ok(()) => YerbaResult::ok(),
625
+ Err(e) => YerbaResult::err(&e.to_string()),
798
626
  }
627
+ }
628
+
629
+ /// Caller must free with yerba_string_free.
630
+ #[no_mangle]
631
+ pub unsafe extern "C" fn yerba_yerbafile_find(directory: *const c_char) -> *mut c_char {
632
+ let start = if directory.is_null() {
633
+ std::env::current_dir().ok()
634
+ } else {
635
+ CStr::from_ptr(directory).to_str().ok().map(std::path::PathBuf::from)
636
+ };
637
+
638
+ let path = match start {
639
+ Some(dir) => crate::Yerbafile::find_from(dir),
640
+ None => None,
641
+ };
799
642
 
800
- YerbaResult::ok()
643
+ match path {
644
+ Some(path) => CString::new(path.to_string_lossy().to_string()).unwrap_or_default().into_raw(),
645
+ None => ptr::null_mut(),
646
+ }
801
647
  }
802
648
 
803
649
  #[no_mangle]
804
- pub unsafe extern "C" fn yerba_document_blank_lines(
805
- document: *mut Document,
806
- path: *const c_char,
807
- count: usize,
808
- ) -> YerbaResult {
650
+ pub unsafe extern "C" fn yerba_document_apply_yerbafile(document: *mut Document, file_path: *const c_char, yerbafile_path: *const c_char) -> YerbaResult {
809
651
  let document = &mut *document;
810
- let path_string = CStr::from_ptr(path).to_str().unwrap_or("");
652
+ let file_path_string = CStr::from_ptr(file_path).to_str().unwrap_or("");
811
653
 
812
- match document.enforce_blank_lines(path_string, count) {
813
- Ok(()) => YerbaResult::ok(),
654
+ let yerbafile = if yerbafile_path.is_null() {
655
+ match crate::Yerbafile::find() {
656
+ Some(path) => match crate::Yerbafile::load(&path) {
657
+ Ok(yerbafile) => yerbafile,
658
+ Err(e) => return YerbaResult::err(&e.to_string()),
659
+ },
660
+
661
+ None => return YerbaResult::err("No Yerbafile found"),
662
+ }
663
+ } else {
664
+ let path = CStr::from_ptr(yerbafile_path).to_str().unwrap_or("");
665
+
666
+ match crate::Yerbafile::load(path) {
667
+ Ok(yerbafile) => yerbafile,
668
+ Err(e) => return YerbaResult::err(&e.to_string()),
669
+ }
670
+ };
671
+
672
+ match yerbafile.apply_to_document(document, file_path_string) {
673
+ Ok(_) => YerbaResult::ok(),
814
674
  Err(e) => YerbaResult::err(&e.to_string()),
815
675
  }
816
676
  }
@@ -855,42 +715,12 @@ pub unsafe extern "C" fn yerba_get_result_free(result: YerbaGetResult) {
855
715
  #[no_mangle]
856
716
  pub unsafe extern "C" fn yerba_glob_get(glob_pattern: *const c_char, path: *const c_char) -> YerbaTypedList {
857
717
  let pattern = CStr::from_ptr(glob_pattern).to_str().unwrap_or("");
858
- let path_string = CStr::from_ptr(path).to_str().unwrap_or("");
859
- let selector = Selector::parse(path_string);
860
-
861
- let files = match glob::glob(pattern) {
862
- Ok(paths) => paths.filter_map(|p| p.ok()).collect::<Vec<_>>(),
863
- Err(_) => {
864
- return YerbaTypedList {
865
- json: CString::new("[]").unwrap_or_default().into_raw(),
866
- length: 0,
867
- }
868
- }
869
- };
870
-
871
- use rayon::prelude::*;
872
-
873
- let results: Vec<serde_json::Value> = files
874
- .par_iter()
875
- .flat_map(|file| {
876
- let mut file_results = Vec::new();
718
+ let selector_string = CStr::from_ptr(path).to_str().unwrap_or("");
719
+ let scalars = crate::glob_get(pattern, selector_string);
877
720
 
878
- if let Ok(document) = Document::parse_file(file) {
879
- if selector.has_wildcard() {
880
- for scalar in document.get_all_typed(path_string) {
881
- let value_type = detect_yaml_type(&scalar);
882
-
883
- file_results.push(serde_json::json!({"text": scalar.text, "type": value_type as u8}));
884
- }
885
- } else if let Some(scalar) = document.get_typed(path_string) {
886
- let value_type = detect_yaml_type(&scalar);
887
-
888
- file_results.push(serde_json::json!({"text": scalar.text, "type": value_type as u8}));
889
- }
890
- }
891
-
892
- file_results
893
- })
721
+ let results: Vec<serde_json::Value> = scalars
722
+ .iter()
723
+ .map(|scalar| serde_json::json!({"text": scalar.text, "type": detect_yaml_type(scalar) as u8}))
894
724
  .collect();
895
725
 
896
726
  let length = results.len();
@@ -903,89 +733,13 @@ pub unsafe extern "C" fn yerba_glob_get(glob_pattern: *const c_char, path: *cons
903
733
  }
904
734
 
905
735
  #[no_mangle]
906
- pub unsafe extern "C" fn yerba_glob_find(
907
- glob_pattern: *const c_char,
908
- path: *const c_char,
909
- condition: *const c_char,
910
- select: *const c_char,
911
- ) -> YerbaTypedList {
736
+ 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 {
912
737
  let pattern = CStr::from_ptr(glob_pattern).to_str().unwrap_or("");
913
- let path_string = CStr::from_ptr(path).to_str().unwrap_or("");
914
-
915
- let condition_string = if condition.is_null() {
916
- None
917
- } else {
918
- CStr::from_ptr(condition).to_str().ok()
919
- };
920
-
921
- let _select_string = if select.is_null() {
922
- None
923
- } else {
924
- CStr::from_ptr(select).to_str().ok()
925
- };
926
-
927
- let files = match glob::glob(pattern) {
928
- Ok(paths) => paths.filter_map(|p| p.ok()).collect::<Vec<_>>(),
929
- Err(_) => {
930
- return YerbaTypedList {
931
- json: CString::new("[]").unwrap_or_default().into_raw(),
932
- length: 0,
933
- }
934
- }
935
- };
936
-
937
- use rayon::prelude::*;
938
-
939
- let select_fields: Option<Vec<&str>> = _select_string.map(|s| s.split(',').collect());
940
-
941
- let all_results: Vec<serde_json::Value> = files
942
- .par_iter()
943
- .flat_map(|file| {
944
- let mut file_results = Vec::new();
945
-
946
- if let Ok(document) = Document::parse_file(file) {
947
- let values = match condition_string {
948
- Some(cond) => document.filter(path_string, cond),
949
- None => document.get_values(path_string),
950
- };
951
-
952
- let file_string = file.to_string_lossy().to_string();
953
-
954
- for value in &values {
955
- let mut result = serde_json::Map::new();
956
- result.insert("__file".to_string(), serde_json::Value::String(file_string.clone()));
957
-
958
- match &select_fields {
959
- Some(fields) => {
960
- for field in fields {
961
- let json_value = crate::json::resolve_select_field(value, field);
962
- let json_key = crate::json::select_field_key(field);
963
- result.insert(json_key, json_value);
964
- }
965
- }
966
-
967
- None => {
968
- if let serde_yaml::Value::Mapping(map) = value {
969
- for (key, yaml_value) in map {
970
- let json_key = match key {
971
- serde_yaml::Value::String(string) => string.clone(),
972
- _ => format!("{:?}", key),
973
- };
974
-
975
- result.insert(json_key, crate::json::yaml_to_json(yaml_value));
976
- }
977
- }
978
- }
979
- }
980
-
981
- file_results.push(serde_json::Value::Object(result));
982
- }
983
- }
984
-
985
- file_results
986
- })
987
- .collect();
738
+ let selector_string = CStr::from_ptr(path).to_str().unwrap_or("");
739
+ let condition_string = if condition.is_null() { None } else { CStr::from_ptr(condition).to_str().ok() };
740
+ let select_string = if select.is_null() { None } else { CStr::from_ptr(select).to_str().ok() };
988
741
 
742
+ let all_results = crate::glob_find(pattern, selector_string, condition_string, select_string);
989
743
  let length = all_results.len();
990
744
  let json = serde_json::to_string_pretty(&all_results).unwrap_or_else(|_| "[]".to_string());
991
745