yerba 0.9.2-aarch64-linux-gnu → 0.10.0-aarch64-linux-gnu

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +34 -17
  3. data/exe/aarch64-linux-gnu/yerba +0 -0
  4. data/ext/yerba/include/yerba.h +56 -34
  5. data/ext/yerba/yerba.c +268 -186
  6. data/lib/yerba/3.2/yerba.so +0 -0
  7. data/lib/yerba/3.3/yerba.so +0 -0
  8. data/lib/yerba/3.4/yerba.so +0 -0
  9. data/lib/yerba/4.0/yerba.so +0 -0
  10. data/lib/yerba/collection.rb +4 -4
  11. data/lib/yerba/document.rb +134 -0
  12. data/lib/yerba/formatting.rb +1 -0
  13. data/lib/yerba/location.rb +8 -1
  14. data/lib/yerba/map.rb +16 -6
  15. data/lib/yerba/node.rb +41 -0
  16. data/lib/yerba/query_result.rb +33 -10
  17. data/lib/yerba/scalar.rb +29 -0
  18. data/lib/yerba/selector.rb +3 -1
  19. data/lib/yerba/sequence.rb +38 -33
  20. data/lib/yerba/version.rb +1 -1
  21. data/rust/Cargo.lock +1 -1
  22. data/rust/Cargo.toml +1 -1
  23. data/rust/src/commands/get.rs +12 -12
  24. data/rust/src/commands/init.rs +8 -8
  25. data/rust/src/commands/insert.rs +4 -18
  26. data/rust/src/commands/location.rs +2 -0
  27. data/rust/src/commands/mod.rs +34 -6
  28. data/rust/src/commands/move_item.rs +1 -1
  29. data/rust/src/commands/move_key.rs +8 -8
  30. data/rust/src/commands/rename.rs +1 -1
  31. data/rust/src/commands/selectors.rs +1 -1
  32. data/rust/src/commands/set.rs +14 -6
  33. data/rust/src/document/condition.rs +25 -25
  34. data/rust/src/document/delete.rs +57 -43
  35. data/rust/src/document/get.rs +91 -82
  36. data/rust/src/document/insert.rs +77 -77
  37. data/rust/src/document/mod.rs +138 -64
  38. data/rust/src/document/nodes.rs +162 -0
  39. data/rust/src/document/schema.rs +8 -8
  40. data/rust/src/document/set.rs +49 -26
  41. data/rust/src/document/sort.rs +54 -54
  42. data/rust/src/document/style.rs +50 -49
  43. data/rust/src/document/unique.rs +5 -5
  44. data/rust/src/error.rs +8 -4
  45. data/rust/src/ffi.rs +218 -106
  46. data/rust/src/json.rs +62 -2
  47. data/rust/src/lib.rs +2 -1
  48. data/rust/src/main.rs +2 -1
  49. data/rust/src/selector.rs +48 -7
  50. data/rust/src/syntax.rs +449 -56
  51. data/rust/src/validation.rs +5 -3
  52. data/rust/src/yerbafile.rs +124 -76
  53. metadata +3 -2
Binary file
Binary file
Binary file
Binary file
@@ -12,12 +12,12 @@ module Yerba
12
12
  Dir.glob(@glob).each { |path| yield Document.new(path) }
13
13
  end
14
14
 
15
- def get(path)
16
- self.class.get(@glob, path)
15
+ def get(selector)
16
+ self.class.get(@glob, selector)
17
17
  end
18
18
 
19
- def find(path, condition: nil, select: nil)
20
- self.class.find(@glob, path, condition: condition, select: select)
19
+ def find(selector, condition: nil, select: nil)
20
+ self.class.find(@glob, selector, condition: condition, select: select)
21
21
  end
22
22
 
23
23
  def find_by(...)
@@ -2,6 +2,8 @@
2
2
 
3
3
  module Yerba
4
4
  class Document
5
+ include NodeTree
6
+
5
7
  ROOT_SELECTOR = ""
6
8
 
7
9
  def self.cache
@@ -76,6 +78,48 @@ module Yerba
76
78
  File.mtime(@path) != @loaded_mtime
77
79
  end
78
80
 
81
+ def [](selector)
82
+ return native_node_at(selector) unless selector.is_a?(String)
83
+
84
+ parsed = Selector.parse(selector)
85
+ matches = find_nodes(parsed)
86
+ return native_node_at(selector) if matches.nil?
87
+
88
+ matches = matches.map { |match| match&.dup }
89
+ parsed.wildcard? ? matches.compact : matches.first
90
+ end
91
+ alias node_at []
92
+
93
+ def value_at(selector)
94
+ return native_value_at(selector) unless selector.is_a?(String) || selector.is_a?(Selector)
95
+
96
+ parsed = Selector.parse(selector)
97
+ matches = find_nodes(parsed) unless parsed.wildcard?
98
+ return native_value_at(selector) if matches.nil?
99
+
100
+ node = matches.first
101
+ return nil if node.nil?
102
+
103
+ node.is_a?(Scalar) && node.tree_node? ? node.value : native_value_at(selector)
104
+ end
105
+
106
+ def nodes
107
+ generation = native_generation
108
+ return @nodes if @nodes_generation == generation
109
+
110
+ @nodes_generation = generation
111
+ @nodes = build_nodes(native_node_records)
112
+ end
113
+
114
+ def length_at(selector)
115
+ parsed = Selector.parse(selector)
116
+ matches = find_nodes(parsed) unless parsed.wildcard?
117
+ return Array(value_at(selector)).length if matches.nil?
118
+
119
+ children = matches.first&.tree_children
120
+ children.is_a?(Array) ? children.length : 0
121
+ end
122
+
79
123
  def dig(*keys)
80
124
  keys.reduce(self) { |node, key| node.nil? ? nil : node[key] }
81
125
  end
@@ -177,6 +221,96 @@ module Yerba
177
221
 
178
222
  private
179
223
 
224
+ def build_nodes(records)
225
+ built = []
226
+
227
+ records.each do |parent, type, selector, key_name, value, has_value, location, key_location|
228
+ key = Scalar.from_document(nil, nil, key_location, nil, value: key_name) if key_name
229
+ node = build_node(type, selector, location, key, value)
230
+
231
+ node.attach_tree(built.first || node, tree_container(type), !has_value) if type
232
+ add_child(built[parent], key_name, node) unless parent.negative?
233
+
234
+ built << node
235
+ end
236
+
237
+ built.each { |node| node&.tree_children&.freeze }
238
+ built.first
239
+ end
240
+
241
+ def build_node(type, selector, location, key, value)
242
+ case type
243
+ when :scalar
244
+ Scalar.from_document(self, selector, location, key, value: value)
245
+ when :map
246
+ Map.from_document(self, selector, location, key)
247
+ when :sequence
248
+ Sequence.from_document(self, selector, location, key)
249
+ else
250
+ native_node_at(selector)
251
+ end
252
+ end
253
+
254
+ def tree_container(type)
255
+ case type
256
+ when :map
257
+ {}
258
+ when :sequence
259
+ []
260
+ end
261
+ end
262
+
263
+ def add_child(parent, key_name, node)
264
+ children = parent.tree_children
265
+
266
+ if children.is_a?(Hash)
267
+ children[key_name] = node
268
+ else
269
+ children << node
270
+ end
271
+ end
272
+
273
+ def find_nodes(selector)
274
+ return nil if selector.relative? || malformed?(selector.to_s) || nodes.nil?
275
+
276
+ wildcard = selector.wildcard?
277
+
278
+ selector.segments.reduce([nodes]) do |current, segment|
279
+ current.flat_map do |node|
280
+ children = node&.tree_node? ? node.tree_children : nil
281
+ matches = children && child_matches(children, segment)
282
+ return nil if matches.nil?
283
+
284
+ wildcard && children.is_a?(Hash) ? matches.reject { |match| valueless_entry?(match) } : matches
285
+ end
286
+ end
287
+ end
288
+
289
+ def valueless_entry?(node)
290
+ node.valueless?
291
+ end
292
+
293
+ def malformed?(selector)
294
+ selector.end_with?(".") || selector.include?("..") || (selector.include?("[") && !selector.include?("]"))
295
+ end
296
+
297
+ def child_matches(children, segment)
298
+ case segment
299
+ when Selector::ALL_ITEMS
300
+ children if children.is_a?(Array)
301
+ when Selector::ALL_KEYS
302
+ children.values if children.is_a?(Hash)
303
+ when Integer
304
+ return nil unless children.is_a?(Array)
305
+
306
+ segment < children.length ? [children[segment]] : []
307
+ else
308
+ return nil unless children.is_a?(Hash)
309
+
310
+ children.key?(segment) ? [children[segment]] : []
311
+ end
312
+ end
313
+
180
314
  def empty_root?
181
315
  root.is_a?(Scalar) && root.value.nil?
182
316
  end
@@ -23,6 +23,7 @@ module Yerba
23
23
  when true then "true"
24
24
  when false then "false"
25
25
  when nil then "null"
26
+ when String then Yerba.quote_scalar(value, "flow")
26
27
  else value.to_s
27
28
  end
28
29
  end
@@ -1,5 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Yerba
4
- Location = Data.define(:start_line, :start_column, :end_line, :end_column, :start_offset, :end_offset)
4
+ Location = Data.define(
5
+ :start_line, :start_column, :end_line, :end_column, :start_offset, :end_offset,
6
+ :start_character_column, :end_character_column
7
+ ) do
8
+ def initialize(start_column:, end_column:, start_character_column: start_column, end_character_column: end_column, **)
9
+ super
10
+ end
11
+ end
5
12
  end
data/lib/yerba/map.rb CHANGED
@@ -40,13 +40,14 @@ module Yerba
40
40
  all_keys = keys
41
41
  key_index = all_keys.index(key.to_s)
42
42
  after_key = key_index&.positive? ? all_keys[key_index - 1] : nil
43
+ before_key = key_index&.zero? ? all_keys[1] : nil
43
44
 
44
- document.delete(new_path)
45
-
46
- if after_key
47
- document.insert(new_path, coerced, after: after_key, plain: true)
45
+ if after_key || before_key
46
+ document.delete(new_path)
47
+ document.insert(new_path, coerced, after: after_key, before: before_key, plain: true)
48
48
  else
49
- document.insert(new_path, coerced, plain: true)
49
+ document.set(new_path, coerce_value(value, style: :flow), plain: true)
50
+ document[new_path].collection_style = :block unless value.empty?
50
51
  end
51
52
  elsif document.exists?(new_path)
52
53
  document.set(new_path, coerced, plain: is_collection)
@@ -93,6 +94,12 @@ module Yerba
93
94
  return self
94
95
  end
95
96
 
97
+ if (tree = current_children)
98
+ tree.each_key { |name| yield name, tree[name]&.dup }
99
+
100
+ return self
101
+ end
102
+
96
103
  names = keys
97
104
  values = document.get_all(@selector.empty? ? "*" : "#{@selector}.*")
98
105
 
@@ -124,6 +131,9 @@ module Yerba
124
131
 
125
132
  def value_at(key)
126
133
  if connected?
134
+ child = current_children&.[](key.to_s)
135
+ return child.value if child.is_a?(Scalar)
136
+
127
137
  new_path = @selector.empty? ? key.to_s : "#{@selector}.#{key}"
128
138
  document.value_at(new_path)
129
139
  else
@@ -135,7 +145,7 @@ module Yerba
135
145
  #
136
146
  # When +key+ is given and the map is connected to a document, deletes that key
137
147
  # from the map in the YAML document. When no +key+ is given and the map is
138
- # connected, removes the entire map node at its selector path. When not connected
148
+ # connected, removes the entire map node at its selector. When not connected
139
149
  # to a document, delegates to the underlying Hash.
140
150
  #
141
151
  # Returns +self+.
data/lib/yerba/node.rb CHANGED
@@ -1,7 +1,31 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Yerba
4
+ module NodeTree
5
+ protected
6
+
7
+ def tree_node?
8
+ !@tree.nil? && @tree.equal?(document.nodes)
9
+ end
10
+
11
+ def tree_children
12
+ @children
13
+ end
14
+
15
+ def valueless?
16
+ @valueless == true
17
+ end
18
+
19
+ def attach_tree(tree, children, valueless)
20
+ @tree = tree
21
+ @children = children
22
+ @valueless = valueless
23
+ end
24
+ end
25
+
4
26
  module Node
27
+ include NodeTree
28
+
5
29
  attr_reader :selector, :location, :key
6
30
 
7
31
  def file_path
@@ -26,6 +50,17 @@ module Yerba
26
50
  document.source(@selector)
27
51
  end
28
52
 
53
+ def children
54
+ tree = current_children
55
+
56
+ case tree
57
+ when Hash
58
+ tree.transform_values { |child| child&.dup }
59
+ when Array
60
+ tree.map { |child| child&.dup }
61
+ end
62
+ end
63
+
29
64
  module ClassMethods
30
65
  def from_document(document, selector, location = nil, key = nil, **attributes)
31
66
  instance = allocate
@@ -58,6 +93,12 @@ module Yerba
58
93
 
59
94
  private
60
95
 
96
+ def current_children
97
+ node = tree_node? ? self : document&.[](@selector)
98
+
99
+ node.tree_children if node.is_a?(Node) && node.tree_node?
100
+ end
101
+
61
102
  def init_node(document, selector, location, key, file_path, line)
62
103
  @document = document
63
104
  @selector = selector
@@ -4,47 +4,70 @@ module Yerba
4
4
  class QueryResult
5
5
  include Enumerable
6
6
 
7
- def initialize(sequence, indices)
7
+ def initialize(sequence, indices, &query)
8
8
  @sequence = sequence
9
9
  @indices = indices
10
+ @query = query
11
+ @tree = current_tree
10
12
  end
11
13
 
12
14
  def each
13
15
  return enum_for(:each) unless block_given?
14
16
 
15
- @indices.each { |index| yield @sequence[index] }
17
+ current_indices.each { |index| yield @sequence[index] }
16
18
  end
17
19
 
18
20
  def [](position)
19
- index = @indices[position]
21
+ index = current_indices[position]
20
22
 
21
23
  @sequence[index] if index
22
24
  end
23
25
 
24
- def first
25
- self[0]
26
+ def first(count = nil)
27
+ return self[0] if count.nil?
28
+
29
+ current_indices.first(count).map { |index| @sequence[index] }
26
30
  end
27
31
 
28
- def last
29
- self[-1]
32
+ def last(count = nil)
33
+ return self[-1] if count.nil?
34
+
35
+ current_indices.last(count).map { |index| @sequence[index] }
30
36
  end
31
37
 
32
38
  def length
33
- @indices.length
39
+ current_indices.length
34
40
  end
35
41
  alias size length
36
42
  alias count length
37
43
 
38
44
  def empty?
39
- @indices.empty?
45
+ current_indices.empty?
40
46
  end
41
47
 
42
48
  def indices
43
- @indices.dup
49
+ current_indices.dup
44
50
  end
45
51
 
46
52
  def inspect
47
53
  "#<Yerba::QueryResult length=#{length}>"
48
54
  end
55
+
56
+ private
57
+
58
+ def current_indices
59
+ tree = current_tree
60
+
61
+ if @query && !tree.equal?(@tree)
62
+ @indices = @query.call
63
+ @tree = tree
64
+ end
65
+
66
+ @indices
67
+ end
68
+
69
+ def current_tree
70
+ @sequence&.document&.nodes
71
+ end
49
72
  end
50
73
  end
data/lib/yerba/scalar.rb CHANGED
@@ -12,6 +12,11 @@ module Yerba
12
12
  end
13
13
 
14
14
  def value
15
+ live = live_document if @selector
16
+ adopt_current_node(live) if live && !tree_node?
17
+
18
+ return @value.is_a?(String) ? @value.dup : @value if tree_node?
19
+
15
20
  @value ||= document&.value_at(@selector)
16
21
  end
17
22
 
@@ -72,8 +77,32 @@ module Yerba
72
77
  end
73
78
  end
74
79
 
80
+ protected
81
+
82
+ def stored_value
83
+ @value
84
+ end
85
+
75
86
  private
76
87
 
88
+ def live_document
89
+ return @document if @document
90
+
91
+ document if @file_path && Document.cache.key?(@file_path)
92
+ end
93
+
94
+ def adopt_current_node(live)
95
+ node = live[@selector]
96
+
97
+ if node.is_a?(Scalar) && node.tree_node?
98
+ @value = node.stored_value
99
+ attach_tree(live.nodes, nil, node.valueless?)
100
+ else
101
+ @value = nil
102
+ @tree = nil
103
+ end
104
+ end
105
+
77
106
  def init_from(value: nil, quote_style: nil, **)
78
107
  @value = value
79
108
  @quote_style = quote_style
@@ -10,11 +10,12 @@ module Yerba
10
10
  # Yerba::Selector.parse("database.*").segments # => ["database", :all_keys]
11
11
  # Yerba::Selector.parse(".title").relative? # => true
12
12
  #
13
- # Keys are Strings, indexes Integers, `[]` is `:all_items` and `*` is `:all_keys`.
13
+ # Keys are Strings, indexes Integers (negative from the end), `[]` is `:all_items` and `*` is `:all_keys`.
14
14
  #
15
15
  class Selector
16
16
  ALL_KEYS = :all_keys
17
17
  ALL_ITEMS = :all_items
18
+ CACHE_LIMIT = 10_000
18
19
 
19
20
  attr_reader :segments
20
21
 
@@ -24,6 +25,7 @@ module Yerba
24
25
  string = selector.to_s
25
26
 
26
27
  cache[string] ||= begin
28
+ cache.clear if cache.size >= CACHE_LIMIT
27
29
  parsed = native_parse(string)
28
30
 
29
31
  new(parsed["segments"].map { |segment| decode(segment) }, relative: parsed["relative"], source: string)
@@ -45,6 +45,9 @@ module Yerba
45
45
 
46
46
  def value_at(index)
47
47
  if connected?
48
+ child = current_children&.[](index) if index.is_a?(Integer) && !index.negative?
49
+ return child.value if child.is_a?(Scalar)
50
+
48
51
  new_path = "#{@selector}[#{index}]"
49
52
  document.value_at(new_path)
50
53
  else
@@ -73,15 +76,15 @@ module Yerba
73
76
 
74
77
  def concat(items)
75
78
  if connected?
76
- hashes = items.map do |item|
79
+ values = items.map do |item|
77
80
  case item
78
81
  when Map then item.to_hash
79
- when Hash then item
80
- else { value: item.to_s }
82
+ when Scalar then item.value
83
+ else item
81
84
  end
82
85
  end
83
86
 
84
- document.insert_objects(@selector, hashes)
87
+ document.insert_objects(@selector, values)
85
88
  else
86
89
  @data.concat(items)
87
90
  end
@@ -92,6 +95,12 @@ module Yerba
92
95
  def each(&)
93
96
  return enum_for(:each) unless block_given?
94
97
 
98
+ if connected? && (tree = current_children)
99
+ tree.each { |child| yield child&.dup }
100
+
101
+ return self
102
+ end
103
+
95
104
  if connected?
96
105
  items = document.get_all("#{@selector}[]")
97
106
 
@@ -114,7 +123,7 @@ module Yerba
114
123
  end
115
124
 
116
125
  def where(selector = nil, value = nil, **criteria)
117
- QueryResult.new(self, indices_of(selector, value, **criteria))
126
+ QueryResult.new(self, indices_of(selector, value, **criteria)) { indices_of(selector, value, **criteria) }
118
127
  end
119
128
 
120
129
  def pluck(*fields)
@@ -211,28 +220,24 @@ module Yerba
211
220
  end
212
221
 
213
222
  def length
214
- if connected?
215
- scalar_items = document.value_at("#{@selector}[]")
223
+ return @data.length unless connected?
216
224
 
217
- if scalar_items.is_a?(Array) && !scalar_items.empty?
218
- scalar_items.length
219
- else
220
- data = document.value_at(@selector)
221
-
222
- data.is_a?(Array) ? data.length : 0
223
- end
224
- else
225
- @data.length
226
- end
225
+ current_children&.size || document.length_at(@selector)
227
226
  end
228
227
  alias size length
229
228
 
230
- def first
231
- self[0]
229
+ def first(count = nil)
230
+ return self[0] if count.nil?
231
+
232
+ super
232
233
  end
233
234
 
234
- def last
235
- self[length - 1] # rubocop:disable Style/NegativeArrayIndex
235
+ def last(count = nil)
236
+ size = length
237
+ return self[size - 1] if count.nil?
238
+ raise ArgumentError, "negative array size" if count.negative?
239
+
240
+ ([size - count, 0].max...size).map { |index| self[index] }
236
241
  end
237
242
 
238
243
  def remove(value)
@@ -263,15 +268,15 @@ module Yerba
263
268
  self
264
269
  end
265
270
 
266
- def sort(by: nil, case_sensitive: false)
267
- document&.sort(@selector, by: by, case_sensitive: case_sensitive)
271
+ def sort(by: nil, order: nil, case_sensitive: false)
272
+ document&.sort(@selector, by: by, order: order, case_sensitive: case_sensitive)
268
273
 
269
274
  self
270
275
  end
271
276
 
272
277
  # Deletes the entire sequence node from the YAML document.
273
278
  #
274
- # Removes the sequence at its selector path from the parent document.
279
+ # Removes the sequence at its selector from the parent document.
275
280
  # Has no effect if the sequence is not connected to a document.
276
281
  #
277
282
  # Returns the document, or +nil+ if the sequence is not connected to a document.
@@ -345,8 +350,8 @@ module Yerba
345
350
 
346
351
  private
347
352
 
348
- def dig_into(hash, path)
349
- keys = path.split(".")
353
+ def dig_into(hash, selector)
354
+ keys = selector.split(".")
350
355
 
351
356
  keys.reduce(hash) { |current, key| current.is_a?(Hash) ? current[key] : nil }
352
357
  end
@@ -356,8 +361,8 @@ module Yerba
356
361
 
357
362
  criteria.each do |field, value|
358
363
  if value.is_a?(Hash)
359
- flatten_hash("#{field}[]", value).each do |path, leaf_value|
360
- expanded << [path, leaf_value]
364
+ flatten_hash("#{field}[]", value).each do |selector, leaf_value|
365
+ expanded << [selector, leaf_value]
361
366
  end
362
367
  elsif value.is_a?(Array)
363
368
  value.each do |item|
@@ -375,12 +380,12 @@ module Yerba
375
380
  result = {}
376
381
 
377
382
  hash.each do |key, value|
378
- path = "#{prefix}.#{key}"
383
+ selector = "#{prefix}.#{key}"
379
384
 
380
385
  if value.is_a?(Hash)
381
- flatten_hash("#{path}[]", value).each { |nested_path, leaf| result[nested_path] = leaf }
386
+ flatten_hash("#{selector}[]", value).each { |nested_selector, leaf| result[nested_selector] = leaf }
382
387
  else
383
- result[path] = value
388
+ result[selector] = value
384
389
  end
385
390
  end
386
391
 
@@ -399,8 +404,8 @@ module Yerba
399
404
  end
400
405
  end
401
406
 
402
- def dig_values(hash, path)
403
- parts = path.split(".")
407
+ def dig_values(hash, selector)
408
+ parts = selector.split(".")
404
409
  current = [hash]
405
410
 
406
411
  parts.each do |part|
data/lib/yerba/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Yerba
4
- VERSION = "0.9.2"
4
+ VERSION = "0.10.0"
5
5
  end
data/rust/Cargo.lock CHANGED
@@ -1431,7 +1431,7 @@ dependencies = [
1431
1431
 
1432
1432
  [[package]]
1433
1433
  name = "yerba"
1434
- version = "0.9.2"
1434
+ version = "0.10.0"
1435
1435
  dependencies = [
1436
1436
  "anstyle-query",
1437
1437
  "cbindgen",
data/rust/Cargo.toml CHANGED
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "yerba"
3
- version = "0.9.2"
3
+ version = "0.10.0"
4
4
  edition = "2021"
5
5
  authors = ["Marco Roth <marco.roth@intergga.ch>"]
6
6
  description = "YAML Editing and Refactoring with Better Accuracy"