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,113 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Protostar
|
|
4
|
+
module Schema
|
|
5
|
+
# Parses `FieldDescriptorProto.default_value` text into Ruby values
|
|
6
|
+
# @api private
|
|
7
|
+
module DefaultValue
|
|
8
|
+
INTEGER_TYPES = %i[int32 int64 uint32 uint64 sint32 sint64 fixed32 fixed64 sfixed32 sfixed64].freeze
|
|
9
|
+
SIMPLE_ESCAPES = {
|
|
10
|
+
"a" => 7, "b" => 8, "f" => 12, "n" => 10, "r" => 13, "t" => 9, "v" => 11,
|
|
11
|
+
"\\" => 92, "'" => 39, "\"" => 34, "?" => 63
|
|
12
|
+
}.freeze
|
|
13
|
+
|
|
14
|
+
# protoc's SimpleDtoa: the shortest of %.15g and %.17g that reads back exactly
|
|
15
|
+
# @param value [Float]
|
|
16
|
+
# @return [String]
|
|
17
|
+
def self.simple_dtoa(value)
|
|
18
|
+
return "inf" if value == Float::INFINITY
|
|
19
|
+
return "-inf" if value == -Float::INFINITY
|
|
20
|
+
return "nan" if value.nan?
|
|
21
|
+
text = format("%.15g", value)
|
|
22
|
+
# the 15-digit form is kept only when it reads back as the very same double
|
|
23
|
+
text = format("%.17g", value) unless [Float(text)].pack("G") == [value].pack("G")
|
|
24
|
+
text
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# @param text [String] as protoc writes it
|
|
28
|
+
# @param type [Symbol] the field's short type
|
|
29
|
+
# @param enum [Enum, nil] the field's enum type
|
|
30
|
+
# @return [Object]
|
|
31
|
+
def self.parse(text, type, enum)
|
|
32
|
+
case type
|
|
33
|
+
when *INTEGER_TYPES then parse_integer(text)
|
|
34
|
+
when :double then parse_float(text)
|
|
35
|
+
when :float then to_float32(parse_float(text))
|
|
36
|
+
when :bool
|
|
37
|
+
case text
|
|
38
|
+
when "true" then true
|
|
39
|
+
when "false" then false
|
|
40
|
+
else raise Error, "invalid bool default #{text.inspect}"
|
|
41
|
+
end
|
|
42
|
+
when :string then text
|
|
43
|
+
when :bytes then unescape_bytes(text)
|
|
44
|
+
when :enum
|
|
45
|
+
raise Error, "enum default #{text.inspect} is not a value of #{enum.full_name}" unless enum.value(text)
|
|
46
|
+
text.to_sym
|
|
47
|
+
else
|
|
48
|
+
raise Error, "a #{type} field cannot have a default value"
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def self.parse_integer(text)
|
|
53
|
+
if text.match?(/\A-?0[xX]\h+\z/)
|
|
54
|
+
Integer(text, 16)
|
|
55
|
+
else
|
|
56
|
+
Integer(text, 10)
|
|
57
|
+
end
|
|
58
|
+
rescue ArgumentError
|
|
59
|
+
raise Error, "invalid integer default #{text.inspect}"
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def self.parse_float(text)
|
|
63
|
+
case text
|
|
64
|
+
when "inf", "infinity" then Float::INFINITY
|
|
65
|
+
when "-inf", "-infinity" then -Float::INFINITY
|
|
66
|
+
when "nan", "-nan" then Float::NAN
|
|
67
|
+
else Float(text)
|
|
68
|
+
end
|
|
69
|
+
rescue ArgumentError
|
|
70
|
+
raise Error, "invalid floating point default #{text.inspect}"
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# The value a float field actually holds: the nearest single-precision value
|
|
74
|
+
def self.to_float32(value)
|
|
75
|
+
return value if value.nan? || value.infinite?
|
|
76
|
+
[value].pack("e").unpack1("e")
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# Undoes protoc's CEscape: simple escapes, 1-3 digit octal, \x hex
|
|
80
|
+
def self.unescape_bytes(text)
|
|
81
|
+
text = text.b
|
|
82
|
+
out = "".b
|
|
83
|
+
i = 0
|
|
84
|
+
n = text.bytesize
|
|
85
|
+
while i < n
|
|
86
|
+
byte = text.getbyte(i)
|
|
87
|
+
if byte != 92 || i + 1 >= n # backslash
|
|
88
|
+
out << byte
|
|
89
|
+
i += 1
|
|
90
|
+
next
|
|
91
|
+
end
|
|
92
|
+
c = text.byteslice(i + 1, 1)
|
|
93
|
+
if (simple = SIMPLE_ESCAPES[c])
|
|
94
|
+
out << simple
|
|
95
|
+
i += 2
|
|
96
|
+
elsif c.match?(/[0-7]/)
|
|
97
|
+
digits = text.byteslice(i + 1, 3)[/\A[0-7]{1,3}/]
|
|
98
|
+
out << (digits.to_i(8) & 0xFF)
|
|
99
|
+
i += 1 + digits.bytesize
|
|
100
|
+
elsif c == "x" || c == "X"
|
|
101
|
+
digits = text.byteslice(i + 2, 2).to_s[/\A\h{1,2}/]
|
|
102
|
+
raise Error, "invalid \\x escape in bytes default #{text.inspect}" unless digits
|
|
103
|
+
out << digits.to_i(16)
|
|
104
|
+
i += 2 + digits.bytesize
|
|
105
|
+
else
|
|
106
|
+
raise Error, "invalid escape \\#{c} in bytes default #{text.inspect}"
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
out
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Protostar
|
|
4
|
+
module Schema
|
|
5
|
+
# What every descriptor has. Made by a {Draft}, frozen and shareable once it is finished.
|
|
6
|
+
class Descriptor
|
|
7
|
+
# @return [String] the simple name
|
|
8
|
+
attr_reader :name
|
|
9
|
+
# @return [String] the fully qualified name, without a leading dot
|
|
10
|
+
attr_reader :full_name
|
|
11
|
+
# @return [Integer] position among its siblings, in declaration order
|
|
12
|
+
attr_reader :index
|
|
13
|
+
# @return [Features] resolved features
|
|
14
|
+
attr_reader :features
|
|
15
|
+
|
|
16
|
+
# @return [Options] declared options; once resolved, also the resolved {Options#features}
|
|
17
|
+
def options
|
|
18
|
+
@options || Options::UNRESOLVED.fetch(self.class)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# Sets attributes that only become known while linking. Drafts are the
|
|
22
|
+
# callers; everything is frozen once the draft is finished.
|
|
23
|
+
# @api private
|
|
24
|
+
def __link(**attrs)
|
|
25
|
+
attrs.each { |key, value| instance_variable_set(:"@#{key}", value) }
|
|
26
|
+
self
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def inspect
|
|
30
|
+
"#<#{self.class.name} #{full_name}>"
|
|
31
|
+
end
|
|
32
|
+
alias_method :to_s, :inspect
|
|
33
|
+
|
|
34
|
+
# `to_proto` as a Hash, for reading; nothing builds from it
|
|
35
|
+
# @return [Hash]
|
|
36
|
+
def to_h
|
|
37
|
+
to_proto.to_h
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Protostar
|
|
4
|
+
module Schema
|
|
5
|
+
module Draft
|
|
6
|
+
# A copy of a built file for another pool, its imports taken from that
|
|
7
|
+
# pool, with each descriptor's options replaced by what the block
|
|
8
|
+
# returns (a MessageValue, or nil for none)
|
|
9
|
+
# @param file [Schema::File]
|
|
10
|
+
# @param pool [Pool] holds the copy's imports
|
|
11
|
+
# @param source_info [SourceInfo, nil]
|
|
12
|
+
# @yieldparam descriptor [Descriptor]
|
|
13
|
+
# @return [Schema::File] frozen; not added to the pool
|
|
14
|
+
def self.copy(file, pool:, source_info: file.source_info, &options)
|
|
15
|
+
Copy.new(file, options).call(pool, source_info)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# @api private
|
|
19
|
+
class Copy
|
|
20
|
+
def initialize(file, options)
|
|
21
|
+
@file = file
|
|
22
|
+
@options = options
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def call(pool, source_info)
|
|
26
|
+
file = @file
|
|
27
|
+
syntax = file.proto_syntax&.to_sym
|
|
28
|
+
draft = Schema::File.draft(file.name, package: file.package, syntax: syntax, edition: file.edition,
|
|
29
|
+
dependencies: file.dependencies, public_dependencies: file.public_dependencies,
|
|
30
|
+
weak_dependencies: file.weak_dependencies, option_dependencies: file.option_dependencies,
|
|
31
|
+
options: options(file))
|
|
32
|
+
draft.source_info = source_info if source_info
|
|
33
|
+
file.messages.each { |m| message(draft, m) }
|
|
34
|
+
file.enums.each { |e| enum(draft, e) }
|
|
35
|
+
file.extensions.each { |f| field(draft, f, nil) }
|
|
36
|
+
file.services.each do |service|
|
|
37
|
+
draft.service(service.name, options: options(service)) do |s|
|
|
38
|
+
service.rpcs.each do |m|
|
|
39
|
+
s.rpc(m.name, ".#{m.input_type.full_name}", ".#{m.output_type.full_name}",
|
|
40
|
+
client_streaming: m.client_streaming?, server_streaming: m.server_streaming?, options: options(m))
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
draft.resolve(pool).freeze!
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
private
|
|
48
|
+
|
|
49
|
+
def options(descriptor)
|
|
50
|
+
@options.call(descriptor)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def message(scope, message)
|
|
54
|
+
scope.message(message.name, visibility: message.visibility, options: options(message)) do |m|
|
|
55
|
+
oneofs = message.oneofs.map { |o| m.oneof(o.name, options: options(o)) }
|
|
56
|
+
message.fields.each { |f| field(m, f, f.oneof && oneofs[f.oneof.index]) }
|
|
57
|
+
message.nested_messages.each { |n| message(m, n) }
|
|
58
|
+
message.nested_enums.each { |e| enum(m, e) }
|
|
59
|
+
message.extensions.each { |f| field(m, f, nil) }
|
|
60
|
+
message.extension_range_descriptors.each { |r| m.extension_range(r.range, options: options(r)) }
|
|
61
|
+
message.reserved_ranges.each { |r| m.reserved(r) }
|
|
62
|
+
m.reserved(*message.reserved_names)
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def field(scope, field, oneof)
|
|
67
|
+
type_name = field.type_name && ".#{field.type_name}"
|
|
68
|
+
kwargs = {label: field.declared_label, default: field.raw_default, json_name: field.json_name,
|
|
69
|
+
optional: field.proto3_optional?, options: options(field)}
|
|
70
|
+
if field.extension?
|
|
71
|
+
scope.extension(field.name, field.number, field.declared_type, type_name,
|
|
72
|
+
extendee: ".#{field.extendee.full_name}", **kwargs)
|
|
73
|
+
else
|
|
74
|
+
scope.field(field.name, field.number, field.declared_type, type_name, oneof: oneof, **kwargs)
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def enum(scope, enum)
|
|
79
|
+
scope.enum(enum.name, visibility: enum.visibility, options: options(enum)) do |e|
|
|
80
|
+
enum.values.each { |v| e.value(v.name, v.number, options: options(v)) }
|
|
81
|
+
enum.reserved_ranges.each { |r| e.reserved(r) }
|
|
82
|
+
e.reserved(*enum.reserved_names)
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Protostar
|
|
4
|
+
module Schema
|
|
5
|
+
module Draft
|
|
6
|
+
# Options, features and defaults of a linked file, as protoc's
|
|
7
|
+
# DescriptorBuilder resolves them once options are known
|
|
8
|
+
# @api private
|
|
9
|
+
class Resolution
|
|
10
|
+
OPTIONS = {
|
|
11
|
+
Schema::File => FileOptions, Schema::Message => MessageOptions, Field => FieldOptions,
|
|
12
|
+
Schema::Oneof => OneofOptions, Schema::Enum => EnumOptions, EnumValue => EnumValueOptions,
|
|
13
|
+
Schema::Service => ServiceOptions, Schema::Method => MethodOptions,
|
|
14
|
+
ExtensionRange => ExtensionRangeOptions
|
|
15
|
+
}.freeze
|
|
16
|
+
|
|
17
|
+
def initialize(file, fields, specs, pool)
|
|
18
|
+
@file = file
|
|
19
|
+
@fields = fields
|
|
20
|
+
@specs = specs
|
|
21
|
+
@pool = pool
|
|
22
|
+
@features = {}
|
|
23
|
+
@empty = {}
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def call
|
|
27
|
+
finder = extension_finder
|
|
28
|
+
@declared = {}.compare_by_identity
|
|
29
|
+
@specs.each do |descriptor, spec|
|
|
30
|
+
options = OPTIONS.fetch(descriptor.class).build(spec, finder, descriptor.full_name)
|
|
31
|
+
@declared[descriptor] = options if options
|
|
32
|
+
end
|
|
33
|
+
resolve_features
|
|
34
|
+
parse_defaults
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
private
|
|
38
|
+
|
|
39
|
+
# Custom options are extensions of this file, its imports and theirs,
|
|
40
|
+
# or else of whatever the pool holds (`import option` files)
|
|
41
|
+
def extension_finder
|
|
42
|
+
files = []
|
|
43
|
+
seen = {}
|
|
44
|
+
queue = [@file]
|
|
45
|
+
until queue.empty?
|
|
46
|
+
f = queue.shift
|
|
47
|
+
next if seen[f.name]
|
|
48
|
+
seen[f.name] = true
|
|
49
|
+
files << f
|
|
50
|
+
queue.concat(f.dependency_files)
|
|
51
|
+
end
|
|
52
|
+
lambda do |name|
|
|
53
|
+
files.each do |f|
|
|
54
|
+
found = f.find_extension(name)
|
|
55
|
+
return found if found
|
|
56
|
+
end
|
|
57
|
+
found = @pool&.find_symbol(name)
|
|
58
|
+
found if found.is_a?(Field) && found.extension?
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def declared(descriptor)
|
|
63
|
+
@declared[descriptor]
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# Options of this file's messages are bound in tree order, so a
|
|
67
|
+
# message type elsewhere in the file may not be bound yet
|
|
68
|
+
def map_entry?(message)
|
|
69
|
+
return message.map_entry? unless message.file.equal?(@file)
|
|
70
|
+
declared(message)&.[](:map_entry) == true
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def bind(descriptor, features)
|
|
74
|
+
options = @declared[descriptor]
|
|
75
|
+
bound = if options
|
|
76
|
+
options.with_features(features)
|
|
77
|
+
else
|
|
78
|
+
klass = OPTIONS.fetch(descriptor.class)
|
|
79
|
+
(@empty[klass] ||= {})[features] ||= klass.new(features: features)
|
|
80
|
+
end
|
|
81
|
+
descriptor.__link(features: features, options: bound)
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def resolve_features
|
|
85
|
+
file_features = intern(Features.defaults(@file.edition_number).merge(declared(@file)&.[](:features)))
|
|
86
|
+
bind(@file, file_features)
|
|
87
|
+
@file.messages.each { |m| resolve_message_features(m, file_features) }
|
|
88
|
+
@file.enums.each { |e| resolve_enum_features(e, file_features) }
|
|
89
|
+
@file.extensions.each { |f| resolve_field_features(f, file_features) }
|
|
90
|
+
@file.services.each do |service|
|
|
91
|
+
service_features = inherit(file_features, service)
|
|
92
|
+
service.rpcs.each { |m| inherit(service_features, m) }
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def resolve_message_features(message, parent)
|
|
97
|
+
features = inherit(parent, message)
|
|
98
|
+
message.oneofs.each { |o| inherit(features, o) }
|
|
99
|
+
message.fields.each { |f| resolve_field_features(f, f.oneof ? f.oneof.features : features) }
|
|
100
|
+
message.extension_range_descriptors.each { |r| inherit(features, r) }
|
|
101
|
+
message.nested_messages.each { |m| resolve_message_features(m, features) }
|
|
102
|
+
message.nested_enums.each { |e| resolve_enum_features(e, features) }
|
|
103
|
+
message.extensions.each { |f| resolve_field_features(f, features) }
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def resolve_enum_features(enum, parent)
|
|
107
|
+
features = inherit(parent, enum)
|
|
108
|
+
enum.values.each { |v| inherit(features, v) }
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# protoc's InferLegacyProtoFeatures for proto2 and proto3 files, then the
|
|
112
|
+
# label and type adjustments of DescriptorBuilder::PostProcessFieldFeatures
|
|
113
|
+
def resolve_field_features(field, parent)
|
|
114
|
+
options = declared(field)
|
|
115
|
+
features = parent.merge(options&.[](:features))
|
|
116
|
+
if @file.syntax != :editions
|
|
117
|
+
legacy = nil
|
|
118
|
+
legacy = {field_presence: :LEGACY_REQUIRED} if field.declared_label == :required
|
|
119
|
+
(legacy ||= {})[:message_encoding] = :DELIMITED if field.declared_type == :group
|
|
120
|
+
if options&.key?(:packed)
|
|
121
|
+
(legacy ||= {})[:repeated_field_encoding] = options[:packed] ? :PACKED : :EXPANDED
|
|
122
|
+
end
|
|
123
|
+
(legacy ||= {})[:field_presence] = :EXPLICIT if field.proto3_optional?
|
|
124
|
+
features = features.with(**legacy) if legacy
|
|
125
|
+
end
|
|
126
|
+
features = intern(features)
|
|
127
|
+
label = field.declared_label
|
|
128
|
+
label = :required if label == :optional && features.field_presence == :LEGACY_REQUIRED
|
|
129
|
+
type = field.declared_type
|
|
130
|
+
if type == :message && features.message_encoding == :DELIMITED
|
|
131
|
+
holder = field.extension? ? field.extendee : field.containing_message
|
|
132
|
+
type = :group unless map_entry?(holder) || map_entry?(field.message_type)
|
|
133
|
+
end
|
|
134
|
+
bind(field, features)
|
|
135
|
+
field.__link(label: label, type: type)
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
def inherit(parent, descriptor)
|
|
139
|
+
features = intern(parent.merge(declared(descriptor)&.[](:features)))
|
|
140
|
+
bind(descriptor, features)
|
|
141
|
+
features
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def intern(features)
|
|
145
|
+
@features[features] ||= features
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
def parse_defaults
|
|
149
|
+
@fields.each do |field, _scope|
|
|
150
|
+
value = nil
|
|
151
|
+
if (text = field.raw_default)
|
|
152
|
+
begin
|
|
153
|
+
value = DefaultValue.parse(text, field.type, field.enum_type)
|
|
154
|
+
rescue Error => e
|
|
155
|
+
raise Error, "#{@file.name}: #{field.full_name}: #{e.message}"
|
|
156
|
+
end
|
|
157
|
+
end
|
|
158
|
+
implicit = if !value.nil?
|
|
159
|
+
value
|
|
160
|
+
elsif field.type == :enum
|
|
161
|
+
field.enum_type.values.first&.name&.to_sym
|
|
162
|
+
else
|
|
163
|
+
Field::ZERO[field.type]
|
|
164
|
+
end
|
|
165
|
+
field.__link(default_value: value, default: implicit)
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
end
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
class Options
|
|
172
|
+
# What a descriptor answers before its options are resolved: an empty
|
|
173
|
+
# instance of its options class
|
|
174
|
+
UNRESOLVED = Draft::Resolution::OPTIONS.transform_values { |klass| klass.new.freeze }.freeze
|
|
175
|
+
end
|
|
176
|
+
end
|
|
177
|
+
end
|