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.
Files changed (56) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE.txt +21 -0
  3. data/README.md +528 -0
  4. data/exe/yerba +6 -0
  5. data/ext/yerba/extconf.rb +111 -0
  6. data/ext/yerba/yerba.c +752 -0
  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 +31 -0
  12. data/lib/yerba/document.rb +59 -0
  13. data/lib/yerba/formatting.rb +18 -0
  14. data/lib/yerba/location.rb +5 -0
  15. data/lib/yerba/map.rb +166 -0
  16. data/lib/yerba/scalar.rb +85 -0
  17. data/lib/yerba/sequence.rb +182 -0
  18. data/lib/yerba/version.rb +5 -0
  19. data/lib/yerba.rb +131 -0
  20. data/rust/Cargo.lock +805 -0
  21. data/rust/Cargo.toml +36 -0
  22. data/rust/build.rs +11 -0
  23. data/rust/cbindgen.toml +27 -0
  24. data/rust/rustfmt.toml +2 -0
  25. data/rust/src/commands/apply.rs +5 -0
  26. data/rust/src/commands/blank_lines.rs +58 -0
  27. data/rust/src/commands/check.rs +5 -0
  28. data/rust/src/commands/delete.rs +35 -0
  29. data/rust/src/commands/get.rs +194 -0
  30. data/rust/src/commands/init.rs +89 -0
  31. data/rust/src/commands/insert.rs +106 -0
  32. data/rust/src/commands/mate.rs +55 -0
  33. data/rust/src/commands/mod.rs +349 -0
  34. data/rust/src/commands/move_item.rs +54 -0
  35. data/rust/src/commands/move_key.rs +87 -0
  36. data/rust/src/commands/quote_style.rs +62 -0
  37. data/rust/src/commands/remove.rs +35 -0
  38. data/rust/src/commands/rename.rs +37 -0
  39. data/rust/src/commands/set.rs +59 -0
  40. data/rust/src/commands/sort.rs +52 -0
  41. data/rust/src/commands/sort_keys.rs +62 -0
  42. data/rust/src/commands/version.rs +8 -0
  43. data/rust/src/document.rs +2237 -0
  44. data/rust/src/error.rs +45 -0
  45. data/rust/src/ffi.rs +991 -0
  46. data/rust/src/json.rs +128 -0
  47. data/rust/src/lib.rs +29 -0
  48. data/rust/src/main.rs +72 -0
  49. data/rust/src/quote_style.rs +42 -0
  50. data/rust/src/selector.rs +241 -0
  51. data/rust/src/syntax.rs +262 -0
  52. data/rust/src/yaml_writer.rs +89 -0
  53. data/rust/src/yerbafile.rs +475 -0
  54. data/sig/yerba.rbs +3 -0
  55. data/yerba.gemspec +52 -0
  56. metadata +108 -0
Binary file
Binary file
Binary file
Binary file
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Yerba
4
+ class Collection
5
+ include Enumerable
6
+
7
+ def initialize(glob)
8
+ @glob = glob
9
+ end
10
+
11
+ def each
12
+ Dir.glob(@glob).each { |path| yield Document.new(path) }
13
+ end
14
+
15
+ def get(path)
16
+ self.class.get(@glob, path)
17
+ end
18
+
19
+ def find(path, condition: nil, select: nil)
20
+ self.class.find(@glob, path, condition: condition, select: select)
21
+ end
22
+
23
+ def apply!
24
+ each do |document|
25
+ yield document
26
+
27
+ document.save!
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Yerba
4
+ class Document
5
+ def root
6
+ self[""]
7
+ end
8
+
9
+ def map?
10
+ root.is_a?(Map)
11
+ end
12
+
13
+ def sequence?
14
+ root.is_a?(Sequence)
15
+ end
16
+
17
+ def to_h
18
+ get_value("")
19
+ end
20
+
21
+ def to_a
22
+ get_value("")
23
+ end
24
+
25
+ def to_yaml
26
+ to_s
27
+ end
28
+
29
+ def dig(*keys)
30
+ result = keys.reduce(self) { |node, key| node.nil? ? nil : node[key] }
31
+
32
+ result&.value
33
+ end
34
+
35
+ def at_path(path)
36
+ if path.include?("[]")
37
+ values = get(path)
38
+ return [] unless values.is_a?(Array)
39
+
40
+ path.sub("[]", "")
41
+
42
+ values.each_with_index.map do |_value, index|
43
+ resolved_path = path.sub("[]", "[#{index}]")
44
+ self[resolved_path]
45
+ end
46
+ else
47
+ self[path]
48
+ end
49
+ end
50
+
51
+ def inspect
52
+ if path
53
+ "#<Yerba::Document path=#{path.inspect}>"
54
+ else
55
+ "#<Yerba::Document (parsed)>"
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Yerba
4
+ module Formatting
5
+ def self.quote(value, style)
6
+ case style
7
+ when :double
8
+ escaped = value.to_s.gsub("\\", "\\\\").gsub('"', '\\"')
9
+ "\"#{escaped}\""
10
+ when :single
11
+ escaped = value.to_s.gsub("'", "''")
12
+ "'#{escaped}'"
13
+ else
14
+ value.to_s
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Yerba
4
+ Location = Data.define(:start_line, :start_column, :end_line, :end_column, :start_offset, :end_offset)
5
+ end
data/lib/yerba/map.rb ADDED
@@ -0,0 +1,166 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Yerba
4
+ class Map
5
+ include Enumerable
6
+
7
+ attr_reader :selector, :location, :key
8
+
9
+ def initialize(document_or_hash = nil, selector = nil, location = nil, key = nil)
10
+ if document_or_hash.is_a?(Document)
11
+ @document = document_or_hash
12
+ @selector = selector
13
+ @location = location
14
+ @key = key
15
+ @data = nil
16
+ elsif document_or_hash.is_a?(Hash)
17
+ @document = nil
18
+ @selector = nil
19
+ @location = nil
20
+ @data = document_or_hash
21
+ else
22
+ @document = nil
23
+ @selector = nil
24
+ @location = nil
25
+ @data = {}
26
+ end
27
+ end
28
+
29
+ def [](key)
30
+ if @document
31
+ new_path = @selector.empty? ? key.to_s : "#{@selector}.#{key}"
32
+ @document[new_path]
33
+ else
34
+ @data[key]
35
+ end
36
+ end
37
+
38
+ def []=(key, value)
39
+ if @document
40
+ new_path = @selector.empty? ? key.to_s : "#{@selector}.#{key}"
41
+ @document.set(new_path, value)
42
+ else
43
+ @data[key] = value
44
+ end
45
+ end
46
+
47
+ def insert(key, value, before: nil, after: nil)
48
+ if @document
49
+ new_path = @selector.empty? ? key.to_s : "#{@selector}.#{key}"
50
+ @document.insert(new_path, value.to_s, before: before, after: after)
51
+ else
52
+ @data[key] = value
53
+ end
54
+
55
+ self
56
+ end
57
+
58
+ def sort_keys(order)
59
+ @document&.sort_keys(@selector, order)
60
+
61
+ self
62
+ end
63
+
64
+ def keys
65
+ if @document
66
+ results = @document.find(@selector)
67
+ return [] unless results.is_a?(Array) && results.first.is_a?(Hash)
68
+
69
+ results.first.keys
70
+ else
71
+ @data.keys
72
+ end
73
+ end
74
+
75
+ def each(&)
76
+ return enum_for(:each) unless block_given?
77
+
78
+ if @document
79
+ keys.each { |key| yield key, self[key] }
80
+ else
81
+ @data.each(&)
82
+ end
83
+ end
84
+
85
+ def dig(*keys)
86
+ if @document
87
+ result = keys.reduce(self) { |node, key| node.nil? ? nil : node[key] }
88
+ result&.value
89
+ else
90
+ @data.dig(*keys)
91
+ end
92
+ end
93
+
94
+ def delete(key = nil)
95
+ if key && @document
96
+ new_path = @selector.empty? ? key.to_s : "#{@selector}.#{key}"
97
+ @document.delete(new_path)
98
+ elsif @document
99
+ @document.delete(@selector)
100
+ else
101
+ @data.delete(key)
102
+ end
103
+
104
+ self
105
+ end
106
+
107
+ def key?(key)
108
+ if @document
109
+ new_path = @selector.empty? ? key.to_s : "#{@selector}.#{key}"
110
+ @document.exists?(new_path)
111
+ else
112
+ @data.key?(key)
113
+ end
114
+ end
115
+ alias has_key? key?
116
+ alias include? key?
117
+
118
+ def value
119
+ @data || nil
120
+ end
121
+
122
+ def to_h
123
+ if @document
124
+ results = @document.find(@selector)
125
+ results&.first || {}
126
+ else
127
+ @data
128
+ end
129
+ end
130
+ alias to_hash to_h
131
+
132
+ def to_yaml
133
+ to_hash.map do |key, val|
134
+ formatted = format_value(val)
135
+ "#{key}: #{formatted}"
136
+ end.join("\n")
137
+ end
138
+
139
+ def inspect
140
+ if @document
141
+ results = @document.find(@selector)
142
+
143
+ if results.is_a?(Array) && !results.empty? && results.first.is_a?(Hash)
144
+ map_keys = results.first.keys.first(5)
145
+ preview = map_keys.map { |key| "#{key}: #{results.first[key].inspect}" }.join(", ")
146
+
147
+ "#<Yerba::Map selector=#{@selector.inspect} {#{preview}}>"
148
+ else
149
+ "#<Yerba::Map selector=#{@selector.inspect}>"
150
+ end
151
+ else
152
+ "#<Yerba::Map {#{@data.map { |key, value| "#{key}: #{value.inspect}" }.join(", ")}}>"
153
+ end
154
+ end
155
+
156
+ private
157
+
158
+ def format_value(value)
159
+ case value
160
+ when Scalar then value.to_yaml
161
+ when nil then "null"
162
+ else value.to_s
163
+ end
164
+ end
165
+ end
166
+ end
@@ -0,0 +1,85 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Yerba
4
+ class Scalar
5
+ attr_reader :selector, :location, :key
6
+
7
+ def initialize(document_or_value, selector_or_opts = nil, value = nil, location = nil, key = nil, quote_style: nil)
8
+ if document_or_value.is_a?(Document) || document_or_value.nil?
9
+ @document = document_or_value
10
+ @selector = selector_or_opts
11
+ @value = value
12
+ @location = location
13
+ @key = key
14
+ @quote_style = quote_style
15
+ else
16
+ @document = nil
17
+ @selector = nil
18
+ @value = document_or_value
19
+ @location = nil
20
+ @key = nil
21
+ @quote_style = selector_or_opts.is_a?(Hash) ? selector_or_opts[:quote_style] : quote_style
22
+ end
23
+ end
24
+
25
+ def value
26
+ @value ||= @document&.get(@selector)
27
+ end
28
+
29
+ def quote_style
30
+ @quote_style || @document&.get_quote_style(@selector)
31
+ end
32
+
33
+ def quote_style=(style)
34
+ @document&.set_quote_style(@selector, style)
35
+
36
+ @quote_style = style
37
+ end
38
+
39
+ def value=(new_value)
40
+ @document&.set(@selector, new_value)
41
+ @value = new_value
42
+ end
43
+ alias set value=
44
+
45
+ def to_s
46
+ value.to_s
47
+ end
48
+
49
+ def to_str
50
+ to_s
51
+ end
52
+
53
+ def to_i
54
+ value.to_i
55
+ end
56
+
57
+ def to_f
58
+ value.to_f
59
+ end
60
+
61
+ def ==(other)
62
+ value == other
63
+ end
64
+
65
+ def to_yaml
66
+ case value
67
+ when nil then "null"
68
+ when String then Formatting.quote(value, quote_style)
69
+ else value.to_s
70
+ end
71
+ end
72
+
73
+ def delete
74
+ @document&.delete(@selector)
75
+ end
76
+
77
+ def inspect
78
+ if @selector
79
+ "#<Yerba::Scalar selector=#{@selector.inspect} value=#{value.inspect}>"
80
+ else
81
+ "#<Yerba::Scalar value=#{value.inspect} quote_style=#{quote_style.inspect}>"
82
+ end
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,182 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Yerba
4
+ class Sequence
5
+ include Enumerable
6
+
7
+ attr_reader :selector, :location, :key
8
+
9
+ def initialize(document_or_array = nil, selector = nil, location = nil, key = nil)
10
+ if document_or_array.is_a?(Document)
11
+ @document = document_or_array
12
+ @selector = selector
13
+ @location = location
14
+ @key = key
15
+ @data = nil
16
+ elsif document_or_array.is_a?(Array)
17
+ @document = nil
18
+ @selector = nil
19
+ @location = nil
20
+ @data = document_or_array
21
+ else
22
+ @document = nil
23
+ @selector = nil
24
+ @location = nil
25
+ @data = []
26
+ end
27
+ end
28
+
29
+ def [](index)
30
+ if @document
31
+ new_path = "#{@selector}[#{index}]"
32
+ @document[new_path]
33
+ else
34
+ @data[index]
35
+ end
36
+ end
37
+
38
+ def <<(item)
39
+ if @document
40
+ case item
41
+ when Map
42
+ @document.insert_object(@selector, item.to_hash)
43
+ when Hash
44
+ @document.insert_object(@selector, item)
45
+ when Scalar
46
+ @document.insert(@selector, item.to_yaml)
47
+ else
48
+ formatted = format_for_insert(item.to_s)
49
+ @document.insert(@selector, formatted)
50
+ end
51
+ else
52
+ @data << item
53
+ end
54
+
55
+ self
56
+ end
57
+
58
+ def each
59
+ return enum_for(:each) unless block_given?
60
+
61
+ length.times { |i| yield self[i] }
62
+ end
63
+
64
+ def length
65
+ if @document
66
+ scalar_items = @document.get("#{@selector}[]")
67
+
68
+ if scalar_items.is_a?(Array) && !scalar_items.empty?
69
+ scalar_items.length
70
+ else
71
+ data = @document.get_value(@selector)
72
+ data.is_a?(Array) ? data.length : 0
73
+ end
74
+ else
75
+ @data.length
76
+ end
77
+ end
78
+ alias size length
79
+
80
+ def first
81
+ self[0]
82
+ end
83
+
84
+ def last
85
+ self[length - 1] # rubocop:disable Style/NegativeArrayIndex
86
+ end
87
+
88
+ def remove(value)
89
+ @document&.remove(@selector, value.to_s)
90
+
91
+ self
92
+ end
93
+
94
+ def delete_at(index)
95
+ @document&.remove_at(@selector, index)
96
+
97
+ self
98
+ end
99
+
100
+ def delete_if
101
+ return enum_for(:delete_if) unless block_given?
102
+
103
+ indices_to_remove = []
104
+
105
+ length.times do |index|
106
+ indices_to_remove << index if yield self[index]
107
+ end
108
+
109
+ indices_to_remove.reverse_each do |index|
110
+ @document&.remove_at(@selector, index)
111
+ end
112
+
113
+ self
114
+ end
115
+
116
+ def sort(by: nil, case_sensitive: false)
117
+ @document&.sort(@selector, by: by, case_sensitive: case_sensitive)
118
+
119
+ self
120
+ end
121
+
122
+ def delete
123
+ @document&.delete(@selector)
124
+ end
125
+
126
+ def value
127
+ items
128
+ end
129
+
130
+ def to_a
131
+ @data || items
132
+ end
133
+ alias to_ary to_a
134
+
135
+ def to_yaml
136
+ items.map do |item|
137
+ case item
138
+ when Scalar then "- #{item.to_yaml}"
139
+ when Map then "- #{item.to_yaml.gsub("\n", "\n ")}"
140
+ else "- #{item}"
141
+ end
142
+ end.join("\n")
143
+ end
144
+
145
+ def inspect
146
+ list = items
147
+ preview = list.first(5).map(&:inspect).join(", ")
148
+ suffix = list.length > 5 ? ", ... (#{list.length} items)" : ""
149
+
150
+ if @selector
151
+ "#<Yerba::Sequence selector=#{@selector.inspect} [#{preview}#{suffix}]>"
152
+ else
153
+ "#<Yerba::Sequence [#{preview}#{suffix}]>"
154
+ end
155
+ end
156
+
157
+ private
158
+
159
+ def format_for_insert(value)
160
+ Formatting.quote(value, detect_quote_style)
161
+ end
162
+
163
+ def detect_quote_style
164
+ @document.get_quote_style("#{@selector}[0]")
165
+ end
166
+
167
+ def items
168
+ if @document
169
+ result = @document.get("#{@selector}[]")
170
+
171
+ if result.is_a?(Array) && !result.empty?
172
+ result
173
+ else
174
+ data = @document.get_value(@selector)
175
+ data.is_a?(Array) ? data : []
176
+ end
177
+ else
178
+ @data
179
+ end
180
+ end
181
+ end
182
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Yerba
4
+ VERSION = "0.2.0"
5
+ end
data/lib/yerba.rb ADDED
@@ -0,0 +1,131 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "yerba/version"
4
+ require_relative "yerba/location"
5
+ require_relative "yerba/formatting"
6
+ require_relative "yerba/scalar"
7
+ require_relative "yerba/map"
8
+ require_relative "yerba/sequence"
9
+ require_relative "yerba/document"
10
+ require_relative "yerba/collection"
11
+
12
+ begin
13
+ require "yerba/yerba"
14
+ rescue LoadError
15
+ # C extension not available, fall back to CLI mode
16
+ end
17
+
18
+ module Yerba
19
+ class UnsupportedPlatformError < StandardError; end
20
+ class ExecutableNotFoundError < StandardError; end
21
+ class CompilationError < StandardError; end
22
+
23
+ GEM_NAME = "yerba"
24
+ EXECUTABLE_NAME = "yerba"
25
+
26
+ NATIVE_PLATFORMS = {
27
+ "arm64-darwin" => "arm64-darwin",
28
+ "x86_64-darwin" => "x86_64-darwin",
29
+ "aarch64-linux" => "aarch64-linux",
30
+ "arm64-linux" => "aarch64-linux",
31
+ "x86_64-linux" => "x86_64-linux",
32
+ }.freeze
33
+
34
+ def self.executable(exe_path: nil)
35
+ if exe_path
36
+ return exe_path if File.executable?(exe_path)
37
+
38
+ raise ExecutableNotFoundError, "yerba executable not found at #{exe_path}"
39
+ end
40
+
41
+ if ENV["YERBA_INSTALL_DIR"]
42
+ install_dir_exe = File.join(ENV["YERBA_INSTALL_DIR"], EXECUTABLE_NAME)
43
+
44
+ if File.executable?(install_dir_exe)
45
+ return install_dir_exe
46
+ end
47
+
48
+ raise ExecutableNotFoundError, "yerba executable not found at #{install_dir_exe} (set by YERBA_INSTALL_DIR)"
49
+ end
50
+
51
+ platform = Gem::Platform.local
52
+ platform_key = "#{platform.cpu}-#{platform.os}"
53
+
54
+ exe_directory = NATIVE_PLATFORMS[platform_key]
55
+
56
+ if exe_directory
57
+ exe_file = File.expand_path(
58
+ File.join("..", "exe", exe_directory, EXECUTABLE_NAME),
59
+ __dir__
60
+ )
61
+
62
+ return exe_file if File.executable?(exe_file)
63
+ end
64
+
65
+ compiled = compile_from_source
66
+
67
+ return compiled if compiled
68
+
69
+ if exe_directory
70
+ raise ExecutableNotFoundError,
71
+ "yerba executable not found at exe/#{exe_directory}/yerba. " \
72
+ "Try reinstalling the gem: gem install yerba"
73
+ else
74
+ raise UnsupportedPlatformError,
75
+ "yerba does not have a precompiled binary for #{platform_key}. " \
76
+ "Install Rust (https://rustup.rs) and reinstall the gem to compile from source, " \
77
+ "or set YERBA_INSTALL_DIR to use a custom binary."
78
+ end
79
+ end
80
+
81
+ def self.compile_from_source
82
+ rust_dir = File.expand_path(File.join("..", "rust"), __dir__)
83
+
84
+ return nil unless File.exist?(File.join(rust_dir, "Cargo.toml"))
85
+ return nil unless system("cargo --version > /dev/null 2>&1")
86
+
87
+ platform = Gem::Platform.local
88
+ platform_key = "#{platform.cpu}-#{platform.os}"
89
+ exe_directory = File.join(File.expand_path(File.join("..", "exe", platform_key), __dir__))
90
+ exe_file = File.join(exe_directory, EXECUTABLE_NAME)
91
+
92
+ return exe_file if File.executable?(exe_file)
93
+
94
+ warn "yerba: No precompiled binary found. Compiling from source..."
95
+
96
+ FileUtils.mkdir_p(exe_directory)
97
+
98
+ unless system("cd #{rust_dir} && cargo build --release")
99
+ raise CompilationError, "Failed to compile yerba from source. Is Rust installed?"
100
+ end
101
+
102
+ source_binary = File.join(rust_dir, "target", "release", EXECUTABLE_NAME)
103
+
104
+ unless File.exist?(source_binary)
105
+ raise CompilationError, "Compilation succeeded but binary not found at #{source_binary}"
106
+ end
107
+
108
+ FileUtils.cp(source_binary, exe_file)
109
+ FileUtils.chmod(0o755, exe_file)
110
+
111
+ exe_file
112
+ end
113
+
114
+ private_class_method :compile_from_source
115
+
116
+ def self.parse(content)
117
+ Document.parse(content)
118
+ end
119
+
120
+ def self.parse_file(path)
121
+ Document.new(path)
122
+ end
123
+
124
+ def self.document(path)
125
+ Document.new(path)
126
+ end
127
+
128
+ def self.files(glob)
129
+ Collection.new(glob)
130
+ end
131
+ end