yerba 0.9.1-arm-linux-gnu → 0.10.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.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +60 -16
  3. data/exe/arm-linux-gnu/yerba +0 -0
  4. data/ext/yerba/include/yerba.h +67 -32
  5. data/ext/yerba/yerba.c +296 -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 +142 -0
  19. data/lib/yerba/sequence.rb +38 -33
  20. data/lib/yerba/version.rb +1 -1
  21. data/lib/yerba.rb +1 -0
  22. data/rust/Cargo.lock +1 -1
  23. data/rust/Cargo.toml +1 -1
  24. data/rust/src/commands/get.rs +12 -12
  25. data/rust/src/commands/init.rs +8 -8
  26. data/rust/src/commands/insert.rs +4 -18
  27. data/rust/src/commands/location.rs +2 -0
  28. data/rust/src/commands/mod.rs +34 -6
  29. data/rust/src/commands/move_item.rs +1 -1
  30. data/rust/src/commands/move_key.rs +8 -8
  31. data/rust/src/commands/rename.rs +1 -1
  32. data/rust/src/commands/selectors.rs +1 -1
  33. data/rust/src/commands/set.rs +14 -6
  34. data/rust/src/document/condition.rs +25 -25
  35. data/rust/src/document/delete.rs +57 -43
  36. data/rust/src/document/get.rs +91 -82
  37. data/rust/src/document/insert.rs +77 -77
  38. data/rust/src/document/mod.rs +138 -64
  39. data/rust/src/document/nodes.rs +162 -0
  40. data/rust/src/document/schema.rs +8 -8
  41. data/rust/src/document/set.rs +49 -26
  42. data/rust/src/document/sort.rs +54 -54
  43. data/rust/src/document/style.rs +50 -49
  44. data/rust/src/document/unique.rs +5 -5
  45. data/rust/src/error.rs +8 -4
  46. data/rust/src/ffi.rs +272 -102
  47. data/rust/src/json.rs +62 -2
  48. data/rust/src/lib.rs +2 -1
  49. data/rust/src/main.rs +2 -1
  50. data/rust/src/selector.rs +48 -7
  51. data/rust/src/syntax.rs +449 -56
  52. data/rust/src/validation.rs +5 -3
  53. data/rust/src/yerbafile.rs +124 -76
  54. metadata +4 -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
@@ -0,0 +1,142 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+
5
+ module Yerba
6
+ # A parsed selector, split into its segments.
7
+ #
8
+ # Yerba::Selector.parse("[3].talks[1]").segments # => [3, "talks", 1]
9
+ # Yerba::Selector.parse("[].speakers[]").segments # => [:all_items, "speakers", :all_items]
10
+ # Yerba::Selector.parse("database.*").segments # => ["database", :all_keys]
11
+ # Yerba::Selector.parse(".title").relative? # => true
12
+ #
13
+ # Keys are Strings, indexes Integers (negative from the end), `[]` is `:all_items` and `*` is `:all_keys`.
14
+ #
15
+ class Selector
16
+ ALL_KEYS = :all_keys
17
+ ALL_ITEMS = :all_items
18
+ CACHE_LIMIT = 10_000
19
+
20
+ attr_reader :segments
21
+
22
+ def self.parse(selector)
23
+ return selector if selector.is_a?(Selector)
24
+
25
+ string = selector.to_s
26
+
27
+ cache[string] ||= begin
28
+ cache.clear if cache.size >= CACHE_LIMIT
29
+ parsed = native_parse(string)
30
+
31
+ new(parsed["segments"].map { |segment| decode(segment) }, relative: parsed["relative"], source: string)
32
+ end
33
+ end
34
+
35
+ # Builds a selector from segments: Strings (keys), Integers (indexes), `:all_items` or `:all_keys`.
36
+ #
37
+ # Yerba::Selector.build([3, "talks", 1]).to_s # => "[3].talks[1]"
38
+ #
39
+ def self.build(segments, relative: false)
40
+ new(segments, relative: relative)
41
+ end
42
+
43
+ def self.clear_cache!
44
+ @cache = nil
45
+ end
46
+
47
+ def self.cache
48
+ @cache ||= {}
49
+ end
50
+ private_class_method :cache
51
+
52
+ def self.decode(segment)
53
+ case segment
54
+ when Hash
55
+ segment["all"] == "keys" ? ALL_KEYS : ALL_ITEMS
56
+ else
57
+ segment
58
+ end
59
+ end
60
+ private_class_method :decode
61
+
62
+ def initialize(segments, relative: false, source: nil)
63
+ @segments = segments.dup.freeze
64
+ @relative = relative
65
+ @source = source
66
+
67
+ freeze
68
+ end
69
+
70
+ def relative?
71
+ @relative
72
+ end
73
+
74
+ def absolute?
75
+ !@relative
76
+ end
77
+
78
+ def empty?
79
+ segments.empty?
80
+ end
81
+
82
+ def wildcard?
83
+ segments.any? { |segment| [ALL_ITEMS, ALL_KEYS].include?(segment) }
84
+ end
85
+
86
+ # "[3].talks[1]" => "[3].talks"
87
+ def parent
88
+ self.class.build(segments[0...-1], relative: relative?)
89
+ end
90
+
91
+ # Selector.parse("[3]").join("talks", 1) => "[3].talks[1]"
92
+ def join(*more)
93
+ self.class.build(segments + more, relative: relative?)
94
+ end
95
+
96
+ def dig(values)
97
+ raise ArgumentError, "Cannot dig a wildcard selector (#{self}), use Document#find or Collection.find instead" if wildcard?
98
+
99
+ segments.reduce(values) do |current, segment|
100
+ case current
101
+ when Array
102
+ segment.is_a?(Integer) ? current[segment] : nil
103
+ when Hash
104
+ segment.is_a?(Integer) ? nil : current[segment]
105
+ end
106
+ end
107
+ end
108
+
109
+ def to_s
110
+ @source || self.class.send(:native_build, JSON.generate(encoded_segments), relative?)
111
+ end
112
+ alias to_str to_s
113
+
114
+ def ==(other)
115
+ other.is_a?(Selector) && other.segments == segments && other.relative? == relative?
116
+ end
117
+ alias eql? ==
118
+
119
+ def hash
120
+ [self.class, segments, relative?].hash
121
+ end
122
+
123
+ def inspect
124
+ "#<#{self.class.name} #{to_s.inspect} #{segments.inspect}>"
125
+ end
126
+
127
+ private
128
+
129
+ def encoded_segments
130
+ segments.map do |segment|
131
+ case segment
132
+ when ALL_ITEMS
133
+ { all: "items" }
134
+ when ALL_KEYS
135
+ { all: "keys" }
136
+ else
137
+ segment
138
+ end
139
+ end
140
+ end
141
+ end
142
+ end