toml-merge 2.0.1 → 7.0.0
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 +4 -4
- checksums.yaml.gz.sig +0 -0
- data/lib/toml/merge/version.rb +3 -4
- data/lib/toml/merge.rb +411 -109
- data/lib/toml-merge.rb +3 -4
- data.tar.gz.sig +0 -0
- metadata +28 -336
- metadata.gz.sig +0 -0
- data/CHANGELOG.md +0 -262
- data/CITATION.cff +0 -20
- data/CODE_OF_CONDUCT.md +0 -134
- data/CONTRIBUTING.md +0 -248
- data/FUNDING.md +0 -74
- data/LICENSE.txt +0 -21
- data/README.md +0 -904
- data/REEK +0 -0
- data/RUBOCOP.md +0 -71
- data/SECURITY.md +0 -21
- data/lib/toml/merge/conflict_resolver.rb +0 -217
- data/lib/toml/merge/debug_logger.rb +0 -41
- data/lib/toml/merge/emitter.rb +0 -120
- data/lib/toml/merge/file_analysis.rb +0 -274
- data/lib/toml/merge/merge_result.rb +0 -141
- data/lib/toml/merge/node_type_normalizer.rb +0 -297
- data/lib/toml/merge/node_wrapper.rb +0 -582
- data/lib/toml/merge/smart_merger.rb +0 -174
- data/lib/toml/merge/table_match_refiner.rb +0 -240
- data/sig/toml/merge.rbs +0 -239
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 3acee846936049e12d2a86754efa4883e926acb0a27ff50088ab4b0f07b2136d
|
|
4
|
+
data.tar.gz: ad65f5a29dc6ffa0c683c5dd88d53f52eb1c9cd2109a1d69643ab9a8157a2c66
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: efcc0a2c04eed99bbcb29e13fb232812d3ce0f783214425b3e85fbc6c0a7eb265ce104459181b2615d4ca6503e02991fb8bf9cceac709cfd03b0384c5bc5d3e9
|
|
7
|
+
data.tar.gz: 903808d9d7df793cce2e67cb76c27ed8d04ec2e1248aae96a70362b973c1ebe26e1bb326d5a9c6333673a47e52f0c8b2c57f8487ea69d1b105a71b413adac9f3
|
checksums.yaml.gz.sig
CHANGED
|
Binary file
|
data/lib/toml/merge/version.rb
CHANGED
|
@@ -2,11 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
module Toml
|
|
4
4
|
module Merge
|
|
5
|
-
# Version information for Toml::Merge
|
|
6
5
|
module Version
|
|
7
|
-
|
|
8
|
-
VERSION = "2.0.1"
|
|
6
|
+
VERSION = "7.0.0"
|
|
9
7
|
end
|
|
10
|
-
|
|
8
|
+
|
|
9
|
+
VERSION = Version::VERSION
|
|
11
10
|
end
|
|
12
11
|
end
|
data/lib/toml/merge.rb
CHANGED
|
@@ -1,120 +1,422 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
require "set"
|
|
5
|
-
|
|
6
|
-
# External gems
|
|
7
|
-
# TreeHaver provides a unified cross-Ruby interface to tree-sitter.
|
|
8
|
-
# It handles grammar discovery, backend selection, Citrus and Parslet fallbacks, automatically
|
|
9
|
-
# via parser_for(:toml). No manual registration needed.
|
|
3
|
+
require "json"
|
|
10
4
|
require "tree_haver"
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
# Shared merge infrastructure
|
|
14
|
-
require "ast/merge"
|
|
15
|
-
|
|
16
|
-
# This gem
|
|
17
|
-
require_relative "merge/version"
|
|
18
|
-
|
|
19
|
-
# Toml::Merge provides a TOML file smart merge system using tree-sitter AST analysis.
|
|
20
|
-
# It intelligently merges template and destination TOML files by identifying matching
|
|
21
|
-
# keys and resolving differences using structural signatures.
|
|
22
|
-
#
|
|
23
|
-
# @example Basic usage
|
|
24
|
-
# template = File.read("template.toml")
|
|
25
|
-
# destination = File.read("destination.toml")
|
|
26
|
-
# merger = Toml::Merge::SmartMerger.new(template, destination)
|
|
27
|
-
# result = merger.merge
|
|
28
|
-
#
|
|
29
|
-
# @example With debug information
|
|
30
|
-
# merger = Toml::Merge::SmartMerger.new(template, destination)
|
|
31
|
-
# debug_result = merger.merge_with_debug
|
|
32
|
-
# puts debug_result[:content]
|
|
33
|
-
# puts debug_result[:statistics]
|
|
5
|
+
|
|
34
6
|
module Toml
|
|
35
|
-
# Smart merge system for TOML files using tree-sitter AST analysis.
|
|
36
|
-
# Provides intelligent merging by understanding TOML structure
|
|
37
|
-
# rather than treating files as plain text.
|
|
38
|
-
#
|
|
39
|
-
# @see SmartMerger Main entry point for merge operations
|
|
40
|
-
# @see FileAnalysis Analyzes TOML structure
|
|
41
|
-
# @see ConflictResolver Resolves content conflicts
|
|
42
7
|
module Merge
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
8
|
+
PACKAGE_NAME = "toml-merge"
|
|
9
|
+
DESTINATION_WINS_ARRAY_POLICY = {
|
|
10
|
+
surface: "array",
|
|
11
|
+
name: "destination_wins_array"
|
|
12
|
+
}.freeze
|
|
13
|
+
|
|
14
|
+
class ParseError < StandardError; end
|
|
15
|
+
|
|
16
|
+
module_function
|
|
17
|
+
|
|
18
|
+
def toml_feature_profile
|
|
19
|
+
{
|
|
20
|
+
family: "toml",
|
|
21
|
+
supported_dialects: ["toml"],
|
|
22
|
+
supported_policies: [DESTINATION_WINS_ARRAY_POLICY]
|
|
23
|
+
}
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def available_toml_backends
|
|
27
|
+
[TreeHaver::KREUZBERG_LANGUAGE_PACK_BACKEND]
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def toml_backend_feature_profile(backend: nil)
|
|
31
|
+
resolved_backend = resolve_backend(backend)
|
|
32
|
+
return unsupported_feature_result("Unsupported TOML backend #{resolved_backend}.") unless resolved_backend == TreeHaver::KREUZBERG_LANGUAGE_PACK_BACKEND.id
|
|
33
|
+
|
|
34
|
+
toml_feature_profile.merge(
|
|
35
|
+
backend: TreeHaver::KREUZBERG_LANGUAGE_PACK_BACKEND.id,
|
|
36
|
+
backend_ref: TreeHaver::KREUZBERG_LANGUAGE_PACK_BACKEND.to_h
|
|
37
|
+
)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def toml_plan_context(backend: nil)
|
|
41
|
+
profile = toml_backend_feature_profile(backend: backend)
|
|
42
|
+
return profile if profile[:ok] == false
|
|
43
|
+
|
|
44
|
+
{
|
|
45
|
+
family_profile: toml_feature_profile,
|
|
46
|
+
feature_profile: {
|
|
47
|
+
backend: profile[:backend],
|
|
48
|
+
supports_dialects: false,
|
|
49
|
+
supported_policies: profile[:supported_policies]
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def analyze_toml_source(source, dialect)
|
|
55
|
+
return unsupported_feature_result("Unsupported TOML dialect #{dialect}.") unless dialect == "toml"
|
|
56
|
+
|
|
57
|
+
parsed = parse_toml_document(source)
|
|
58
|
+
{
|
|
59
|
+
ok: true,
|
|
60
|
+
diagnostics: [],
|
|
61
|
+
analysis: {
|
|
62
|
+
kind: "toml",
|
|
63
|
+
dialect: "toml",
|
|
64
|
+
normalized_source: canonical_toml(parsed),
|
|
65
|
+
root_kind: "table",
|
|
66
|
+
owners: collect_toml_owners(parsed)
|
|
67
|
+
},
|
|
68
|
+
policies: []
|
|
69
|
+
}
|
|
70
|
+
rescue StandardError => e
|
|
71
|
+
parse_error_result(e.message)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def parse_toml(source, dialect, backend: nil)
|
|
75
|
+
return unsupported_feature_result("Unsupported TOML dialect #{dialect}.") unless dialect == "toml"
|
|
76
|
+
|
|
77
|
+
resolved_backend = resolve_backend(backend)
|
|
78
|
+
return unsupported_feature_result("Unsupported TOML backend #{resolved_backend}.") unless resolved_backend == TreeHaver::KREUZBERG_LANGUAGE_PACK_BACKEND.id
|
|
79
|
+
|
|
80
|
+
syntax_result = TreeHaver.parse_with_language_pack(
|
|
81
|
+
TreeHaver::ParserRequest.new(source: source, language: "toml", dialect: dialect)
|
|
82
|
+
)
|
|
83
|
+
return { ok: false, diagnostics: syntax_result[:diagnostics] } unless syntax_result[:ok]
|
|
84
|
+
|
|
85
|
+
analyze_toml_source(source, dialect)
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def match_toml_owners(template, destination)
|
|
89
|
+
destination_paths = destination[:owners].to_h { |owner| [owner[:path], true] }
|
|
90
|
+
template_paths = template[:owners].to_h { |owner| [owner[:path], true] }
|
|
91
|
+
|
|
92
|
+
{
|
|
93
|
+
matched: template[:owners]
|
|
94
|
+
.filter { |owner| destination_paths[owner[:path]] }
|
|
95
|
+
.map { |owner| { template_path: owner[:path], destination_path: owner[:path] } },
|
|
96
|
+
unmatched_template: template[:owners].map { |owner| owner[:path] }.reject { |path| destination_paths[path] },
|
|
97
|
+
unmatched_destination: destination[:owners].map { |owner| owner[:path] }.reject { |path| template_paths[path] }
|
|
98
|
+
}
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def merge_toml_with_parser(template_source, destination_source, dialect, &parser)
|
|
102
|
+
template = parser.call(template_source, dialect)
|
|
103
|
+
return { ok: false, diagnostics: template[:diagnostics], policies: [] } unless template[:ok]
|
|
104
|
+
|
|
105
|
+
destination = parser.call(destination_source, dialect)
|
|
106
|
+
unless destination[:ok]
|
|
107
|
+
return {
|
|
108
|
+
ok: false,
|
|
109
|
+
diagnostics: destination[:diagnostics].map do |diagnostic|
|
|
110
|
+
diagnostic[:category] == "parse_error" ? diagnostic.merge(category: "destination_parse_error") : diagnostic
|
|
111
|
+
end,
|
|
112
|
+
policies: []
|
|
113
|
+
}
|
|
63
114
|
end
|
|
115
|
+
|
|
116
|
+
merged = merge_toml_tables(
|
|
117
|
+
parse_toml_document(template.dig(:analysis, :normalized_source)),
|
|
118
|
+
parse_toml_document(destination.dig(:analysis, :normalized_source))
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
{
|
|
122
|
+
ok: true,
|
|
123
|
+
diagnostics: [],
|
|
124
|
+
output: canonical_toml(merged),
|
|
125
|
+
policies: [DESTINATION_WINS_ARRAY_POLICY]
|
|
126
|
+
}
|
|
127
|
+
rescue StandardError => e
|
|
128
|
+
{
|
|
129
|
+
ok: false,
|
|
130
|
+
diagnostics: [{ severity: "error", category: "destination_parse_error", message: e.message }],
|
|
131
|
+
policies: []
|
|
132
|
+
}
|
|
64
133
|
end
|
|
65
134
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
# begin
|
|
70
|
-
# merger = SmartMerger.new(template, destination)
|
|
71
|
-
# result = merger.merge
|
|
72
|
-
# rescue TemplateParseError => e
|
|
73
|
-
# puts "Template syntax error: #{e.message}"
|
|
74
|
-
# e.errors.each do |error|
|
|
75
|
-
# puts " #{error.message}"
|
|
76
|
-
# end
|
|
77
|
-
# end
|
|
78
|
-
class TemplateParseError < ParseError; end
|
|
79
|
-
|
|
80
|
-
# Raised when the destination file has syntax errors.
|
|
81
|
-
#
|
|
82
|
-
# @example Handling destination parse errors
|
|
83
|
-
# begin
|
|
84
|
-
# merger = SmartMerger.new(template, destination)
|
|
85
|
-
# result = merger.merge
|
|
86
|
-
# rescue DestinationParseError => e
|
|
87
|
-
# puts "Destination syntax error: #{e.message}"
|
|
88
|
-
# e.errors.each do |error|
|
|
89
|
-
# puts " #{error.message}"
|
|
90
|
-
# end
|
|
91
|
-
# end
|
|
92
|
-
class DestinationParseError < ParseError; end
|
|
93
|
-
|
|
94
|
-
autoload :DebugLogger, "toml/merge/debug_logger"
|
|
95
|
-
autoload :Emitter, "toml/merge/emitter"
|
|
96
|
-
autoload :FileAnalysis, "toml/merge/file_analysis"
|
|
97
|
-
autoload :MergeResult, "toml/merge/merge_result"
|
|
98
|
-
autoload :NodeTypeNormalizer, "toml/merge/node_type_normalizer"
|
|
99
|
-
autoload :NodeWrapper, "toml/merge/node_wrapper"
|
|
100
|
-
autoload :ConflictResolver, "toml/merge/conflict_resolver"
|
|
101
|
-
autoload :SmartMerger, "toml/merge/smart_merger"
|
|
102
|
-
autoload :TableMatchRefiner, "toml/merge/table_match_refiner"
|
|
103
|
-
end
|
|
104
|
-
end
|
|
135
|
+
def merge_toml(template_source, destination_source, dialect, backend: nil)
|
|
136
|
+
resolved_backend = resolve_backend(backend)
|
|
137
|
+
return unsupported_feature_result("Unsupported TOML backend #{resolved_backend}.") unless resolved_backend == TreeHaver::KREUZBERG_LANGUAGE_PACK_BACKEND.id
|
|
105
138
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
139
|
+
merge_toml_with_parser(template_source, destination_source, dialect) do |source, parse_dialect|
|
|
140
|
+
parse_toml(source, parse_dialect, backend: resolved_backend)
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def resolve_backend(backend)
|
|
145
|
+
backend.to_s.empty? ? TreeHaver::KREUZBERG_LANGUAGE_PACK_BACKEND.id : backend.to_s
|
|
146
|
+
end
|
|
147
|
+
private_class_method :resolve_backend
|
|
148
|
+
|
|
149
|
+
def normalize_toml_source(source)
|
|
150
|
+
source.gsub(/\r\n?/, "\n")
|
|
151
|
+
end
|
|
152
|
+
private_class_method :normalize_toml_source
|
|
153
|
+
|
|
154
|
+
def strip_toml_comment(line)
|
|
155
|
+
result = +""
|
|
156
|
+
in_string = false
|
|
157
|
+
escaped = false
|
|
158
|
+
|
|
159
|
+
line.each_char do |char|
|
|
160
|
+
if in_string
|
|
161
|
+
result << char
|
|
162
|
+
if escaped
|
|
163
|
+
escaped = false
|
|
164
|
+
elsif char == "\\"
|
|
165
|
+
escaped = true
|
|
166
|
+
elsif char == '"'
|
|
167
|
+
in_string = false
|
|
168
|
+
end
|
|
169
|
+
next
|
|
170
|
+
end
|
|
117
171
|
|
|
118
|
-
|
|
119
|
-
|
|
172
|
+
if char == '"'
|
|
173
|
+
in_string = true
|
|
174
|
+
result << char
|
|
175
|
+
next
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
break if char == "#"
|
|
179
|
+
|
|
180
|
+
result << char
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
raise ParseError, "Unterminated TOML string." if in_string
|
|
184
|
+
|
|
185
|
+
result.strip
|
|
186
|
+
end
|
|
187
|
+
private_class_method :strip_toml_comment
|
|
188
|
+
|
|
189
|
+
def split_outside_quotes(value, separator)
|
|
190
|
+
parts = []
|
|
191
|
+
current = +""
|
|
192
|
+
in_string = false
|
|
193
|
+
escaped = false
|
|
194
|
+
depth = 0
|
|
195
|
+
|
|
196
|
+
value.each_char do |char|
|
|
197
|
+
if in_string
|
|
198
|
+
current << char
|
|
199
|
+
if escaped
|
|
200
|
+
escaped = false
|
|
201
|
+
elsif char == "\\"
|
|
202
|
+
escaped = true
|
|
203
|
+
elsif char == '"'
|
|
204
|
+
in_string = false
|
|
205
|
+
end
|
|
206
|
+
next
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
case char
|
|
210
|
+
when '"'
|
|
211
|
+
in_string = true
|
|
212
|
+
current << char
|
|
213
|
+
when "["
|
|
214
|
+
depth += 1
|
|
215
|
+
current << char
|
|
216
|
+
when "]"
|
|
217
|
+
depth -= 1
|
|
218
|
+
current << char
|
|
219
|
+
else
|
|
220
|
+
if char == separator && depth.zero?
|
|
221
|
+
parts << current.strip
|
|
222
|
+
current = +""
|
|
223
|
+
else
|
|
224
|
+
current << char
|
|
225
|
+
end
|
|
226
|
+
end
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
raise ParseError, "Unterminated TOML string or array." if in_string || !depth.zero?
|
|
230
|
+
|
|
231
|
+
parts << current.strip
|
|
232
|
+
parts
|
|
233
|
+
end
|
|
234
|
+
private_class_method :split_outside_quotes
|
|
235
|
+
|
|
236
|
+
def parse_toml_key_path(value)
|
|
237
|
+
trimmed = value.strip
|
|
238
|
+
raise ParseError, "Missing TOML key path." if trimmed.empty?
|
|
239
|
+
|
|
240
|
+
parts = trimmed.split(".").map(&:strip)
|
|
241
|
+
raise ParseError, "Unsupported TOML key path #{trimmed}." unless parts.all? { |part| part.match?(/\A[A-Za-z0-9_-]+\z/) }
|
|
242
|
+
|
|
243
|
+
parts
|
|
244
|
+
end
|
|
245
|
+
private_class_method :parse_toml_key_path
|
|
246
|
+
|
|
247
|
+
def parse_toml_scalar_value(value)
|
|
248
|
+
case value
|
|
249
|
+
when /\A".*"\z/m
|
|
250
|
+
JSON.parse(value)
|
|
251
|
+
when "true"
|
|
252
|
+
true
|
|
253
|
+
when "false"
|
|
254
|
+
false
|
|
255
|
+
when /\A-?\d+\z/
|
|
256
|
+
value.to_i
|
|
257
|
+
when /\A-?\d+\.\d+\z/
|
|
258
|
+
value.to_f
|
|
259
|
+
else
|
|
260
|
+
raise ParseError, "Unsupported TOML value #{value}."
|
|
261
|
+
end
|
|
262
|
+
rescue JSON::ParserError
|
|
263
|
+
raise ParseError, "Invalid TOML string #{value}."
|
|
264
|
+
end
|
|
265
|
+
private_class_method :parse_toml_scalar_value
|
|
266
|
+
|
|
267
|
+
def parse_toml_value(value)
|
|
268
|
+
stripped = value.strip
|
|
269
|
+
if stripped.start_with?("[")
|
|
270
|
+
raise ParseError, "Invalid TOML array #{value}." unless stripped.end_with?("]")
|
|
271
|
+
|
|
272
|
+
inner = stripped[1..-2].strip
|
|
273
|
+
return [] if inner.empty?
|
|
274
|
+
|
|
275
|
+
split_outside_quotes(inner, ",").map { |entry| parse_toml_scalar_value(entry) }
|
|
276
|
+
else
|
|
277
|
+
parse_toml_scalar_value(stripped)
|
|
278
|
+
end
|
|
279
|
+
end
|
|
280
|
+
private_class_method :parse_toml_value
|
|
281
|
+
|
|
282
|
+
def ensure_toml_table(root, path)
|
|
283
|
+
current = root
|
|
284
|
+
path.each do |segment|
|
|
285
|
+
existing = current[segment]
|
|
286
|
+
if existing.nil?
|
|
287
|
+
current[segment] = {}
|
|
288
|
+
current = current[segment]
|
|
289
|
+
elsif existing.is_a?(Hash)
|
|
290
|
+
current = existing
|
|
291
|
+
else
|
|
292
|
+
raise ParseError, "TOML table path /#{path.join('/')} conflicts with a value."
|
|
293
|
+
end
|
|
294
|
+
end
|
|
295
|
+
current
|
|
296
|
+
end
|
|
297
|
+
private_class_method :ensure_toml_table
|
|
298
|
+
|
|
299
|
+
def assign_toml_value(root, path, value)
|
|
300
|
+
raise ParseError, "Missing TOML assignment path." if path.empty?
|
|
301
|
+
|
|
302
|
+
table = ensure_toml_table(root, path[0..-2])
|
|
303
|
+
key = path[-1]
|
|
304
|
+
existing = table[key]
|
|
305
|
+
raise ParseError, "TOML key /#{path.join('/')} conflicts with a table." if existing.is_a?(Hash)
|
|
306
|
+
|
|
307
|
+
table[key] = value
|
|
308
|
+
end
|
|
309
|
+
private_class_method :assign_toml_value
|
|
310
|
+
|
|
311
|
+
def parse_toml_document(source)
|
|
312
|
+
lines = normalize_toml_source(source).split("\n")
|
|
313
|
+
root = {}
|
|
314
|
+
current_table_path = []
|
|
315
|
+
|
|
316
|
+
lines.each do |raw_line|
|
|
317
|
+
line = strip_toml_comment(raw_line)
|
|
318
|
+
next if line.empty?
|
|
319
|
+
|
|
320
|
+
if line.start_with?("[")
|
|
321
|
+
raise ParseError, "Invalid TOML table header #{line}." unless line.end_with?("]")
|
|
322
|
+
|
|
323
|
+
current_table_path = parse_toml_key_path(line[1..-2])
|
|
324
|
+
ensure_toml_table(root, current_table_path)
|
|
325
|
+
next
|
|
326
|
+
end
|
|
327
|
+
|
|
328
|
+
parts = split_outside_quotes(line, "=")
|
|
329
|
+
raise ParseError, "Invalid TOML assignment #{line}." unless parts.length == 2
|
|
330
|
+
|
|
331
|
+
key_path = parse_toml_key_path(parts[0])
|
|
332
|
+
value = parse_toml_value(parts[1])
|
|
333
|
+
assign_toml_value(root, current_table_path + key_path, value)
|
|
334
|
+
end
|
|
335
|
+
|
|
336
|
+
root
|
|
337
|
+
end
|
|
338
|
+
private_class_method :parse_toml_document
|
|
339
|
+
|
|
340
|
+
def render_toml_scalar(value)
|
|
341
|
+
if value.is_a?(String)
|
|
342
|
+
JSON.generate(value)
|
|
343
|
+
elsif value == true || value == false
|
|
344
|
+
value ? "true" : "false"
|
|
345
|
+
else
|
|
346
|
+
value.to_s
|
|
347
|
+
end
|
|
348
|
+
end
|
|
349
|
+
private_class_method :render_toml_scalar
|
|
350
|
+
|
|
351
|
+
def render_toml_value(value)
|
|
352
|
+
return "[#{value.map { |item| render_toml_scalar(item) }.join(', ')}]" if value.is_a?(Array)
|
|
353
|
+
|
|
354
|
+
render_toml_scalar(value)
|
|
355
|
+
end
|
|
356
|
+
private_class_method :render_toml_value
|
|
357
|
+
|
|
358
|
+
def render_toml_table(table, path = [])
|
|
359
|
+
lines = []
|
|
360
|
+
keys = table.keys.sort
|
|
361
|
+
value_keys = keys.reject { |key| table[key].is_a?(Hash) }
|
|
362
|
+
table_keys = keys.select { |key| table[key].is_a?(Hash) }
|
|
363
|
+
|
|
364
|
+
lines << "[#{path.join('.')}]" unless path.empty?
|
|
365
|
+
value_keys.each do |key|
|
|
366
|
+
lines << "#{key} = #{render_toml_value(table[key])}"
|
|
367
|
+
end
|
|
368
|
+
table_keys.each do |key|
|
|
369
|
+
lines << "" unless lines.empty?
|
|
370
|
+
lines.concat(render_toml_table(table[key], path + [key]))
|
|
371
|
+
end
|
|
372
|
+
lines
|
|
373
|
+
end
|
|
374
|
+
private_class_method :render_toml_table
|
|
375
|
+
|
|
376
|
+
def canonical_toml(table)
|
|
377
|
+
"#{render_toml_table(table).join("\n")}\n"
|
|
378
|
+
end
|
|
379
|
+
private_class_method :canonical_toml
|
|
380
|
+
|
|
381
|
+
def collect_toml_owners(table, prefix = "")
|
|
382
|
+
table.keys.sort.flat_map do |key|
|
|
383
|
+
path = "#{prefix}/#{key}"
|
|
384
|
+
value = table[key]
|
|
385
|
+
if value.is_a?(Array)
|
|
386
|
+
[{ path: path, owner_kind: "key_value", match_key: key }] +
|
|
387
|
+
value.each_index.map { |index| { path: "#{path}/#{index}", owner_kind: "array_item" } }
|
|
388
|
+
elsif value.is_a?(Hash)
|
|
389
|
+
[{ path: path, owner_kind: "table", match_key: key }] + collect_toml_owners(value, path)
|
|
390
|
+
else
|
|
391
|
+
[{ path: path, owner_kind: "key_value", match_key: key }]
|
|
392
|
+
end
|
|
393
|
+
end
|
|
394
|
+
end
|
|
395
|
+
private_class_method :collect_toml_owners
|
|
396
|
+
|
|
397
|
+
def merge_toml_tables(template, destination)
|
|
398
|
+
(template.keys | destination.keys).sort.each_with_object({}) do |key, merged|
|
|
399
|
+
if !template.key?(key)
|
|
400
|
+
merged[key] = destination[key]
|
|
401
|
+
elsif !destination.key?(key)
|
|
402
|
+
merged[key] = template[key]
|
|
403
|
+
elsif template[key].is_a?(Hash) && destination[key].is_a?(Hash)
|
|
404
|
+
merged[key] = merge_toml_tables(template[key], destination[key])
|
|
405
|
+
else
|
|
406
|
+
merged[key] = destination[key]
|
|
407
|
+
end
|
|
408
|
+
end
|
|
409
|
+
end
|
|
410
|
+
private_class_method :merge_toml_tables
|
|
411
|
+
|
|
412
|
+
def parse_error_result(message)
|
|
413
|
+
{ ok: false, diagnostics: [{ severity: "error", category: "parse_error", message: message }] }
|
|
414
|
+
end
|
|
415
|
+
private_class_method :parse_error_result
|
|
416
|
+
|
|
417
|
+
def unsupported_feature_result(message)
|
|
418
|
+
{ ok: false, diagnostics: [{ severity: "error", category: "unsupported_feature", message: message }], policies: [] }
|
|
419
|
+
end
|
|
420
|
+
private_class_method :unsupported_feature_result
|
|
421
|
+
end
|
|
120
422
|
end
|
data/lib/toml-merge.rb
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
#
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
require "toml/merge"
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "toml/merge"
|
data.tar.gz.sig
CHANGED
|
Binary file
|