yerba 0.2.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.
- checksums.yaml +7 -0
- data/LICENSE.txt +21 -0
- data/README.md +528 -0
- data/exe/yerba +6 -0
- data/ext/yerba/extconf.rb +111 -0
- data/ext/yerba/yerba.c +752 -0
- 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 +31 -0
- data/lib/yerba/document.rb +59 -0
- data/lib/yerba/formatting.rb +18 -0
- data/lib/yerba/location.rb +5 -0
- data/lib/yerba/map.rb +166 -0
- data/lib/yerba/scalar.rb +85 -0
- data/lib/yerba/sequence.rb +182 -0
- data/lib/yerba/version.rb +5 -0
- data/lib/yerba.rb +131 -0
- data/rust/Cargo.lock +805 -0
- data/rust/Cargo.toml +36 -0
- data/rust/build.rs +11 -0
- data/rust/cbindgen.toml +27 -0
- data/rust/rustfmt.toml +2 -0
- data/rust/src/commands/apply.rs +5 -0
- data/rust/src/commands/blank_lines.rs +58 -0
- data/rust/src/commands/check.rs +5 -0
- data/rust/src/commands/delete.rs +35 -0
- data/rust/src/commands/get.rs +194 -0
- data/rust/src/commands/init.rs +89 -0
- data/rust/src/commands/insert.rs +106 -0
- data/rust/src/commands/mate.rs +55 -0
- data/rust/src/commands/mod.rs +349 -0
- data/rust/src/commands/move_item.rs +54 -0
- data/rust/src/commands/move_key.rs +87 -0
- data/rust/src/commands/quote_style.rs +62 -0
- data/rust/src/commands/remove.rs +35 -0
- data/rust/src/commands/rename.rs +37 -0
- data/rust/src/commands/set.rs +59 -0
- data/rust/src/commands/sort.rs +52 -0
- data/rust/src/commands/sort_keys.rs +62 -0
- data/rust/src/commands/version.rs +8 -0
- data/rust/src/document.rs +2237 -0
- data/rust/src/error.rs +45 -0
- data/rust/src/ffi.rs +991 -0
- data/rust/src/json.rs +128 -0
- data/rust/src/lib.rs +29 -0
- data/rust/src/main.rs +72 -0
- data/rust/src/quote_style.rs +42 -0
- data/rust/src/selector.rs +241 -0
- data/rust/src/syntax.rs +262 -0
- data/rust/src/yaml_writer.rs +89 -0
- data/rust/src/yerbafile.rs +475 -0
- data/sig/yerba.rbs +3 -0
- data/yerba.gemspec +52 -0
- metadata +108 -0
data/ext/yerba/yerba.c
ADDED
|
@@ -0,0 +1,752 @@
|
|
|
1
|
+
#include <ruby.h>
|
|
2
|
+
#include <ruby/encoding.h>
|
|
3
|
+
#include <string.h>
|
|
4
|
+
#include "include/yerba.h"
|
|
5
|
+
|
|
6
|
+
static VALUE rb_mYerba;
|
|
7
|
+
static VALUE rb_cDocument;
|
|
8
|
+
static VALUE rb_eError;
|
|
9
|
+
static VALUE rb_ePathNotFoundError;
|
|
10
|
+
static VALUE rb_eParseError;
|
|
11
|
+
static VALUE rb_ePathValidationError;
|
|
12
|
+
|
|
13
|
+
static void document_dfree(void *pointer) {
|
|
14
|
+
if (pointer) yerba_document_free(pointer);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
static size_t document_dsize(const void *pointer) {
|
|
18
|
+
(void) pointer;
|
|
19
|
+
|
|
20
|
+
return sizeof(void *);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
static const rb_data_type_t document_type = {
|
|
24
|
+
.wrap_struct_name = "Yerba::Document",
|
|
25
|
+
.function = {
|
|
26
|
+
.dfree = document_dfree,
|
|
27
|
+
.dsize = document_dsize,
|
|
28
|
+
},
|
|
29
|
+
.flags = RUBY_TYPED_FREE_IMMEDIATELY,
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
static VALUE document_alloc(VALUE klass) {
|
|
33
|
+
return TypedData_Wrap_Struct(klass, &document_type, NULL);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
static inline struct Document *get_document(VALUE self) {
|
|
37
|
+
struct Document *document;
|
|
38
|
+
TypedData_Get_Struct(self, struct Document, &document_type, document);
|
|
39
|
+
return document;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
static VALUE make_utf8_string(const char *cstring) {
|
|
43
|
+
return rb_enc_str_new_cstr(cstring, rb_utf8_encoding());
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
static void check_result(YerbaResult result) {
|
|
47
|
+
if (!result.success) {
|
|
48
|
+
VALUE message = make_utf8_string(result.error);
|
|
49
|
+
VALUE error_class = rb_eError;
|
|
50
|
+
|
|
51
|
+
if (strstr(result.error, "invalid path")) {
|
|
52
|
+
error_class = rb_ePathValidationError;
|
|
53
|
+
} else if (strstr(result.error, "path not found") || strstr(result.error, "not a sequence")) {
|
|
54
|
+
error_class = rb_ePathNotFoundError;
|
|
55
|
+
} else if (strstr(result.error, "parse error")) {
|
|
56
|
+
error_class = rb_eParseError;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
yerba_result_free(result);
|
|
60
|
+
|
|
61
|
+
rb_raise(error_class, "%s", StringValueCStr(message));
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
static VALUE typed_value_to_ruby(YerbaTypedValue typed_value) {
|
|
66
|
+
if (typed_value.text == NULL) return Qnil;
|
|
67
|
+
|
|
68
|
+
VALUE result;
|
|
69
|
+
switch (typed_value.value_type) {
|
|
70
|
+
case YERBA_VALUE_TYPE_NULL:
|
|
71
|
+
result = Qnil;
|
|
72
|
+
break;
|
|
73
|
+
|
|
74
|
+
case YERBA_VALUE_TYPE_BOOLEAN:
|
|
75
|
+
result = (strcmp(typed_value.text, "true") == 0 || strcmp(typed_value.text, "True") == 0 ||
|
|
76
|
+
strcmp(typed_value.text, "TRUE") == 0 || strcmp(typed_value.text, "yes") == 0 ||
|
|
77
|
+
strcmp(typed_value.text, "Yes") == 0 || strcmp(typed_value.text, "YES") == 0 ||
|
|
78
|
+
strcmp(typed_value.text, "on") == 0 || strcmp(typed_value.text, "On") == 0 ||
|
|
79
|
+
strcmp(typed_value.text, "ON") == 0 || strcmp(typed_value.text, "y") == 0 ||
|
|
80
|
+
strcmp(typed_value.text, "Y") == 0)
|
|
81
|
+
? Qtrue : Qfalse;
|
|
82
|
+
break;
|
|
83
|
+
|
|
84
|
+
case YERBA_VALUE_TYPE_INTEGER:
|
|
85
|
+
result = rb_cstr_to_inum(typed_value.text, 0, 0);
|
|
86
|
+
break;
|
|
87
|
+
|
|
88
|
+
case YERBA_VALUE_TYPE_FLOAT:
|
|
89
|
+
result = rb_float_new(rb_cstr_to_dbl(typed_value.text, 0));
|
|
90
|
+
break;
|
|
91
|
+
|
|
92
|
+
case YERBA_VALUE_TYPE_STRING:
|
|
93
|
+
default:
|
|
94
|
+
result = make_utf8_string(typed_value.text);
|
|
95
|
+
break;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return result;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
static int should_proceed(struct Document *document, VALUE opts) {
|
|
102
|
+
if (NIL_P(opts)) return 1;
|
|
103
|
+
VALUE v_condition = rb_hash_aref(opts, ID2SYM(rb_intern("condition")));
|
|
104
|
+
if (NIL_P(v_condition)) return 1;
|
|
105
|
+
|
|
106
|
+
VALUE v_path = rb_hash_aref(opts, ID2SYM(rb_intern("condition_path")));
|
|
107
|
+
const char *parent_path = NIL_P(v_path) ? "" : StringValueCStr(v_path);
|
|
108
|
+
|
|
109
|
+
return yerba_document_evaluate_condition(document, parent_path, StringValueCStr(v_condition));
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/* Document.new(path) */
|
|
113
|
+
static VALUE document_initialize(VALUE self, VALUE path) {
|
|
114
|
+
const char *file_path = StringValueCStr(path);
|
|
115
|
+
YerbaParseResult result = yerba_document_parse_file(file_path);
|
|
116
|
+
|
|
117
|
+
if (!result.document) {
|
|
118
|
+
VALUE message = make_utf8_string(result.error);
|
|
119
|
+
yerba_string_free(result.error);
|
|
120
|
+
|
|
121
|
+
rb_raise(rb_eParseError, "%s", StringValueCStr(message));
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
RTYPEDDATA_DATA(self) = result.document;
|
|
125
|
+
rb_iv_set(self, "@path", path);
|
|
126
|
+
|
|
127
|
+
return self;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/* Document.parse(content) */
|
|
131
|
+
static VALUE document_s_parse(VALUE klass, VALUE content) {
|
|
132
|
+
const char *yaml_content = StringValueCStr(content);
|
|
133
|
+
YerbaParseResult result = yerba_document_parse(yaml_content);
|
|
134
|
+
|
|
135
|
+
if (!result.document) {
|
|
136
|
+
VALUE message = make_utf8_string(result.error);
|
|
137
|
+
yerba_string_free(result.error);
|
|
138
|
+
|
|
139
|
+
rb_raise(rb_eParseError, "%s", StringValueCStr(message));
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
VALUE instance = document_alloc(klass);
|
|
143
|
+
RTYPEDDATA_DATA(instance) = result.document;
|
|
144
|
+
rb_iv_set(instance, "@path", Qnil);
|
|
145
|
+
|
|
146
|
+
return instance;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/* document.get(path) */
|
|
150
|
+
static VALUE document_get(VALUE self, VALUE path) {
|
|
151
|
+
struct Document *document = get_document(self);
|
|
152
|
+
YerbaGetResult result = yerba_document_get(document, StringValueCStr(path));
|
|
153
|
+
|
|
154
|
+
if (result.error) {
|
|
155
|
+
VALUE message = make_utf8_string(result.error);
|
|
156
|
+
yerba_get_result_free(result);
|
|
157
|
+
|
|
158
|
+
rb_raise(rb_ePathValidationError, "%s", StringValueCStr(message));
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
if (!result.is_list) {
|
|
162
|
+
VALUE ruby_value = typed_value_to_ruby(result.single);
|
|
163
|
+
yerba_get_result_free(result);
|
|
164
|
+
|
|
165
|
+
return ruby_value;
|
|
166
|
+
} else {
|
|
167
|
+
VALUE json_string = make_utf8_string(result.list.json);
|
|
168
|
+
yerba_get_result_free(result);
|
|
169
|
+
|
|
170
|
+
VALUE items = rb_funcall(rb_path2class("JSON"), rb_intern("parse"), 1, json_string);
|
|
171
|
+
long length = RARRAY_LEN(items);
|
|
172
|
+
VALUE array = rb_ary_new_capa(length);
|
|
173
|
+
|
|
174
|
+
for (long i = 0; i < length; i++) {
|
|
175
|
+
VALUE item = rb_ary_entry(items, i);
|
|
176
|
+
VALUE text = rb_hash_aref(item, rb_str_new_cstr("text"));
|
|
177
|
+
int type_value = NUM2INT(rb_hash_aref(item, rb_str_new_cstr("type")));
|
|
178
|
+
|
|
179
|
+
if (NIL_P(text)) {
|
|
180
|
+
rb_ary_push(array, Qnil);
|
|
181
|
+
|
|
182
|
+
continue;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
YerbaTypedValue typed_value;
|
|
186
|
+
typed_value.text = (char *)StringValueCStr(text);
|
|
187
|
+
typed_value.value_type = (YerbaValueType)type_value;
|
|
188
|
+
|
|
189
|
+
rb_ary_push(array, typed_value_to_ruby(typed_value));
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
return array;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
static VALUE location_to_ruby(YerbaLocation location) {
|
|
197
|
+
VALUE klass = rb_path2class("Yerba::Location");
|
|
198
|
+
|
|
199
|
+
return rb_funcall(klass, rb_intern("new"), 6,
|
|
200
|
+
SIZET2NUM(location.start_line),
|
|
201
|
+
SIZET2NUM(location.start_column),
|
|
202
|
+
SIZET2NUM(location.end_line),
|
|
203
|
+
SIZET2NUM(location.end_column),
|
|
204
|
+
SIZET2NUM(location.start_offset),
|
|
205
|
+
SIZET2NUM(location.end_offset)
|
|
206
|
+
);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/* document[](path) → Yerba::Scalar, Yerba::Map, Yerba::Sequence, or nil */
|
|
210
|
+
static VALUE document_bracket(VALUE self, VALUE path) {
|
|
211
|
+
struct Document *document = get_document(self);
|
|
212
|
+
YerbaGetResult result = yerba_document_get(document, StringValueCStr(path));
|
|
213
|
+
|
|
214
|
+
if (result.error) {
|
|
215
|
+
VALUE message = make_utf8_string(result.error);
|
|
216
|
+
yerba_get_result_free(result);
|
|
217
|
+
|
|
218
|
+
rb_raise(rb_ePathValidationError, "%s", StringValueCStr(message));
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
VALUE instance;
|
|
222
|
+
VALUE location = location_to_ruby(result.location);
|
|
223
|
+
VALUE key = Qnil;
|
|
224
|
+
|
|
225
|
+
if (result.key_name) {
|
|
226
|
+
VALUE key_location = location_to_ruby(result.key_location);
|
|
227
|
+
VALUE key_value = make_utf8_string(result.key_name);
|
|
228
|
+
|
|
229
|
+
key = rb_funcall(rb_path2class("Yerba::Scalar"), rb_intern("new"), 4, Qnil, Qnil, key_value, key_location);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
switch (result.node_type) {
|
|
233
|
+
case YERBA_NODE_TYPE_SCALAR: {
|
|
234
|
+
VALUE klass = rb_path2class("Yerba::Scalar");
|
|
235
|
+
VALUE value = typed_value_to_ruby(result.single);
|
|
236
|
+
yerba_get_result_free(result);
|
|
237
|
+
|
|
238
|
+
instance = rb_funcall(klass, rb_intern("new"), 5, self, path, value, location, key);
|
|
239
|
+
|
|
240
|
+
return instance;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
case YERBA_NODE_TYPE_MAP: {
|
|
244
|
+
yerba_get_result_free(result);
|
|
245
|
+
VALUE klass = rb_path2class("Yerba::Map");
|
|
246
|
+
|
|
247
|
+
instance = rb_funcall(klass, rb_intern("new"), 4, self, path, location, key);
|
|
248
|
+
|
|
249
|
+
return instance;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
case YERBA_NODE_TYPE_SEQUENCE: {
|
|
253
|
+
yerba_get_result_free(result);
|
|
254
|
+
VALUE klass = rb_path2class("Yerba::Sequence");
|
|
255
|
+
|
|
256
|
+
instance = rb_funcall(klass, rb_intern("new"), 4, self, path, location, key);
|
|
257
|
+
|
|
258
|
+
return instance;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
default:
|
|
262
|
+
yerba_get_result_free(result);
|
|
263
|
+
|
|
264
|
+
return Qnil;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/* document.exists?(path) */
|
|
269
|
+
static VALUE document_exists_p(VALUE self, VALUE path) {
|
|
270
|
+
struct Document *document = get_document(self);
|
|
271
|
+
|
|
272
|
+
return yerba_document_exists(document, StringValueCStr(path)) ? Qtrue : Qfalse;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/* document.get_value(path) → parsed Ruby object (Hash/Array/String/Integer/etc) */
|
|
276
|
+
static VALUE document_get_value(VALUE self, VALUE path) {
|
|
277
|
+
struct Document *document = get_document(self);
|
|
278
|
+
char *json = yerba_document_get_value(document, StringValueCStr(path));
|
|
279
|
+
|
|
280
|
+
if (!json) return Qnil;
|
|
281
|
+
|
|
282
|
+
VALUE json_string = make_utf8_string(json);
|
|
283
|
+
yerba_string_free(json);
|
|
284
|
+
|
|
285
|
+
return rb_funcall(rb_path2class("JSON"), rb_intern("parse"), 1, json_string);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/* document.get_values(path) → Array of parsed Ruby objects */
|
|
289
|
+
static VALUE document_get_values(VALUE self, VALUE path) {
|
|
290
|
+
struct Document *document = get_document(self);
|
|
291
|
+
char *json = yerba_document_get_values(document, StringValueCStr(path));
|
|
292
|
+
|
|
293
|
+
if (!json) return rb_ary_new();
|
|
294
|
+
|
|
295
|
+
VALUE json_string = make_utf8_string(json);
|
|
296
|
+
yerba_string_free(json);
|
|
297
|
+
|
|
298
|
+
return rb_funcall(rb_path2class("JSON"), rb_intern("parse"), 1, json_string);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/* document.get_quote_style(path) → :plain, :single, :double, or nil */
|
|
302
|
+
static VALUE document_get_quote_style(VALUE self, VALUE path) {
|
|
303
|
+
struct Document *document = get_document(self);
|
|
304
|
+
int style = yerba_document_get_quote_style(document, StringValueCStr(path));
|
|
305
|
+
|
|
306
|
+
switch (style) {
|
|
307
|
+
case 0: return ID2SYM(rb_intern("plain"));
|
|
308
|
+
case 1: return ID2SYM(rb_intern("single"));
|
|
309
|
+
case 2: return ID2SYM(rb_intern("double"));
|
|
310
|
+
default: return Qnil;
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/* document.set_quote_style(path, style) */
|
|
315
|
+
static VALUE document_set_quote_style(VALUE self, VALUE path, VALUE style) {
|
|
316
|
+
struct Document *document = get_document(self);
|
|
317
|
+
|
|
318
|
+
int style_int;
|
|
319
|
+
if (RB_TYPE_P(style, T_SYMBOL)) {
|
|
320
|
+
ID style_id = SYM2ID(style);
|
|
321
|
+
if (style_id == rb_intern("plain")) style_int = 0;
|
|
322
|
+
else if (style_id == rb_intern("single")) style_int = 1;
|
|
323
|
+
else if (style_id == rb_intern("double")) style_int = 2;
|
|
324
|
+
else rb_raise(rb_eError, "Invalid quote style (use :plain, :single, or :double)");
|
|
325
|
+
} else {
|
|
326
|
+
style_int = NUM2INT(style);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
YerbaResult result = yerba_document_set_quote_style(document, StringValueCStr(path), style_int);
|
|
330
|
+
|
|
331
|
+
check_result(result);
|
|
332
|
+
|
|
333
|
+
return self;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
/* document.condition?(condition, path: "") */
|
|
337
|
+
static VALUE document_condition_p(int argc, VALUE *argv, VALUE self) {
|
|
338
|
+
VALUE condition, opts;
|
|
339
|
+
rb_scan_args(argc, argv, "1:", &condition, &opts);
|
|
340
|
+
|
|
341
|
+
const char *parent_path = "";
|
|
342
|
+
if (!NIL_P(opts)) {
|
|
343
|
+
VALUE v_path = rb_hash_aref(opts, ID2SYM(rb_intern("path")));
|
|
344
|
+
|
|
345
|
+
if (!NIL_P(v_path)) {
|
|
346
|
+
parent_path = StringValueCStr(v_path);
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
struct Document *document = get_document(self);
|
|
351
|
+
|
|
352
|
+
return yerba_document_evaluate_condition(document, parent_path, StringValueCStr(condition)) ? Qtrue : Qfalse;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
/* document.find(path, condition: nil, select: nil) */
|
|
356
|
+
static VALUE document_find(int argc, VALUE *argv, VALUE self) {
|
|
357
|
+
VALUE path, opts;
|
|
358
|
+
rb_scan_args(argc, argv, "1:", &path, &opts);
|
|
359
|
+
|
|
360
|
+
const char *condition = NULL;
|
|
361
|
+
const char *select = NULL;
|
|
362
|
+
|
|
363
|
+
if (!NIL_P(opts)) {
|
|
364
|
+
VALUE v_condition = rb_hash_aref(opts, ID2SYM(rb_intern("condition")));
|
|
365
|
+
VALUE v_select = rb_hash_aref(opts, ID2SYM(rb_intern("select")));
|
|
366
|
+
|
|
367
|
+
if (!NIL_P(v_condition)) condition = StringValueCStr(v_condition);
|
|
368
|
+
if (!NIL_P(v_select)) select = StringValueCStr(v_select);
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
struct Document *document = get_document(self);
|
|
372
|
+
char *json = yerba_document_find(document, StringValueCStr(path), condition, select);
|
|
373
|
+
|
|
374
|
+
if (!json) return rb_ary_new();
|
|
375
|
+
|
|
376
|
+
VALUE json_string = make_utf8_string(json);
|
|
377
|
+
yerba_string_free(json);
|
|
378
|
+
|
|
379
|
+
return rb_funcall(rb_path2class("JSON"), rb_intern("parse"), 1, json_string);
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
/* document.set(path, value, condition: nil, if_exists: false, if_missing: false) */
|
|
383
|
+
static VALUE document_set(int argc, VALUE *argv, VALUE self) {
|
|
384
|
+
VALUE path, value, opts;
|
|
385
|
+
rb_scan_args(argc, argv, "2:", &path, &value, &opts);
|
|
386
|
+
|
|
387
|
+
struct Document *document = get_document(self);
|
|
388
|
+
|
|
389
|
+
if (!should_proceed(document, opts)) return self;
|
|
390
|
+
|
|
391
|
+
if (!NIL_P(opts)) {
|
|
392
|
+
VALUE v_if_exists = rb_hash_aref(opts, ID2SYM(rb_intern("if_exists")));
|
|
393
|
+
VALUE v_if_missing = rb_hash_aref(opts, ID2SYM(rb_intern("if_missing")));
|
|
394
|
+
|
|
395
|
+
if (RTEST(v_if_exists) && !yerba_document_exists(document, StringValueCStr(path))) return self;
|
|
396
|
+
if (RTEST(v_if_missing) && yerba_document_exists(document, StringValueCStr(path))) return self;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
const char *c_value;
|
|
400
|
+
YerbaValueType value_type;
|
|
401
|
+
char number_buffer[64];
|
|
402
|
+
|
|
403
|
+
if (value == Qnil) {
|
|
404
|
+
c_value = "null";
|
|
405
|
+
value_type = YERBA_VALUE_TYPE_NULL;
|
|
406
|
+
} else if (value == Qtrue) {
|
|
407
|
+
c_value = "true";
|
|
408
|
+
value_type = YERBA_VALUE_TYPE_BOOLEAN;
|
|
409
|
+
} else if (value == Qfalse) {
|
|
410
|
+
c_value = "false";
|
|
411
|
+
value_type = YERBA_VALUE_TYPE_BOOLEAN;
|
|
412
|
+
} else if (RB_INTEGER_TYPE_P(value)) {
|
|
413
|
+
snprintf(number_buffer, sizeof(number_buffer), "%ld", NUM2LONG(value));
|
|
414
|
+
c_value = number_buffer;
|
|
415
|
+
value_type = YERBA_VALUE_TYPE_INTEGER;
|
|
416
|
+
} else if (RB_FLOAT_TYPE_P(value)) {
|
|
417
|
+
snprintf(number_buffer, sizeof(number_buffer), "%g", NUM2DBL(value));
|
|
418
|
+
c_value = number_buffer;
|
|
419
|
+
value_type = YERBA_VALUE_TYPE_FLOAT;
|
|
420
|
+
} else {
|
|
421
|
+
c_value = StringValueCStr(value);
|
|
422
|
+
value_type = YERBA_VALUE_TYPE_STRING;
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
YerbaResult result = yerba_document_set(document, StringValueCStr(path), c_value, value_type);
|
|
426
|
+
check_result(result);
|
|
427
|
+
|
|
428
|
+
return self;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
/* document.insert(path, value, before: nil, after: nil, at: nil) */
|
|
432
|
+
static VALUE document_insert(int argc, VALUE *argv, VALUE self) {
|
|
433
|
+
VALUE path, value, opts;
|
|
434
|
+
rb_scan_args(argc, argv, "2:", &path, &value, &opts);
|
|
435
|
+
|
|
436
|
+
const char *before = NULL;
|
|
437
|
+
const char *after = NULL;
|
|
438
|
+
long long at = -1;
|
|
439
|
+
|
|
440
|
+
if (!NIL_P(opts)) {
|
|
441
|
+
VALUE v_before = rb_hash_aref(opts, ID2SYM(rb_intern("before")));
|
|
442
|
+
VALUE v_after = rb_hash_aref(opts, ID2SYM(rb_intern("after")));
|
|
443
|
+
VALUE v_at = rb_hash_aref(opts, ID2SYM(rb_intern("at")));
|
|
444
|
+
|
|
445
|
+
if (!NIL_P(v_before)) before = StringValueCStr(v_before);
|
|
446
|
+
if (!NIL_P(v_after)) after = StringValueCStr(v_after);
|
|
447
|
+
if (!NIL_P(v_at)) at = NUM2LL(v_at);
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
struct Document *document = get_document(self);
|
|
451
|
+
YerbaResult result = yerba_document_insert(document, StringValueCStr(path), StringValueCStr(value), before, after, at);
|
|
452
|
+
check_result(result);
|
|
453
|
+
|
|
454
|
+
return self;
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
/* document.insert_object(path, object, before: nil, after: nil, at: nil) */
|
|
458
|
+
static VALUE document_insert_object(int argc, VALUE *argv, VALUE self) {
|
|
459
|
+
VALUE path, object, opts;
|
|
460
|
+
rb_scan_args(argc, argv, "2:", &path, &object, &opts);
|
|
461
|
+
|
|
462
|
+
const char *before = NULL;
|
|
463
|
+
const char *after = NULL;
|
|
464
|
+
long long at = -1;
|
|
465
|
+
|
|
466
|
+
if (!NIL_P(opts)) {
|
|
467
|
+
VALUE v_before = rb_hash_aref(opts, ID2SYM(rb_intern("before")));
|
|
468
|
+
VALUE v_after = rb_hash_aref(opts, ID2SYM(rb_intern("after")));
|
|
469
|
+
VALUE v_at = rb_hash_aref(opts, ID2SYM(rb_intern("at")));
|
|
470
|
+
|
|
471
|
+
if (!NIL_P(v_before)) before = StringValueCStr(v_before);
|
|
472
|
+
if (!NIL_P(v_after)) after = StringValueCStr(v_after);
|
|
473
|
+
if (!NIL_P(v_at)) at = NUM2LL(v_at);
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
struct Document *document = get_document(self);
|
|
477
|
+
VALUE json_string = rb_funcall(rb_path2class("JSON"), rb_intern("generate"), 1, object);
|
|
478
|
+
|
|
479
|
+
YerbaResult result = yerba_document_insert_object(document, StringValueCStr(path), StringValueCStr(json_string), before, after, at);
|
|
480
|
+
check_result(result);
|
|
481
|
+
return self;
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
/* document.delete(path, condition: nil) */
|
|
485
|
+
static VALUE document_delete(int argc, VALUE *argv, VALUE self) {
|
|
486
|
+
VALUE path, opts;
|
|
487
|
+
rb_scan_args(argc, argv, "1:", &path, &opts);
|
|
488
|
+
|
|
489
|
+
struct Document *document = get_document(self);
|
|
490
|
+
if (!should_proceed(document, opts)) return self;
|
|
491
|
+
|
|
492
|
+
YerbaResult result = yerba_document_delete(document, StringValueCStr(path));
|
|
493
|
+
|
|
494
|
+
check_result(result);
|
|
495
|
+
|
|
496
|
+
return self;
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
/* document.remove(path, value) */
|
|
500
|
+
static VALUE document_remove(VALUE self, VALUE path, VALUE value) {
|
|
501
|
+
struct Document *document = get_document(self);
|
|
502
|
+
YerbaResult result = yerba_document_remove(document, StringValueCStr(path), StringValueCStr(value));
|
|
503
|
+
|
|
504
|
+
check_result(result);
|
|
505
|
+
|
|
506
|
+
return self;
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
/* document.remove_at(path, index) */
|
|
510
|
+
static VALUE document_remove_at(VALUE self, VALUE path, VALUE index) {
|
|
511
|
+
struct Document *document = get_document(self);
|
|
512
|
+
YerbaResult result = yerba_document_remove_at(document, StringValueCStr(path), NUM2SIZET(index));
|
|
513
|
+
|
|
514
|
+
check_result(result);
|
|
515
|
+
|
|
516
|
+
return self;
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
/* document.rename(source, destination) */
|
|
520
|
+
static VALUE document_rename(VALUE self, VALUE source, VALUE destination) {
|
|
521
|
+
struct Document *document = get_document(self);
|
|
522
|
+
YerbaResult result = yerba_document_rename(document, StringValueCStr(source), StringValueCStr(destination));
|
|
523
|
+
|
|
524
|
+
check_result(result);
|
|
525
|
+
|
|
526
|
+
return self;
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
/* document.sort(path, by: nil, case_sensitive: false) */
|
|
530
|
+
static VALUE document_sort(int argc, VALUE *argv, VALUE self) {
|
|
531
|
+
VALUE path, opts;
|
|
532
|
+
rb_scan_args(argc, argv, "1:", &path, &opts);
|
|
533
|
+
|
|
534
|
+
const char *by = NULL;
|
|
535
|
+
bool case_sensitive = false;
|
|
536
|
+
|
|
537
|
+
if (!NIL_P(opts)) {
|
|
538
|
+
VALUE v_by = rb_hash_aref(opts, ID2SYM(rb_intern("by")));
|
|
539
|
+
VALUE v_case_sensitive = rb_hash_aref(opts, ID2SYM(rb_intern("case_sensitive")));
|
|
540
|
+
|
|
541
|
+
if (!NIL_P(v_by)) by = StringValueCStr(v_by);
|
|
542
|
+
if (RTEST(v_case_sensitive)) case_sensitive = true;
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
struct Document *document = get_document(self);
|
|
546
|
+
YerbaResult result = yerba_document_sort(document, StringValueCStr(path), by, case_sensitive);
|
|
547
|
+
|
|
548
|
+
check_result(result);
|
|
549
|
+
|
|
550
|
+
return self;
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
/* document.sort_keys(path, order) */
|
|
554
|
+
static VALUE document_sort_keys(VALUE self, VALUE path, VALUE order) {
|
|
555
|
+
struct Document *document = get_document(self);
|
|
556
|
+
|
|
557
|
+
VALUE order_string;
|
|
558
|
+
if (RB_TYPE_P(order, T_ARRAY)) {
|
|
559
|
+
order_string = rb_ary_join(order, rb_str_new_cstr(","));
|
|
560
|
+
} else {
|
|
561
|
+
order_string = order;
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
YerbaResult result = yerba_document_sort_keys(document, StringValueCStr(path), StringValueCStr(order_string));
|
|
565
|
+
|
|
566
|
+
check_result(result);
|
|
567
|
+
|
|
568
|
+
return self;
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
/* document.quote_style(path: nil, key_style: nil, value_style: nil) */
|
|
572
|
+
static VALUE document_quote_style(int argc, VALUE *argv, VALUE self) {
|
|
573
|
+
VALUE opts;
|
|
574
|
+
rb_scan_args(argc, argv, ":", &opts);
|
|
575
|
+
|
|
576
|
+
const char *path = NULL;
|
|
577
|
+
const char *key_style = NULL;
|
|
578
|
+
const char *value_style = NULL;
|
|
579
|
+
|
|
580
|
+
if (!NIL_P(opts)) {
|
|
581
|
+
VALUE v_path = rb_hash_aref(opts, ID2SYM(rb_intern("path")));
|
|
582
|
+
VALUE v_key_style = rb_hash_aref(opts, ID2SYM(rb_intern("key_style")));
|
|
583
|
+
VALUE v_value_style = rb_hash_aref(opts, ID2SYM(rb_intern("value_style")));
|
|
584
|
+
|
|
585
|
+
if (!NIL_P(v_path)) path = StringValueCStr(v_path);
|
|
586
|
+
if (!NIL_P(v_key_style)) key_style = StringValueCStr(v_key_style);
|
|
587
|
+
if (!NIL_P(v_value_style)) value_style = StringValueCStr(v_value_style);
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
struct Document *document = get_document(self);
|
|
591
|
+
YerbaResult result = yerba_document_quote_style(document, path, key_style, value_style);
|
|
592
|
+
|
|
593
|
+
check_result(result);
|
|
594
|
+
|
|
595
|
+
return self;
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
/* document.blank_lines(path, count) */
|
|
599
|
+
static VALUE document_blank_lines(VALUE self, VALUE path, VALUE count) {
|
|
600
|
+
struct Document *document = get_document(self);
|
|
601
|
+
YerbaResult result = yerba_document_blank_lines(document, StringValueCStr(path), NUM2SIZET(count));
|
|
602
|
+
|
|
603
|
+
check_result(result);
|
|
604
|
+
|
|
605
|
+
return self;
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
/* document.to_s */
|
|
609
|
+
static VALUE document_to_s(VALUE self) {
|
|
610
|
+
struct Document *document = get_document(self);
|
|
611
|
+
char *content = yerba_document_to_string(document);
|
|
612
|
+
VALUE string = make_utf8_string(content);
|
|
613
|
+
|
|
614
|
+
yerba_string_free(content);
|
|
615
|
+
|
|
616
|
+
return string;
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
/* document.save! */
|
|
620
|
+
static VALUE document_save(VALUE self) {
|
|
621
|
+
VALUE path = rb_iv_get(self, "@path");
|
|
622
|
+
|
|
623
|
+
if (NIL_P(path)) {
|
|
624
|
+
rb_raise(rb_eError, "Cannot save: document has no file path");
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
VALUE content = document_to_s(self);
|
|
628
|
+
|
|
629
|
+
rb_funcall(rb_cFile, rb_intern("write"), 2, path, content);
|
|
630
|
+
|
|
631
|
+
return self;
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
/* document.changed? */
|
|
635
|
+
static VALUE document_changed_p(VALUE self) {
|
|
636
|
+
VALUE path = rb_iv_get(self, "@path");
|
|
637
|
+
if (NIL_P(path)) return Qtrue;
|
|
638
|
+
|
|
639
|
+
VALUE current = document_to_s(self);
|
|
640
|
+
VALUE original = rb_funcall(rb_cFile, rb_intern("read"), 1, path);
|
|
641
|
+
|
|
642
|
+
return rb_str_equal(current, original) ? Qfalse : Qtrue;
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
/* document.path */
|
|
646
|
+
static VALUE document_path(VALUE self) {
|
|
647
|
+
return rb_iv_get(self, "@path");
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
/* Collection.get(glob, path) — get values across files */
|
|
651
|
+
static VALUE collection_s_get(VALUE self, VALUE pattern, VALUE path) {
|
|
652
|
+
(void)self;
|
|
653
|
+
YerbaTypedList result = yerba_glob_get(StringValueCStr(pattern), StringValueCStr(path));
|
|
654
|
+
|
|
655
|
+
if (!result.json) return rb_ary_new();
|
|
656
|
+
|
|
657
|
+
VALUE json_string = make_utf8_string(result.json);
|
|
658
|
+
yerba_string_free(result.json);
|
|
659
|
+
|
|
660
|
+
VALUE items = rb_funcall(rb_path2class("JSON"), rb_intern("parse"), 1, json_string);
|
|
661
|
+
long length = RARRAY_LEN(items);
|
|
662
|
+
VALUE array = rb_ary_new_capa(length);
|
|
663
|
+
|
|
664
|
+
for (long i = 0; i < length; i++) {
|
|
665
|
+
VALUE item = rb_ary_entry(items, i);
|
|
666
|
+
VALUE text = rb_hash_aref(item, rb_str_new_cstr("text"));
|
|
667
|
+
int type_value = NUM2INT(rb_hash_aref(item, rb_str_new_cstr("type")));
|
|
668
|
+
|
|
669
|
+
if (NIL_P(text)) {
|
|
670
|
+
rb_ary_push(array, Qnil);
|
|
671
|
+
continue;
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
YerbaTypedValue typed_value;
|
|
675
|
+
typed_value.text = (char *)StringValueCStr(text);
|
|
676
|
+
typed_value.value_type = (YerbaValueType)type_value;
|
|
677
|
+
rb_ary_push(array, typed_value_to_ruby(typed_value));
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
return array;
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
/* Collection.find(glob, path, condition: nil, select: nil) */
|
|
684
|
+
static VALUE collection_s_find(int argc, VALUE *argv, VALUE self) {
|
|
685
|
+
(void)self;
|
|
686
|
+
VALUE pattern, path, opts;
|
|
687
|
+
rb_scan_args(argc, argv, "2:", &pattern, &path, &opts);
|
|
688
|
+
|
|
689
|
+
const char *condition = NULL;
|
|
690
|
+
const char *select = NULL;
|
|
691
|
+
|
|
692
|
+
if (!NIL_P(opts)) {
|
|
693
|
+
VALUE v_condition = rb_hash_aref(opts, ID2SYM(rb_intern("condition")));
|
|
694
|
+
VALUE v_select = rb_hash_aref(opts, ID2SYM(rb_intern("select")));
|
|
695
|
+
|
|
696
|
+
if (!NIL_P(v_condition)) condition = StringValueCStr(v_condition);
|
|
697
|
+
if (!NIL_P(v_select)) select = StringValueCStr(v_select);
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
YerbaTypedList result = yerba_glob_find(StringValueCStr(pattern), StringValueCStr(path), condition, select);
|
|
701
|
+
|
|
702
|
+
if (!result.json) return rb_ary_new();
|
|
703
|
+
|
|
704
|
+
VALUE json_string = make_utf8_string(result.json);
|
|
705
|
+
yerba_string_free(result.json);
|
|
706
|
+
|
|
707
|
+
return rb_funcall(rb_path2class("JSON"), rb_intern("parse"), 1, json_string);
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
void Init_yerba(void) {
|
|
711
|
+
rb_require("json");
|
|
712
|
+
|
|
713
|
+
rb_mYerba = rb_define_module("Yerba");
|
|
714
|
+
rb_eError = rb_define_class_under(rb_mYerba, "Error", rb_eStandardError);
|
|
715
|
+
rb_ePathNotFoundError = rb_define_class_under(rb_mYerba, "PathNotFoundError", rb_eError);
|
|
716
|
+
rb_eParseError = rb_define_class_under(rb_mYerba, "ParseError", rb_eError);
|
|
717
|
+
rb_ePathValidationError = rb_define_class_under(rb_mYerba, "PathValidationError", rb_eError);
|
|
718
|
+
|
|
719
|
+
VALUE rb_cCollection = rb_define_class_under(rb_mYerba, "Collection", rb_cObject);
|
|
720
|
+
rb_define_singleton_method(rb_cCollection, "get", collection_s_get, 2);
|
|
721
|
+
rb_define_singleton_method(rb_cCollection, "find", collection_s_find, -1);
|
|
722
|
+
|
|
723
|
+
rb_cDocument = rb_define_class_under(rb_mYerba, "Document", rb_cObject);
|
|
724
|
+
|
|
725
|
+
rb_define_alloc_func(rb_cDocument, document_alloc);
|
|
726
|
+
rb_define_method(rb_cDocument, "initialize", document_initialize, 1);
|
|
727
|
+
rb_define_singleton_method(rb_cDocument, "parse", document_s_parse, 1);
|
|
728
|
+
rb_define_method(rb_cDocument, "get", document_get, 1);
|
|
729
|
+
rb_define_method(rb_cDocument, "[]", document_bracket, 1);
|
|
730
|
+
rb_define_method(rb_cDocument, "get_value", document_get_value, 1);
|
|
731
|
+
rb_define_method(rb_cDocument, "get_values", document_get_values, 1);
|
|
732
|
+
rb_define_method(rb_cDocument, "get_quote_style", document_get_quote_style, 1);
|
|
733
|
+
rb_define_method(rb_cDocument, "set_quote_style", document_set_quote_style, 2);
|
|
734
|
+
rb_define_method(rb_cDocument, "exists?", document_exists_p, 1);
|
|
735
|
+
rb_define_method(rb_cDocument, "condition?", document_condition_p, -1);
|
|
736
|
+
rb_define_method(rb_cDocument, "find", document_find, -1);
|
|
737
|
+
rb_define_method(rb_cDocument, "set", document_set, -1);
|
|
738
|
+
rb_define_method(rb_cDocument, "insert", document_insert, -1);
|
|
739
|
+
rb_define_method(rb_cDocument, "insert_object", document_insert_object, -1);
|
|
740
|
+
rb_define_method(rb_cDocument, "delete", document_delete, -1);
|
|
741
|
+
rb_define_method(rb_cDocument, "remove", document_remove, 2);
|
|
742
|
+
rb_define_method(rb_cDocument, "remove_at", document_remove_at, 2);
|
|
743
|
+
rb_define_method(rb_cDocument, "rename", document_rename, 2);
|
|
744
|
+
rb_define_method(rb_cDocument, "sort", document_sort, -1);
|
|
745
|
+
rb_define_method(rb_cDocument, "sort_keys", document_sort_keys, 2);
|
|
746
|
+
rb_define_method(rb_cDocument, "quote_style", document_quote_style, -1);
|
|
747
|
+
rb_define_method(rb_cDocument, "blank_lines", document_blank_lines, 2);
|
|
748
|
+
rb_define_method(rb_cDocument, "to_s", document_to_s, 0);
|
|
749
|
+
rb_define_method(rb_cDocument, "save!", document_save, 0);
|
|
750
|
+
rb_define_method(rb_cDocument, "changed?", document_changed_p, 0);
|
|
751
|
+
rb_define_method(rb_cDocument, "path", document_path, 0);
|
|
752
|
+
}
|