protostar 0.1.0.beta1
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/CHANGELOG.md +60 -0
- data/LICENSE.txt +21 -0
- data/README.md +647 -0
- data/THIRD_PARTY_NOTICES.md +61 -0
- data/exe/protoc-gen-protostar +7 -0
- data/exe/protostar +7 -0
- data/lib/protostar/builder/generator.rb +71 -0
- data/lib/protostar/builder/sink.rb +35 -0
- data/lib/protostar/builder.rb +965 -0
- data/lib/protostar/cli.rb +267 -0
- data/lib/protostar/codec/decode_generator.rb +199 -0
- data/lib/protostar/codec/decoder.rb +296 -0
- data/lib/protostar/codec/encode_generator.rb +224 -0
- data/lib/protostar/codec/encoder.rb +121 -0
- data/lib/protostar/codec/sizer.rb +91 -0
- data/lib/protostar/codec.rb +15 -0
- data/lib/protostar/compiler/ast.rb +193 -0
- data/lib/protostar/compiler/emitters/descriptor_set.rb +57 -0
- data/lib/protostar/compiler/emitters/google_ruby.rb +103 -0
- data/lib/protostar/compiler/emitters/grpc.rb +175 -0
- data/lib/protostar/compiler/emitters/protostar_rbs.rb +252 -0
- data/lib/protostar/compiler/emitters/rbs.rb +394 -0
- data/lib/protostar/compiler/emitters/ruby.rb +503 -0
- data/lib/protostar/compiler/emitters.rb +268 -0
- data/lib/protostar/compiler/include/LICENSE +32 -0
- data/lib/protostar/compiler/include/google/protobuf/any.proto +106 -0
- data/lib/protostar/compiler/include/google/protobuf/api.proto +229 -0
- data/lib/protostar/compiler/include/google/protobuf/compiler/plugin.proto +180 -0
- data/lib/protostar/compiler/include/google/protobuf/cpp_features.proto +87 -0
- data/lib/protostar/compiler/include/google/protobuf/descriptor.proto +1532 -0
- data/lib/protostar/compiler/include/google/protobuf/duration.proto +115 -0
- data/lib/protostar/compiler/include/google/protobuf/empty.proto +51 -0
- data/lib/protostar/compiler/include/google/protobuf/field_mask.proto +243 -0
- data/lib/protostar/compiler/include/google/protobuf/java_features.proto +132 -0
- data/lib/protostar/compiler/include/google/protobuf/source_context.proto +48 -0
- data/lib/protostar/compiler/include/google/protobuf/struct.proto +111 -0
- data/lib/protostar/compiler/include/google/protobuf/timestamp.proto +145 -0
- data/lib/protostar/compiler/include/google/protobuf/type.proto +217 -0
- data/lib/protostar/compiler/include/google/protobuf/wrappers.proto +157 -0
- data/lib/protostar/compiler/lexer.rb +659 -0
- data/lib/protostar/compiler/linker.rb +894 -0
- data/lib/protostar/compiler/option_interpreter.rb +363 -0
- data/lib/protostar/compiler/parser.rb +1696 -0
- data/lib/protostar/compiler/retention.rb +104 -0
- data/lib/protostar/compiler/run.rb +184 -0
- data/lib/protostar/compiler/source_info.rb +161 -0
- data/lib/protostar/compiler/symbol_table.rb +223 -0
- data/lib/protostar/compiler/text_format.rb +340 -0
- data/lib/protostar/compiler/validator.rb +944 -0
- data/lib/protostar/compiler.rb +304 -0
- data/lib/protostar/diagnostics/codes.rb +1307 -0
- data/lib/protostar/diagnostics/formats.rb +64 -0
- data/lib/protostar/diagnostics/renderer.rb +240 -0
- data/lib/protostar/diagnostics/source.rb +153 -0
- data/lib/protostar/diagnostics.rb +117 -0
- data/lib/protostar/dsl.rb +367 -0
- data/lib/protostar/enum.rb +70 -0
- data/lib/protostar/errors.rb +23 -0
- data/lib/protostar/google/protobuf/any_pb.rb +90 -0
- data/lib/protostar/google/protobuf/api_pb.rb +199 -0
- data/lib/protostar/google/protobuf/compiler/plugin_pb.rb +168 -0
- data/lib/protostar/google/protobuf/descriptor_pb.rb +1239 -0
- data/lib/protostar/google/protobuf/duration_pb.rb +99 -0
- data/lib/protostar/google/protobuf/empty_pb.rb +36 -0
- data/lib/protostar/google/protobuf/field_mask_pb.rb +228 -0
- data/lib/protostar/google/protobuf/source_context_pb.rb +33 -0
- data/lib/protostar/google/protobuf/struct_pb.rb +91 -0
- data/lib/protostar/google/protobuf/timestamp_pb.rb +129 -0
- data/lib/protostar/google/protobuf/type_pb.rb +202 -0
- data/lib/protostar/google/protobuf/wrappers_pb.rb +125 -0
- data/lib/protostar/google_protobuf_shim.rb +24 -0
- data/lib/protostar/json/encoder.rb +340 -0
- data/lib/protostar/json/errors.rb +34 -0
- data/lib/protostar/json/parser.rb +437 -0
- data/lib/protostar/json/path.rb +39 -0
- data/lib/protostar/json.rb +95 -0
- data/lib/protostar/lint/command.rb +197 -0
- data/lib/protostar/lint/config.rb +98 -0
- data/lib/protostar/lint/context.rb +126 -0
- data/lib/protostar/lint/directives.rb +56 -0
- data/lib/protostar/lint/fixer.rb +165 -0
- data/lib/protostar/lint/naming.rb +54 -0
- data/lib/protostar/lint/rule.rb +120 -0
- data/lib/protostar/lint/rules/comment_enum.rb +31 -0
- data/lib/protostar/lint/rules/comment_enum_value.rb +32 -0
- data/lib/protostar/lint/rules/comment_field.rb +33 -0
- data/lib/protostar/lint/rules/comment_message.rb +31 -0
- data/lib/protostar/lint/rules/comment_rpc.rb +31 -0
- data/lib/protostar/lint/rules/comment_service.rb +31 -0
- data/lib/protostar/lint/rules/edition_recommended.rb +31 -0
- data/lib/protostar/lint/rules/enum_first_value_zero.rb +34 -0
- data/lib/protostar/lint/rules/enum_pascal_case.rb +25 -0
- data/lib/protostar/lint/rules/enum_value_prefix.rb +38 -0
- data/lib/protostar/lint/rules/enum_value_upper_snake_case.rb +28 -0
- data/lib/protostar/lint/rules/enum_zero_value_suffix.rb +41 -0
- data/lib/protostar/lint/rules/field_lower_snake_case.rb +34 -0
- data/lib/protostar/lint/rules/field_not_required.rb +39 -0
- data/lib/protostar/lint/rules/field_number_reuse_risk.rb +45 -0
- data/lib/protostar/lint/rules/field_numbers_hot_range.rb +44 -0
- data/lib/protostar/lint/rules/file_lower_snake_case.rb +27 -0
- data/lib/protostar/lint/rules/import_no_public.rb +29 -0
- data/lib/protostar/lint/rules/import_no_weak.rb +28 -0
- data/lib/protostar/lint/rules/import_used.rb +33 -0
- data/lib/protostar/lint/rules/message_pascal_case.rb +27 -0
- data/lib/protostar/lint/rules/no_deprecated_features.rb +50 -0
- data/lib/protostar/lint/rules/no_groups.rb +34 -0
- data/lib/protostar/lint/rules/oneof_lower_snake_case.rb +27 -0
- data/lib/protostar/lint/rules/package_defined.rb +28 -0
- data/lib/protostar/lint/rules/package_directory_match.rb +33 -0
- data/lib/protostar/lint/rules/package_lower_snake_case.rb +28 -0
- data/lib/protostar/lint/rules/package_same_directory.rb +39 -0
- data/lib/protostar/lint/rules/package_version_suffix.rb +31 -0
- data/lib/protostar/lint/rules/reserved_names_with_numbers.rb +44 -0
- data/lib/protostar/lint/rules/rpc_pascal_case.rb +25 -0
- data/lib/protostar/lint/rules/rpc_request_response_unique.rb +40 -0
- data/lib/protostar/lint/rules/rpc_request_standard_name.rb +32 -0
- data/lib/protostar/lint/rules/rpc_response_standard_name.rb +31 -0
- data/lib/protostar/lint/rules/service_pascal_case.rb +25 -0
- data/lib/protostar/lint/rules/service_suffix.rb +28 -0
- data/lib/protostar/lint/rules/syntax_specified.rb +35 -0
- data/lib/protostar/lint/runner.rb +135 -0
- data/lib/protostar/lint.rb +122 -0
- data/lib/protostar/message/accessors.rb +86 -0
- data/lib/protostar/message/coercion.rb +171 -0
- data/lib/protostar/message/field_info.rb +145 -0
- data/lib/protostar/message/reflection.rb +149 -0
- data/lib/protostar/message.rb +572 -0
- data/lib/protostar/plugin.rb +94 -0
- data/lib/protostar/schema/default_value.rb +113 -0
- data/lib/protostar/schema/descriptor.rb +41 -0
- data/lib/protostar/schema/draft/copy.rb +88 -0
- data/lib/protostar/schema/draft/resolution.rb +177 -0
- data/lib/protostar/schema/draft.rb +779 -0
- data/lib/protostar/schema/enum.rb +106 -0
- data/lib/protostar/schema/extension_range.rb +37 -0
- data/lib/protostar/schema/features.rb +153 -0
- data/lib/protostar/schema/field.rb +201 -0
- data/lib/protostar/schema/file.rb +217 -0
- data/lib/protostar/schema/freezer.rb +61 -0
- data/lib/protostar/schema/message.rb +104 -0
- data/lib/protostar/schema/oneof.rb +38 -0
- data/lib/protostar/schema/options.rb +367 -0
- data/lib/protostar/schema/pool.rb +179 -0
- data/lib/protostar/schema/proto.rb +576 -0
- data/lib/protostar/schema/resolver.rb +118 -0
- data/lib/protostar/schema/service.rb +89 -0
- data/lib/protostar/schema/source_info.rb +53 -0
- data/lib/protostar/schema.rb +65 -0
- data/lib/protostar/short_names/any.rb +7 -0
- data/lib/protostar/short_names/duration.rb +7 -0
- data/lib/protostar/short_names/empty.rb +7 -0
- data/lib/protostar/short_names/field_mask.rb +7 -0
- data/lib/protostar/short_names/struct.rb +9 -0
- data/lib/protostar/short_names/timestamp.rb +7 -0
- data/lib/protostar/short_names/wrappers.rb +15 -0
- data/lib/protostar/text/extensions.rb +114 -0
- data/lib/protostar/text/parser.rb +439 -0
- data/lib/protostar/text/printer.rb +304 -0
- data/lib/protostar/text/tokenizer.rb +116 -0
- data/lib/protostar/text.rb +67 -0
- data/lib/protostar/types.rb +152 -0
- data/lib/protostar/unknown_field.rb +58 -0
- data/lib/protostar/version.rb +5 -0
- data/lib/protostar/view/generator.rb +298 -0
- data/lib/protostar/view/map.rb +194 -0
- data/lib/protostar/view/repeated.rb +143 -0
- data/lib/protostar/view/scan.rb +183 -0
- data/lib/protostar/view.rb +283 -0
- data/lib/protostar/well_known/any.rb +52 -0
- data/lib/protostar/well_known/duration.rb +53 -0
- data/lib/protostar/well_known/list_value.rb +63 -0
- data/lib/protostar/well_known/struct.rb +55 -0
- data/lib/protostar/well_known/timestamp.rb +64 -0
- data/lib/protostar/well_known/value.rb +60 -0
- data/lib/protostar/well_known.rb +38 -0
- data/lib/protostar/wire/io_source.rb +40 -0
- data/lib/protostar/wire/reader.rb +254 -0
- data/lib/protostar/wire/writer.rb +201 -0
- data/lib/protostar/wire.rb +96 -0
- data/lib/protostar.rb +95 -0
- data/shims/google-protobuf/lib/google/protobuf/any_pb.rb +17 -0
- data/shims/google-protobuf/lib/google/protobuf/api_pb.rb +22 -0
- data/shims/google-protobuf/lib/google/protobuf/compiler/plugin_pb.rb +25 -0
- data/shims/google-protobuf/lib/google/protobuf/descriptor_pb.rb +72 -0
- data/shims/google-protobuf/lib/google/protobuf/descriptor_pool.rb +164 -0
- data/shims/google-protobuf/lib/google/protobuf/descriptors.rb +553 -0
- data/shims/google-protobuf/lib/google/protobuf/duration_pb.rb +17 -0
- data/shims/google-protobuf/lib/google/protobuf/empty_pb.rb +17 -0
- data/shims/google-protobuf/lib/google/protobuf/field_mask_pb.rb +17 -0
- data/shims/google-protobuf/lib/google/protobuf/internal/convert.rb +180 -0
- data/shims/google-protobuf/lib/google/protobuf/internal/extensions.rb +154 -0
- data/shims/google-protobuf/lib/google/protobuf/internal/json.rb +104 -0
- data/shims/google-protobuf/lib/google/protobuf/internal/well_known.rb +140 -0
- data/shims/google-protobuf/lib/google/protobuf/map.rb +216 -0
- data/shims/google-protobuf/lib/google/protobuf/message.rb +544 -0
- data/shims/google-protobuf/lib/google/protobuf/message_exts.rb +41 -0
- data/shims/google-protobuf/lib/google/protobuf/plugin_pb.rb +4 -0
- data/shims/google-protobuf/lib/google/protobuf/repeated_field.rb +366 -0
- data/shims/google-protobuf/lib/google/protobuf/source_context_pb.rb +17 -0
- data/shims/google-protobuf/lib/google/protobuf/struct_pb.rb +20 -0
- data/shims/google-protobuf/lib/google/protobuf/timestamp_pb.rb +17 -0
- data/shims/google-protobuf/lib/google/protobuf/type_pb.rb +27 -0
- data/shims/google-protobuf/lib/google/protobuf/well_known_types.rb +37 -0
- data/shims/google-protobuf/lib/google/protobuf/wrappers_pb.rb +25 -0
- data/shims/google-protobuf/lib/google/protobuf.rb +107 -0
- data/shims/google-protobuf/lib/google/protobuf_ffi.rb +5 -0
- data/shims/google-protobuf/lib/google/protobuf_native.rb +5 -0
- data/sig/protostar/google/protobuf/any_pb.rbs +77 -0
- data/sig/protostar/google/protobuf/api_pb.rbs +242 -0
- data/sig/protostar/google/protobuf/compiler/plugin_pb.rbs +264 -0
- data/sig/protostar/google/protobuf/descriptor_pb.rbs +2220 -0
- data/sig/protostar/google/protobuf/duration_pb.rbs +82 -0
- data/sig/protostar/google/protobuf/empty_pb.rbs +18 -0
- data/sig/protostar/google/protobuf/field_mask_pb.rbs +170 -0
- data/sig/protostar/google/protobuf/source_context_pb.rbs +24 -0
- data/sig/protostar/google/protobuf/struct_pb.rbs +135 -0
- data/sig/protostar/google/protobuf/timestamp_pb.rbs +102 -0
- data/sig/protostar/google/protobuf/type_pb.rbs +304 -0
- data/sig/protostar/google/protobuf/wrappers_pb.rbs +169 -0
- data/sig/protostar/lint.rbs +216 -0
- data/sig/protostar.rbs +326 -0
- metadata +366 -0
|
@@ -0,0 +1,363 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Protostar
|
|
4
|
+
class Compiler
|
|
5
|
+
# protoc's OptionInterpreter. Each `option` statement becomes an entry
|
|
6
|
+
# `[field, value]` (`value` is itself a list of entries for message
|
|
7
|
+
# fields), accumulated per element the way protoc accumulates unknown
|
|
8
|
+
# fields, so "already set" checks and the merging of `(a).b = 1` with
|
|
9
|
+
# `(a).c = 2` behave as in protoc. The entries are then folded into a
|
|
10
|
+
# {Schema::MessageValue}: options message fields by name, extensions by
|
|
11
|
+
# their {Schema::Field}.
|
|
12
|
+
#
|
|
13
|
+
# Option types are read from descriptors: those in the pool, and for the
|
|
14
|
+
# file being linked its draft, linked but not yet given options, so
|
|
15
|
+
# options declared and used in the same file (descriptor.proto's own
|
|
16
|
+
# included) resolve the same way.
|
|
17
|
+
#
|
|
18
|
+
# @api private
|
|
19
|
+
class OptionInterpreter
|
|
20
|
+
INT32_RANGE = (-2**31..2**31 - 1)
|
|
21
|
+
INT64_RANGE = (-2**63..2**63 - 1)
|
|
22
|
+
UINT32_MAX = 2**32 - 1
|
|
23
|
+
UINT64_MAX = 2**64 - 1
|
|
24
|
+
|
|
25
|
+
# @return [Hash{Array<Integer> => Array<Integer>}] uninterpreted_option paths to option paths
|
|
26
|
+
attr_reader :interpreted_paths
|
|
27
|
+
|
|
28
|
+
# @param linker [Linker]
|
|
29
|
+
# @param pool [Schema::Pool]
|
|
30
|
+
# @param fallback [#call, nil] the bundled descriptor.proto's message for a name
|
|
31
|
+
def initialize(linker, pool, fallback)
|
|
32
|
+
@linker = linker
|
|
33
|
+
@table = linker.table
|
|
34
|
+
@pool = pool
|
|
35
|
+
@fallback = fallback
|
|
36
|
+
@interpreted_paths = {}
|
|
37
|
+
@repeated_counts = Hash.new(0)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# @param pending [Array<Linker::Pending>]
|
|
41
|
+
# @return [Hash{AST::Node => Schema::MessageValue}] the options of every element that declared options
|
|
42
|
+
def run(pending)
|
|
43
|
+
@local = @linker.draft.descriptor
|
|
44
|
+
state = pending.map { |p| [p, [], []] }
|
|
45
|
+
state.each { |p, first, _| interpret(p, first, skip_extensions: true) }
|
|
46
|
+
if block_given?
|
|
47
|
+
features = {}.compare_by_identity
|
|
48
|
+
state.each do |p, first, _|
|
|
49
|
+
folded = fold(first)
|
|
50
|
+
features[p.node] = folded[:features] if folded.key?(:features)
|
|
51
|
+
end
|
|
52
|
+
yield features
|
|
53
|
+
end
|
|
54
|
+
state.each { |p, first, second| interpret(p, second, skip_extensions: false, earlier: first) }
|
|
55
|
+
result = {}.compare_by_identity
|
|
56
|
+
state.each do |p, first, second|
|
|
57
|
+
result[p.node] = fold(first + second, p.options.known)
|
|
58
|
+
end
|
|
59
|
+
result
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# A Schema descriptor for a symbol table entry
|
|
63
|
+
def schema_of(symbol)
|
|
64
|
+
symbol&.object
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# The options message to interpret against, protoc's choice: the pool's
|
|
68
|
+
# (or this file's) when present, else the one compiled into protoc
|
|
69
|
+
def options_type(name)
|
|
70
|
+
found = @table.find_not_enforcing_deps(name)
|
|
71
|
+
message = schema_of(found) if found&.kind == :message
|
|
72
|
+
return message if message
|
|
73
|
+
builtin = @fallback&.call(name)
|
|
74
|
+
@builtin_file = builtin&.file
|
|
75
|
+
builtin
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
private
|
|
79
|
+
|
|
80
|
+
def interpret(pending, entries, skip_extensions:, earlier: nil)
|
|
81
|
+
@pending = pending
|
|
82
|
+
pending.options.uninterpreted.each_with_index do |option, i|
|
|
83
|
+
src_path = pending.path + [999, i]
|
|
84
|
+
break unless interpret_single(option, entries, src_path, skip_extensions)
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def name_error(option, message)
|
|
89
|
+
@linker.error(option, :option_name, message)
|
|
90
|
+
false
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def value_error(option, message)
|
|
94
|
+
@linker.error(option, :option_value, message)
|
|
95
|
+
false
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def interpret_single(option, entries, src_path, skip_extensions)
|
|
99
|
+
if option.name.empty?
|
|
100
|
+
return true if skip_extensions
|
|
101
|
+
return name_error(option, "Option must have a name.")
|
|
102
|
+
end
|
|
103
|
+
if option.name[0][0] == "uninterpreted_option"
|
|
104
|
+
return true if skip_extensions
|
|
105
|
+
return name_error(option, "Option must not use reserved name \"uninterpreted_option\".")
|
|
106
|
+
end
|
|
107
|
+
return true if skip_extensions == option.name[0][1]
|
|
108
|
+
|
|
109
|
+
descriptor = options_type(Linker::OPTIONS_TYPES.fetch(@pending.kind))
|
|
110
|
+
field = nil
|
|
111
|
+
intermediate = []
|
|
112
|
+
debug = +""
|
|
113
|
+
dest_path = @pending.path.dup
|
|
114
|
+
option.name.each_with_index do |(part, extension), i|
|
|
115
|
+
debug << "." unless debug.empty?
|
|
116
|
+
unresolved = nil
|
|
117
|
+
if extension
|
|
118
|
+
debug << "(#{part})"
|
|
119
|
+
symbol = @table.lookup(part, @pending.name_scope)
|
|
120
|
+
unresolved = @table.unresolved_name
|
|
121
|
+
field = (symbol&.kind == :field) ? schema_of(symbol) : nil
|
|
122
|
+
else
|
|
123
|
+
debug << part
|
|
124
|
+
field = descriptor.fields_by_name[part]
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
if field.nil?
|
|
128
|
+
if unresolved
|
|
129
|
+
return name_error(option, "Option \"#{debug}\" is resolved to \"(#{unresolved})\", which is not defined. " \
|
|
130
|
+
"The innermost scope is searched first in name resolution. Consider using a leading '.'(i.e., " \
|
|
131
|
+
"\"(.#{debug[1..]}\") to start from the outermost scope.")
|
|
132
|
+
end
|
|
133
|
+
return name_error(option, "Option \"#{debug}\" unknown. Ensure that your proto definition file imports " \
|
|
134
|
+
"the proto which defines the option (i.e. via import option after edition 2024).")
|
|
135
|
+
end
|
|
136
|
+
holder = field.extension? ? field.extendee : field.containing_message
|
|
137
|
+
if holder.full_name != descriptor.full_name
|
|
138
|
+
return name_error(option, "Option field \"#{debug}\" is not a field or extension of message " \
|
|
139
|
+
"\"#{descriptor.name}\".")
|
|
140
|
+
end
|
|
141
|
+
dest_path << field.number
|
|
142
|
+
if field.file.equal?(@local) && option.name[0] == ["features", false]
|
|
143
|
+
return name_error(option, "Feature \"#{debug}\" can't be used in the same file it's defined in.")
|
|
144
|
+
end
|
|
145
|
+
next unless i < option.name.size - 1
|
|
146
|
+
unless field.type == :message || field.type == :group
|
|
147
|
+
return name_error(option, "Option \"#{debug}\" is an atomic type, not a message.")
|
|
148
|
+
end
|
|
149
|
+
if field.repeated?
|
|
150
|
+
return name_error(option, "Option field \"#{debug}\" is a repeated message. Repeated message options " \
|
|
151
|
+
"must be initialized using an aggregate value.")
|
|
152
|
+
end
|
|
153
|
+
intermediate << field
|
|
154
|
+
descriptor = field.message_type
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
return false if !field.repeated? && already_set?(intermediate, field, debug, entries, option)
|
|
158
|
+
|
|
159
|
+
value = option_value(field, option)
|
|
160
|
+
return false if value.equal?(FAILED)
|
|
161
|
+
entry = [field, value]
|
|
162
|
+
intermediate.reverse_each { |outer| entry = [outer, [entry]] }
|
|
163
|
+
entries << entry
|
|
164
|
+
|
|
165
|
+
if field.repeated?
|
|
166
|
+
key = dest_path.dup.freeze
|
|
167
|
+
index = @repeated_counts[key]
|
|
168
|
+
@repeated_counts[key] += 1
|
|
169
|
+
dest_path << index
|
|
170
|
+
end
|
|
171
|
+
@interpreted_paths[src_path] = dest_path
|
|
172
|
+
true
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
FAILED = Object.new.freeze
|
|
176
|
+
private_constant :FAILED
|
|
177
|
+
|
|
178
|
+
# ExamineIfOptionIsSet
|
|
179
|
+
def already_set?(intermediate, field, debug, entries, option)
|
|
180
|
+
if intermediate.empty?
|
|
181
|
+
if entries.any? { |f, _| f.number == field.number }
|
|
182
|
+
name_error(option, "Option \"#{debug}\" was already set.")
|
|
183
|
+
return true
|
|
184
|
+
end
|
|
185
|
+
return false
|
|
186
|
+
end
|
|
187
|
+
first, *rest = intermediate
|
|
188
|
+
entries.each do |f, value|
|
|
189
|
+
next unless f.number == first.number && value.is_a?(Array)
|
|
190
|
+
return true if already_set?(rest, field, debug, value, option)
|
|
191
|
+
end
|
|
192
|
+
false
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
def option_value(field, option)
|
|
196
|
+
kind = option.value_kind
|
|
197
|
+
value = option.value
|
|
198
|
+
name = field.full_name
|
|
199
|
+
case field.type
|
|
200
|
+
when :int32, :sint32, :sfixed32
|
|
201
|
+
int_option(option, kind, value, INT32_RANGE, "int32", name)
|
|
202
|
+
when :int64, :sint64, :sfixed64
|
|
203
|
+
if kind == :positive_int_value
|
|
204
|
+
if value > INT64_RANGE.end
|
|
205
|
+
value_error(option, out_of_range(INT64_RANGE, "int64", name))
|
|
206
|
+
return FAILED
|
|
207
|
+
end
|
|
208
|
+
value
|
|
209
|
+
elsif kind == :negative_int_value
|
|
210
|
+
value
|
|
211
|
+
else
|
|
212
|
+
value_error(option, must_be_int(INT64_RANGE, "int64", name))
|
|
213
|
+
FAILED
|
|
214
|
+
end
|
|
215
|
+
when :uint32, :fixed32
|
|
216
|
+
if kind == :positive_int_value
|
|
217
|
+
if value > UINT32_MAX
|
|
218
|
+
value_error(option, out_of_range(0..UINT32_MAX, "uint32", name))
|
|
219
|
+
return FAILED
|
|
220
|
+
end
|
|
221
|
+
value
|
|
222
|
+
else
|
|
223
|
+
value_error(option, must_be_int(0..UINT32_MAX, "uint32", name))
|
|
224
|
+
FAILED
|
|
225
|
+
end
|
|
226
|
+
when :uint64, :fixed64
|
|
227
|
+
return value if kind == :positive_int_value
|
|
228
|
+
value_error(option, must_be_int(0..UINT64_MAX, "uint64", name))
|
|
229
|
+
FAILED
|
|
230
|
+
when :float, :double
|
|
231
|
+
number = case kind
|
|
232
|
+
when :double_value, :positive_int_value, :negative_int_value then value.to_f
|
|
233
|
+
when :identifier_value
|
|
234
|
+
if value == "inf"
|
|
235
|
+
Float::INFINITY
|
|
236
|
+
elsif value == "nan"
|
|
237
|
+
Float::NAN
|
|
238
|
+
end
|
|
239
|
+
end
|
|
240
|
+
if number.nil?
|
|
241
|
+
value_error(option, "Value must be number for #{field.type} option \"#{name}\".")
|
|
242
|
+
return FAILED
|
|
243
|
+
end
|
|
244
|
+
(field.type == :float) ? Schema::DefaultValue.to_float32(number) : number
|
|
245
|
+
when :bool
|
|
246
|
+
unless kind == :identifier_value
|
|
247
|
+
value_error(option, "Value must be identifier for boolean option \"#{name}\".")
|
|
248
|
+
return FAILED
|
|
249
|
+
end
|
|
250
|
+
case value
|
|
251
|
+
when "true" then true
|
|
252
|
+
when "false" then false
|
|
253
|
+
else
|
|
254
|
+
value_error(option, "Value must be \"true\" or \"false\" for boolean option \"#{name}\".")
|
|
255
|
+
FAILED
|
|
256
|
+
end
|
|
257
|
+
when :enum
|
|
258
|
+
unless kind == :identifier_value
|
|
259
|
+
value_error(option, "Value must be identifier for enum-valued option \"#{name}\".")
|
|
260
|
+
return FAILED
|
|
261
|
+
end
|
|
262
|
+
enum_option(field, value, option)
|
|
263
|
+
when :string, :bytes
|
|
264
|
+
unless kind == :string_value
|
|
265
|
+
value_error(option, "Value must be quoted string for string option \"#{name}\".")
|
|
266
|
+
return FAILED
|
|
267
|
+
end
|
|
268
|
+
(field.type == :string) ? value.dup.force_encoding(Encoding::UTF_8) : value.b
|
|
269
|
+
when :message, :group
|
|
270
|
+
unless kind == :aggregate_value
|
|
271
|
+
value_error(option, "Option \"#{name}\" is a message. To set the entire message, use syntax like " \
|
|
272
|
+
"\"#{field.name} = { <proto text format> }\". To set fields within it, use syntax like " \
|
|
273
|
+
"\"#{field.name}.foo = value\".")
|
|
274
|
+
return FAILED
|
|
275
|
+
end
|
|
276
|
+
parser = TextFormat.new(value, field.message_type, self, @table)
|
|
277
|
+
entries = parser.parse
|
|
278
|
+
unless entries
|
|
279
|
+
value_error(option, "Error while parsing option value for \"#{field.name}\": #{parser.errors.join("; ")}")
|
|
280
|
+
return FAILED
|
|
281
|
+
end
|
|
282
|
+
entries
|
|
283
|
+
end
|
|
284
|
+
end
|
|
285
|
+
|
|
286
|
+
def int_option(option, kind, value, range, type, name)
|
|
287
|
+
if kind == :positive_int_value || kind == :negative_int_value
|
|
288
|
+
return value if range.cover?(value)
|
|
289
|
+
value_error(option, out_of_range(range, type, name))
|
|
290
|
+
else
|
|
291
|
+
value_error(option, must_be_int(range, type, name))
|
|
292
|
+
end
|
|
293
|
+
FAILED
|
|
294
|
+
end
|
|
295
|
+
|
|
296
|
+
def out_of_range(range, type, name)
|
|
297
|
+
"Value out of range, #{range.begin} to #{range.end}, for #{type} option \"#{name}\"."
|
|
298
|
+
end
|
|
299
|
+
|
|
300
|
+
def must_be_int(range, type, name)
|
|
301
|
+
"Value must be integer, from #{range.begin} to #{range.end}, for #{type} option \"#{name}\"."
|
|
302
|
+
end
|
|
303
|
+
|
|
304
|
+
def enum_option(field, value_name, option)
|
|
305
|
+
enum = field.enum_type
|
|
306
|
+
found = nil
|
|
307
|
+
if @builtin_file && enum.file.equal?(@builtin_file)
|
|
308
|
+
found = enum.value(value_name)
|
|
309
|
+
else
|
|
310
|
+
scope = enum.full_name[0, enum.full_name.size - enum.name.size]
|
|
311
|
+
symbol = @table.find_not_enforcing_deps(scope + value_name)
|
|
312
|
+
if symbol&.kind == :enum_value
|
|
313
|
+
candidate = schema_of(symbol)
|
|
314
|
+
if candidate.enum.full_name != enum.full_name
|
|
315
|
+
value_error(option, "Enum type \"#{enum.full_name}\" has no value named \"#{value_name}\" for option " \
|
|
316
|
+
"\"#{field.full_name}\". This appears to be a value from a sibling type.")
|
|
317
|
+
return FAILED
|
|
318
|
+
end
|
|
319
|
+
found = candidate
|
|
320
|
+
end
|
|
321
|
+
end
|
|
322
|
+
unless found
|
|
323
|
+
value_error(option, "Enum type \"#{enum.full_name}\" has no value named \"#{value_name}\" for option " \
|
|
324
|
+
"\"#{field.full_name}\".")
|
|
325
|
+
return FAILED
|
|
326
|
+
end
|
|
327
|
+
enum.values_by_number.fetch(found.number).name.to_sym
|
|
328
|
+
end
|
|
329
|
+
|
|
330
|
+
public
|
|
331
|
+
|
|
332
|
+
# Folds entries into a value the way parsing their serialized form
|
|
333
|
+
# would: repeated fields append, message fields merge, scalars take the
|
|
334
|
+
# last value
|
|
335
|
+
# @param known [Hash{Symbol => Object}] fields set before any entry (the parser's `map_entry`)
|
|
336
|
+
# @return [Schema::MessageValue]
|
|
337
|
+
def fold(entries, known = nil)
|
|
338
|
+
merged = {}.compare_by_identity
|
|
339
|
+
entries.each do |field, value|
|
|
340
|
+
if (field.type == :message || field.type == :group) && !field.repeated?
|
|
341
|
+
(merged[field] ||= []).concat(value)
|
|
342
|
+
else
|
|
343
|
+
merged[field] ||= []
|
|
344
|
+
merged[field] << value
|
|
345
|
+
end
|
|
346
|
+
end
|
|
347
|
+
fields = known ? known.dup : {}
|
|
348
|
+
extensions = {}
|
|
349
|
+
merged.each do |field, values|
|
|
350
|
+
target, key = field.extension? ? [extensions, field] : [fields, field.name.to_sym]
|
|
351
|
+
target[key] = if field.type == :message || field.type == :group
|
|
352
|
+
field.repeated? ? values.map { |v| fold(v) } : fold(values)
|
|
353
|
+
elsif field.repeated?
|
|
354
|
+
values
|
|
355
|
+
else
|
|
356
|
+
values.last
|
|
357
|
+
end
|
|
358
|
+
end
|
|
359
|
+
Schema::MessageValue.new(fields, extensions)
|
|
360
|
+
end
|
|
361
|
+
end
|
|
362
|
+
end
|
|
363
|
+
end
|