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,779 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Protostar
|
|
4
|
+
module Schema
|
|
5
|
+
# The construction stage of {Schema}: mutable handles that create
|
|
6
|
+
# descriptors, then link, resolve and freeze them. Map entries, the
|
|
7
|
+
# synthetic oneofs of proto3 `optional` fields, group fields and json
|
|
8
|
+
# names are made here and nowhere else.
|
|
9
|
+
module Draft
|
|
10
|
+
SYNTAXES = {nil => :proto2, :proto2 => :proto2, :proto3 => :proto3, :editions => :editions}.freeze
|
|
11
|
+
SCALARS = (Field::TYPES.values - %i[message enum group]).to_h { |t| [t, true] }.freeze
|
|
12
|
+
TYPES = Field::TYPES.values.to_h { |t| [t, true] }.freeze
|
|
13
|
+
LABELS = %i[optional required repeated].freeze
|
|
14
|
+
|
|
15
|
+
# protoc's name for the entry message of a map field: the field name in
|
|
16
|
+
# CamelCase plus "Entry"
|
|
17
|
+
# @param field_name [String]
|
|
18
|
+
# @return [String]
|
|
19
|
+
def self.map_entry_name(field_name)
|
|
20
|
+
out = +""
|
|
21
|
+
cap_next = true
|
|
22
|
+
field_name.to_s.each_char do |c|
|
|
23
|
+
if c == "_"
|
|
24
|
+
cap_next = true
|
|
25
|
+
elsif cap_next
|
|
26
|
+
out << (c.between?("a", "z") ? c.upcase : c)
|
|
27
|
+
cap_next = false
|
|
28
|
+
else
|
|
29
|
+
out << c
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
out << "Entry"
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# The names protoc's parser gives the synthetic oneofs of proto3
|
|
36
|
+
# `optional` fields: `_` and the field name, with `X` prepended until
|
|
37
|
+
# nothing in the message has that name
|
|
38
|
+
# @param field_names [Array<String>] the fields needing one, in order
|
|
39
|
+
# @param taken [Array<String>] every field and oneof name of the message
|
|
40
|
+
# @return [Array<String>]
|
|
41
|
+
def self.synthetic_oneof_names(field_names, taken)
|
|
42
|
+
names = taken.to_h { |n| [n, true] }
|
|
43
|
+
field_names.map do |field_name|
|
|
44
|
+
name = field_name.start_with?("_") ? field_name : "_#{field_name}"
|
|
45
|
+
name = "X#{name}" while names.key?(name)
|
|
46
|
+
names[name] = true
|
|
47
|
+
name
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# The field a `group Name = n { ... }` declares: the group's name in lower case
|
|
52
|
+
# @return [String]
|
|
53
|
+
def self.group_field_name(group_name)
|
|
54
|
+
group_name.to_s.downcase(:ascii)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# protoc's spelling of a default value
|
|
58
|
+
# @api private
|
|
59
|
+
def self.default_text(value)
|
|
60
|
+
case value
|
|
61
|
+
when nil, String then value
|
|
62
|
+
when Symbol then value.to_s
|
|
63
|
+
when Float then DefaultValue.simple_dtoa(value)
|
|
64
|
+
else value.to_s
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
VISIBILITIES = {
|
|
69
|
+
nil => nil, :local => :local, :export => :export, :VISIBILITY_LOCAL => :local,
|
|
70
|
+
:VISIBILITY_EXPORT => :export, :VISIBILITY_UNSET => nil
|
|
71
|
+
}.freeze
|
|
72
|
+
|
|
73
|
+
# :local or :export from either spelling; nil when unset
|
|
74
|
+
# @api private
|
|
75
|
+
def self.visibility(value)
|
|
76
|
+
VISIBILITIES.fetch(value) { raise Error, "unknown visibility #{value.inspect}" }
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# Inclusive bounds of an Integer or a Range
|
|
80
|
+
# @api private
|
|
81
|
+
def self.bounds(item)
|
|
82
|
+
case item
|
|
83
|
+
when Integer then [item, item]
|
|
84
|
+
when ::Range
|
|
85
|
+
last = item.exclude_end? ? item.end - 1 : item.end
|
|
86
|
+
[item.begin, last]
|
|
87
|
+
else raise Error, "expected an Integer or a Range, got #{item.inspect}"
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# What files and messages share: nested messages, enums and extensions
|
|
92
|
+
class Scope
|
|
93
|
+
# @api private
|
|
94
|
+
attr_reader :file_draft
|
|
95
|
+
|
|
96
|
+
# A message type
|
|
97
|
+
# @param name [String]
|
|
98
|
+
# @param visibility [Symbol, nil] :local or :export (edition 2024)
|
|
99
|
+
# @param options [Hash, nil]
|
|
100
|
+
# @yieldparam m [Message]
|
|
101
|
+
# @return [Message]
|
|
102
|
+
def message(name, visibility: nil, options: nil, &block)
|
|
103
|
+
name = name.to_s
|
|
104
|
+
descriptor = Schema::Message.new(name: name, full_name: qualify(name), file: @file_draft.descriptor,
|
|
105
|
+
containing_message: containing_message, index: @messages.size)
|
|
106
|
+
draft = Message.new(@file_draft, descriptor, Draft.visibility(visibility), options)
|
|
107
|
+
@messages << draft
|
|
108
|
+
@file_draft.register(descriptor)
|
|
109
|
+
block&.call(draft)
|
|
110
|
+
draft
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
# An enum type
|
|
114
|
+
# @yieldparam e [Enum]
|
|
115
|
+
# @return [Enum]
|
|
116
|
+
def enum(name, visibility: nil, options: nil, &block)
|
|
117
|
+
name = name.to_s
|
|
118
|
+
descriptor = Schema::Enum.new(name: name, full_name: qualify(name), file: @file_draft.descriptor,
|
|
119
|
+
containing_message: containing_message, index: @enums.size)
|
|
120
|
+
draft = Enum.new(@file_draft, descriptor, scope_name, Draft.visibility(visibility), options)
|
|
121
|
+
@enums << draft
|
|
122
|
+
@file_draft.register(descriptor)
|
|
123
|
+
block&.call(draft)
|
|
124
|
+
draft
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
# Extensions of another message, declared in this scope
|
|
128
|
+
# @param extendee [String] the extended message, as written
|
|
129
|
+
# @yieldparam e [Extend]
|
|
130
|
+
def extend(extendee, &block)
|
|
131
|
+
block.call(Extend.new(self, extendee.to_s))
|
|
132
|
+
self
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
# One extension field; the arguments of {Message#field} plus the extendee
|
|
136
|
+
# @return [Schema::Field]
|
|
137
|
+
def extension(name, number, type, type_name = nil, extendee:, label: :optional, default: nil, json_name: nil,
|
|
138
|
+
optional: false, options: nil)
|
|
139
|
+
field = @file_draft.new_field(self, name, number, type, type_name, @extensions.size, extendee.to_s, label,
|
|
140
|
+
default, json_name, optional, options, nil)
|
|
141
|
+
@extensions << field
|
|
142
|
+
field
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
# @api private
|
|
146
|
+
def qualify(name)
|
|
147
|
+
scope = scope_name
|
|
148
|
+
-(scope.empty? ? name : "#{scope}.#{name}")
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
def inspect
|
|
152
|
+
"#<#{self.class.name} #{@descriptor.full_name}>"
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
# A file under construction
|
|
157
|
+
class File < Scope
|
|
158
|
+
# @return [Schema::File] the file being built; frozen after {#freeze!}
|
|
159
|
+
attr_reader :descriptor
|
|
160
|
+
|
|
161
|
+
# @api private
|
|
162
|
+
def initialize(name, package:, syntax:, edition:, dependencies:, public_dependencies:, weak_dependencies:,
|
|
163
|
+
option_dependencies:, options:)
|
|
164
|
+
@file_draft = self
|
|
165
|
+
@messages = []
|
|
166
|
+
@enums = []
|
|
167
|
+
@extensions = []
|
|
168
|
+
@services = []
|
|
169
|
+
@fields = []
|
|
170
|
+
@methods = []
|
|
171
|
+
@duplicates = []
|
|
172
|
+
@options = {}.compare_by_identity
|
|
173
|
+
@dependencies = Array(dependencies).map { |d| -d.to_s }.freeze
|
|
174
|
+
@public = indexes(name, public_dependencies)
|
|
175
|
+
@weak = indexes(name, weak_dependencies)
|
|
176
|
+
@option_dependencies = Array(option_dependencies).map { |d| -d.to_s }.freeze
|
|
177
|
+
syntax, edition, proto_syntax = syntax_and_edition(name, syntax, edition)
|
|
178
|
+
@descriptor = Schema::File.new(name: -name.to_s, package: -package.to_s, syntax: syntax, edition: edition,
|
|
179
|
+
proto_syntax: proto_syntax)
|
|
180
|
+
@options[@descriptor] = options
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
# @return [String]
|
|
184
|
+
def scope_name
|
|
185
|
+
@descriptor.package
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
# A service
|
|
189
|
+
# @yieldparam s [Service]
|
|
190
|
+
# @return [Service]
|
|
191
|
+
def service(name, options: nil, &block)
|
|
192
|
+
name = name.to_s
|
|
193
|
+
descriptor = Schema::Service.new(name: name, full_name: qualify(name), file: @descriptor,
|
|
194
|
+
index: @services.size)
|
|
195
|
+
draft = Service.new(self, descriptor)
|
|
196
|
+
@options[descriptor] = options
|
|
197
|
+
@services << draft
|
|
198
|
+
register(descriptor)
|
|
199
|
+
block&.call(draft)
|
|
200
|
+
draft
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
# @param info [SourceInfo, Array<SourceInfo::Location>, nil]
|
|
204
|
+
def source_info=(info)
|
|
205
|
+
info = SourceInfo.new(info) if info.is_a?(Array)
|
|
206
|
+
@descriptor.__link(source_info: info)
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
# Sets (or replaces) the options of a descriptor of this file
|
|
210
|
+
# @param descriptor [Descriptor]
|
|
211
|
+
# @param options [Hash, nil]
|
|
212
|
+
def set_options(descriptor, options)
|
|
213
|
+
@options[descriptor] = options
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
# Finds dependencies in the pool, completes every message's lookup
|
|
217
|
+
# tables and resolves every type name, extendee and method type. Runs
|
|
218
|
+
# once; {#resolve} calls it when it has not run.
|
|
219
|
+
# @param pool [Pool] where the dependencies are
|
|
220
|
+
# @return [self]
|
|
221
|
+
# @raise [Error, ResolveError]
|
|
222
|
+
def link(pool)
|
|
223
|
+
return self if @linked
|
|
224
|
+
unless @duplicates.empty?
|
|
225
|
+
first, second = @duplicates.first
|
|
226
|
+
raise Error, "#{@descriptor.name}: \"#{first.full_name}\" is defined twice " \
|
|
227
|
+
"(#{kind_name(first)} and #{kind_name(second)})"
|
|
228
|
+
end
|
|
229
|
+
files = @dependencies.map do |dep|
|
|
230
|
+
pool.file(dep) || raise(Error, "#{@descriptor.name}: dependency \"#{dep}\" is not in the pool")
|
|
231
|
+
end
|
|
232
|
+
@descriptor.__dependencies(dependencies: @dependencies, public_dependencies: @public,
|
|
233
|
+
weak_dependencies: @weak, option_dependencies: @option_dependencies, dependency_files: files.freeze)
|
|
234
|
+
seal
|
|
235
|
+
link_fields(pool)
|
|
236
|
+
link_methods(pool)
|
|
237
|
+
@linked = true
|
|
238
|
+
self
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
# Links (when not done yet), then builds options, resolves features and
|
|
242
|
+
# parses defaults. May run again after options change.
|
|
243
|
+
# @param pool [Pool]
|
|
244
|
+
# @return [self]
|
|
245
|
+
def resolve(pool)
|
|
246
|
+
link(pool)
|
|
247
|
+
Resolution.new(@descriptor, @fields, @options, pool).call
|
|
248
|
+
@resolved = true
|
|
249
|
+
self
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
# Freezes the whole graph and makes it Ractor-shareable
|
|
253
|
+
# @return [Schema::File]
|
|
254
|
+
def freeze!
|
|
255
|
+
raise Error, "#{@descriptor.name}: resolve the draft before freezing it" unless @resolved
|
|
256
|
+
Freezer.deep_freeze(@descriptor)
|
|
257
|
+
Ractor.make_shareable(@descriptor)
|
|
258
|
+
end
|
|
259
|
+
|
|
260
|
+
# Completes every message's, enum's and service's lookup tables once
|
|
261
|
+
# nothing more will be added. {#link} does it; a caller that resolves
|
|
262
|
+
# names itself (the compiler) calls it first.
|
|
263
|
+
# @api private
|
|
264
|
+
def __seal
|
|
265
|
+
seal
|
|
266
|
+
self
|
|
267
|
+
end
|
|
268
|
+
|
|
269
|
+
# Sets what a field's type name resolved to, for a caller that
|
|
270
|
+
# resolves names itself; {#link} then leaves the field alone
|
|
271
|
+
# @api private
|
|
272
|
+
def __resolve_type(field, target)
|
|
273
|
+
type = field.declared_type || (target.is_a?(Schema::Message) ? :message : :enum)
|
|
274
|
+
kind = target.is_a?(Schema::Message) ? :message_type : :enum_type
|
|
275
|
+
field.__link(kind => target, :type_name => target.full_name, :declared_type => type, :type => type)
|
|
276
|
+
end
|
|
277
|
+
|
|
278
|
+
# @api private
|
|
279
|
+
def __resolve_extendee(field, target)
|
|
280
|
+
field.__link(extendee: target)
|
|
281
|
+
end
|
|
282
|
+
|
|
283
|
+
# @api private
|
|
284
|
+
def __resolve_rpc(method, input: nil, output: nil)
|
|
285
|
+
method.__link(input_type: input) if input
|
|
286
|
+
method.__link(output_type: output) if output
|
|
287
|
+
end
|
|
288
|
+
|
|
289
|
+
# @api private
|
|
290
|
+
def register(descriptor)
|
|
291
|
+
symbols = @descriptor.symbols
|
|
292
|
+
if (existing = symbols[descriptor.full_name])
|
|
293
|
+
@duplicates << [existing, descriptor]
|
|
294
|
+
else
|
|
295
|
+
symbols[descriptor.full_name] = descriptor
|
|
296
|
+
end
|
|
297
|
+
end
|
|
298
|
+
|
|
299
|
+
# @api private
|
|
300
|
+
def new_field(scope, name, number, type, type_name, index, extendee, label, default, json_name, optional, options,
|
|
301
|
+
oneof)
|
|
302
|
+
name = name.to_s
|
|
303
|
+
raise Error, "#{@descriptor.name}: field #{name} needs an integer number" unless number.is_a?(Integer)
|
|
304
|
+
label = (label || :optional).to_sym
|
|
305
|
+
raise Error, "#{@descriptor.name}: unknown label #{label.inspect} for #{name}" unless LABELS.include?(label)
|
|
306
|
+
if type.is_a?(String)
|
|
307
|
+
raise Error, "#{@descriptor.name}: #{name} has two type names" if type_name
|
|
308
|
+
type_name = type
|
|
309
|
+
type = nil
|
|
310
|
+
elsif !type.nil?
|
|
311
|
+
type = type.to_sym
|
|
312
|
+
raise Error, "#{@descriptor.name}: unknown field type #{type.inspect} for #{name}" unless TYPES.key?(type)
|
|
313
|
+
end
|
|
314
|
+
if type_name.nil? && !SCALARS.key?(type)
|
|
315
|
+
raise Error, "#{@descriptor.name}: #{name} needs a type name"
|
|
316
|
+
elsif type_name && SCALARS.key?(type)
|
|
317
|
+
raise Error, "#{@descriptor.name}: #{name} is a #{type} and cannot have a type name"
|
|
318
|
+
end
|
|
319
|
+
message = scope.is_a?(Message) ? scope.descriptor : nil
|
|
320
|
+
field = Field.new(
|
|
321
|
+
name: name, full_name: scope.qualify(name), number: number, label: label, type: type,
|
|
322
|
+
type_name: type_name && -type_name.to_s, extendee_name: extendee && -extendee, raw_default:
|
|
323
|
+
Draft.default_text(default)&.then { |t| -t }, json_name: -(json_name&.to_s || Schema.json_name(name)),
|
|
324
|
+
proto3_optional: optional ? true : false, extension: !extendee.nil?, containing_message: message,
|
|
325
|
+
file: @descriptor, index: index
|
|
326
|
+
)
|
|
327
|
+
@options[field] = options
|
|
328
|
+
@fields << [field, scope.scope_name]
|
|
329
|
+
register(field)
|
|
330
|
+
scope.__oneof_member(field, oneof) if extendee.nil? && (oneof || optional)
|
|
331
|
+
field
|
|
332
|
+
end
|
|
333
|
+
|
|
334
|
+
# @api private
|
|
335
|
+
def new_method(method, options)
|
|
336
|
+
@options[method] = options
|
|
337
|
+
@methods << method
|
|
338
|
+
register(method)
|
|
339
|
+
end
|
|
340
|
+
|
|
341
|
+
private
|
|
342
|
+
|
|
343
|
+
def kind_name(descriptor)
|
|
344
|
+
descriptor.class.name.split("::").last
|
|
345
|
+
end
|
|
346
|
+
|
|
347
|
+
def indexes(name, list)
|
|
348
|
+
deps = Array(@dependencies)
|
|
349
|
+
list = Array(list).map do |dep|
|
|
350
|
+
next dep if dep.is_a?(Integer) && dep >= 0 && dep < deps.size
|
|
351
|
+
index = deps.index(dep.to_s)
|
|
352
|
+
raise Error, "#{name}: #{dep.inspect} is not among the dependencies" unless index
|
|
353
|
+
index
|
|
354
|
+
end
|
|
355
|
+
list.empty? ? Freezer::EMPTY_ARRAY : list.freeze
|
|
356
|
+
end
|
|
357
|
+
|
|
358
|
+
def syntax_and_edition(name, syntax, edition)
|
|
359
|
+
syntax = syntax.to_sym if syntax.is_a?(String)
|
|
360
|
+
raise Error, "#{name}: unknown syntax #{syntax.inspect}" unless SYNTAXES.key?(syntax)
|
|
361
|
+
proto_syntax = syntax&.to_s
|
|
362
|
+
if edition.is_a?(Integer)
|
|
363
|
+
edition = Features::EDITION_NAMES.fetch(edition, edition)
|
|
364
|
+
elsif edition
|
|
365
|
+
edition = edition.to_sym
|
|
366
|
+
raise Error, "#{name}: unknown edition #{edition.inspect}" unless Features::EDITIONS.key?(edition)
|
|
367
|
+
end
|
|
368
|
+
if edition && syntax.nil?
|
|
369
|
+
syntax = :editions
|
|
370
|
+
proto_syntax = "editions"
|
|
371
|
+
end
|
|
372
|
+
resolved = SYNTAXES.fetch(syntax)
|
|
373
|
+
if resolved == :editions && edition.nil?
|
|
374
|
+
raise Error, "#{name}: syntax \"editions\" needs an edition"
|
|
375
|
+
elsif resolved != :editions && edition
|
|
376
|
+
raise Error, "#{name}: syntax #{proto_syntax.inspect} cannot have an edition"
|
|
377
|
+
end
|
|
378
|
+
[resolved, edition, proto_syntax && -proto_syntax]
|
|
379
|
+
end
|
|
380
|
+
|
|
381
|
+
def containing_message
|
|
382
|
+
nil
|
|
383
|
+
end
|
|
384
|
+
|
|
385
|
+
def seal
|
|
386
|
+
return if @sealed
|
|
387
|
+
@sealed = true
|
|
388
|
+
messages = @messages.map { |m| m.__seal(@options) }.freeze
|
|
389
|
+
enums = @enums.map { |e| e.__seal(@options) }.freeze
|
|
390
|
+
services = @services.map(&:__seal).freeze
|
|
391
|
+
extensions = @extensions.freeze
|
|
392
|
+
all = []
|
|
393
|
+
walk = lambda do |m|
|
|
394
|
+
all << m
|
|
395
|
+
m.nested_messages.each(&walk)
|
|
396
|
+
end
|
|
397
|
+
messages.each(&walk)
|
|
398
|
+
@descriptor.__link(
|
|
399
|
+
messages: messages, enums: enums, services: services, extensions: extensions,
|
|
400
|
+
all_messages: all.freeze, all_enums: (enums + all.flat_map(&:nested_enums)).freeze,
|
|
401
|
+
all_extensions: (extensions + all.flat_map(&:extensions)).freeze
|
|
402
|
+
)
|
|
403
|
+
end
|
|
404
|
+
|
|
405
|
+
def link_fields(pool)
|
|
406
|
+
@fields.each do |field, scope|
|
|
407
|
+
attrs = {}
|
|
408
|
+
if field.type_name && field.message_type.nil? && field.enum_type.nil?
|
|
409
|
+
target = resolve_name(pool, field.type_name, scope, field)
|
|
410
|
+
type = field.declared_type
|
|
411
|
+
case target
|
|
412
|
+
when Schema::Message
|
|
413
|
+
type ||= :message
|
|
414
|
+
unless type == :message || type == :group
|
|
415
|
+
raise ResolveError.new("#{@descriptor.name}: #{field.full_name}: \"#{field.type_name}\" is not an " \
|
|
416
|
+
"enum type", name: field.type_name, scope: scope)
|
|
417
|
+
end
|
|
418
|
+
attrs[:message_type] = target
|
|
419
|
+
when Schema::Enum
|
|
420
|
+
type ||= :enum
|
|
421
|
+
unless type == :enum
|
|
422
|
+
raise ResolveError.new("#{@descriptor.name}: #{field.full_name}: \"#{field.type_name}\" is not a " \
|
|
423
|
+
"message type", name: field.type_name, scope: scope)
|
|
424
|
+
end
|
|
425
|
+
attrs[:enum_type] = target
|
|
426
|
+
else
|
|
427
|
+
raise ResolveError.new("#{@descriptor.name}: #{field.full_name}: \"#{field.type_name}\" is not a type",
|
|
428
|
+
name: field.type_name, scope: scope)
|
|
429
|
+
end
|
|
430
|
+
attrs.merge!(type_name: target.full_name, declared_type: type, type: type)
|
|
431
|
+
end
|
|
432
|
+
if field.extension? && field.extendee.nil?
|
|
433
|
+
extendee = resolve_name(pool, field.extendee_name, scope, field)
|
|
434
|
+
unless extendee.is_a?(Schema::Message)
|
|
435
|
+
raise ResolveError.new("#{@descriptor.name}: #{field.full_name}: \"#{field.extendee_name}\" is not a " \
|
|
436
|
+
"message type", name: field.extendee_name, scope: scope)
|
|
437
|
+
end
|
|
438
|
+
attrs[:extendee] = extendee
|
|
439
|
+
end
|
|
440
|
+
field.__link(**attrs) unless attrs.empty?
|
|
441
|
+
end
|
|
442
|
+
end
|
|
443
|
+
|
|
444
|
+
def link_methods(pool)
|
|
445
|
+
@methods.each do |method|
|
|
446
|
+
next if method.input_type && method.output_type
|
|
447
|
+
scope = method.service.full_name
|
|
448
|
+
input = resolve_name(pool, method.input_type_name, scope, method)
|
|
449
|
+
output = resolve_name(pool, method.output_type_name, scope, method)
|
|
450
|
+
[[method.input_type_name, input], [method.output_type_name, output]].each do |written, found|
|
|
451
|
+
next if found.is_a?(Schema::Message)
|
|
452
|
+
raise ResolveError.new("#{@descriptor.name}: #{method.full_name}: \"#{written}\" is not a message type",
|
|
453
|
+
name: written, scope: scope)
|
|
454
|
+
end
|
|
455
|
+
method.__link(input_type: input, output_type: output)
|
|
456
|
+
end
|
|
457
|
+
end
|
|
458
|
+
|
|
459
|
+
def resolve_name(pool, name, scope, element)
|
|
460
|
+
Resolver.resolve(name, scope: scope, file: @descriptor, pool: pool)
|
|
461
|
+
rescue ResolveError => e
|
|
462
|
+
raise ResolveError.new("#{@descriptor.name}: #{element.full_name}: #{e.message}", name: e.name,
|
|
463
|
+
scope: e.scope)
|
|
464
|
+
end
|
|
465
|
+
end
|
|
466
|
+
|
|
467
|
+
# A message under construction: the block argument of {Scope#message}
|
|
468
|
+
class Message < Scope
|
|
469
|
+
# @return [Schema::Message]
|
|
470
|
+
attr_reader :descriptor
|
|
471
|
+
|
|
472
|
+
# @api private
|
|
473
|
+
def initialize(file_draft, descriptor, visibility, options)
|
|
474
|
+
@file_draft = file_draft
|
|
475
|
+
@descriptor = descriptor
|
|
476
|
+
@visibility = visibility
|
|
477
|
+
@messages = []
|
|
478
|
+
@enums = []
|
|
479
|
+
@extensions = []
|
|
480
|
+
@fields = []
|
|
481
|
+
@oneofs = []
|
|
482
|
+
@oneof_names = {}
|
|
483
|
+
@synthetic = []
|
|
484
|
+
@ranges = []
|
|
485
|
+
@reserved_ranges = []
|
|
486
|
+
@reserved_names = []
|
|
487
|
+
file_draft.set_options(descriptor, options)
|
|
488
|
+
end
|
|
489
|
+
|
|
490
|
+
# @return [String]
|
|
491
|
+
def scope_name
|
|
492
|
+
@descriptor.full_name
|
|
493
|
+
end
|
|
494
|
+
|
|
495
|
+
# A field
|
|
496
|
+
# @param name [String, Symbol]
|
|
497
|
+
# @param number [Integer]
|
|
498
|
+
# @param type [Symbol, String] :int32, :string, ... :message, :enum or :group (then
|
|
499
|
+
# give `type_name`), or a type name whose kind is found when linking
|
|
500
|
+
# @param type_name [String, nil] the message or enum type as written, e.g. ".acme.Person"
|
|
501
|
+
# @param label [Symbol] :optional, :required or :repeated
|
|
502
|
+
# @param default [String, Object, nil] the default; text is taken as protoc stores it
|
|
503
|
+
# @param json_name [String, nil] when not the one derived from the name
|
|
504
|
+
# @param optional [Boolean] a proto3 `optional` field; its synthetic oneof is made here
|
|
505
|
+
# unless `oneof:` names one
|
|
506
|
+
# @param oneof [String, Symbol, Schema::Oneof, nil] the oneof the field belongs to
|
|
507
|
+
# @param options [Hash, nil]
|
|
508
|
+
# @return [Schema::Field]
|
|
509
|
+
def field(name, number, type, type_name = nil, label: :optional, default: nil, json_name: nil, optional: false,
|
|
510
|
+
oneof: nil, options: nil)
|
|
511
|
+
field = @file_draft.new_field(self, name, number, type, type_name, @fields.size, nil, label, default,
|
|
512
|
+
json_name, optional, options, oneof)
|
|
513
|
+
@fields << field
|
|
514
|
+
field
|
|
515
|
+
end
|
|
516
|
+
|
|
517
|
+
# A `map<K, V>` field and the entry message protoc synthesizes for it
|
|
518
|
+
# @param key [Symbol] the key type
|
|
519
|
+
# @param value [Symbol, String] a scalar type, or the value's message or enum type name
|
|
520
|
+
# @param value_type_name [String, nil] the type name when `value` is :message or :enum
|
|
521
|
+
# @param options [Hash, nil] the field's options; its `features` go to the key and value too
|
|
522
|
+
# @return [Schema::Field]
|
|
523
|
+
def map(name, number, key:, value:, value_type_name: nil, json_name: nil, options: nil)
|
|
524
|
+
name = name.to_s
|
|
525
|
+
if value.is_a?(String)
|
|
526
|
+
value_type_name = value
|
|
527
|
+
value = nil
|
|
528
|
+
end
|
|
529
|
+
features = options && (options[:features] || options["features"])
|
|
530
|
+
entry_options = features && {features: features}
|
|
531
|
+
entry = message(Draft.map_entry_name(name), options: {map_entry: true})
|
|
532
|
+
entry.field("key", 1, key, options: entry_options)
|
|
533
|
+
entry.field("value", 2, value, value_type_name, options: entry_options)
|
|
534
|
+
field(name, number, :message, ".#{entry.descriptor.full_name}", label: :repeated, json_name: json_name,
|
|
535
|
+
options: options)
|
|
536
|
+
end
|
|
537
|
+
|
|
538
|
+
# A proto2 group: a nested message and a field of type group named after it
|
|
539
|
+
# @param message_options [Hash, nil] the nested message's options
|
|
540
|
+
# @yieldparam g [Message]
|
|
541
|
+
# @return [Schema::Field]
|
|
542
|
+
def group(name, number, label: :optional, json_name: nil, oneof: nil, options: nil, message_options: nil,
|
|
543
|
+
&block)
|
|
544
|
+
nested = message(name.to_s, options: message_options, &block)
|
|
545
|
+
field(Draft.group_field_name(name), number, :group, ".#{nested.descriptor.full_name}", label: label,
|
|
546
|
+
json_name: json_name, oneof: oneof, options: options)
|
|
547
|
+
end
|
|
548
|
+
|
|
549
|
+
# A oneof. Fields given in the block belong to it; fields elsewhere may
|
|
550
|
+
# name it with `oneof:`. Declaring it again only sets its options.
|
|
551
|
+
# @yieldparam o [Oneof]
|
|
552
|
+
# @return [Schema::Oneof]
|
|
553
|
+
def oneof(name, options: nil, &block)
|
|
554
|
+
oneof = declare_oneof(name.to_s)
|
|
555
|
+
@file_draft.set_options(oneof, options) if options
|
|
556
|
+
block&.call(Oneof.new(self, oneof))
|
|
557
|
+
oneof
|
|
558
|
+
end
|
|
559
|
+
|
|
560
|
+
# One extension range, inclusive
|
|
561
|
+
# @param range [Range, Integer]
|
|
562
|
+
# @return [Schema::ExtensionRange]
|
|
563
|
+
def extension_range(range, options: nil)
|
|
564
|
+
first, last = Draft.bounds(range)
|
|
565
|
+
descriptor = Schema::ExtensionRange.new(containing_message: @descriptor, index: @ranges.size,
|
|
566
|
+
range: first..last)
|
|
567
|
+
@file_draft.set_options(descriptor, options)
|
|
568
|
+
@ranges << descriptor
|
|
569
|
+
descriptor
|
|
570
|
+
end
|
|
571
|
+
|
|
572
|
+
# Extension ranges, inclusive, sharing options
|
|
573
|
+
# @param ranges [Array<Range, Integer>]
|
|
574
|
+
def extensions(*ranges, options: nil)
|
|
575
|
+
ranges.each { |r| extension_range(r, options: options) }
|
|
576
|
+
self
|
|
577
|
+
end
|
|
578
|
+
|
|
579
|
+
# Reserved numbers (Integers or inclusive Ranges) and names (Strings or Symbols)
|
|
580
|
+
def reserved(*items)
|
|
581
|
+
items.each do |item|
|
|
582
|
+
if item.is_a?(String) || item.is_a?(Symbol)
|
|
583
|
+
@reserved_names << -item.to_s
|
|
584
|
+
else
|
|
585
|
+
first, last = Draft.bounds(item)
|
|
586
|
+
@reserved_ranges << (first..last)
|
|
587
|
+
end
|
|
588
|
+
end
|
|
589
|
+
self
|
|
590
|
+
end
|
|
591
|
+
|
|
592
|
+
# @api private
|
|
593
|
+
def __oneof_member(field, oneof)
|
|
594
|
+
if oneof.nil?
|
|
595
|
+
@synthetic << field
|
|
596
|
+
return
|
|
597
|
+
end
|
|
598
|
+
oneof = declare_oneof(oneof.to_s) unless oneof.is_a?(Schema::Oneof)
|
|
599
|
+
oneof.fields << field
|
|
600
|
+
field.__link(oneof: oneof)
|
|
601
|
+
end
|
|
602
|
+
|
|
603
|
+
# @api private
|
|
604
|
+
def __seal(options)
|
|
605
|
+
unless @synthetic.empty?
|
|
606
|
+
taken = @fields.map(&:name) + @oneofs.map(&:name)
|
|
607
|
+
names = Draft.synthetic_oneof_names(@synthetic.map(&:name), taken)
|
|
608
|
+
@synthetic.zip(names) { |field, name| __oneof_member(field, declare_oneof(name)) }
|
|
609
|
+
@synthetic = []
|
|
610
|
+
end
|
|
611
|
+
@oneofs.each do |oneof|
|
|
612
|
+
members = oneof.fields.freeze
|
|
613
|
+
oneof.__link(synthetic: members.size == 1 && members.first.proto3_optional?)
|
|
614
|
+
end
|
|
615
|
+
nested = @messages.map { |m| m.__seal(options) }.freeze
|
|
616
|
+
enums = @enums.map { |e| e.__seal(options) }.freeze
|
|
617
|
+
@descriptor.__link(reserved_ranges: frozen(@reserved_ranges), reserved_names: frozen(@reserved_names),
|
|
618
|
+
visibility: @visibility)
|
|
619
|
+
@descriptor.__members(fields: frozen(@fields), oneofs: frozen(@oneofs), nested_messages: nested,
|
|
620
|
+
nested_enums: enums, extensions: frozen(@extensions), extension_range_descriptors: frozen(@ranges))
|
|
621
|
+
end
|
|
622
|
+
|
|
623
|
+
private
|
|
624
|
+
|
|
625
|
+
def containing_message
|
|
626
|
+
@descriptor
|
|
627
|
+
end
|
|
628
|
+
|
|
629
|
+
def declare_oneof(name)
|
|
630
|
+
existing = @oneof_names[name]
|
|
631
|
+
return existing if existing
|
|
632
|
+
oneof = Schema::Oneof.new(name: -name, full_name: -"#{@descriptor.full_name}.#{name}",
|
|
633
|
+
containing_message: @descriptor, index: @oneofs.size)
|
|
634
|
+
@oneofs << oneof
|
|
635
|
+
@oneof_names[name] = oneof
|
|
636
|
+
@file_draft.set_options(oneof, nil)
|
|
637
|
+
@file_draft.register(oneof)
|
|
638
|
+
oneof
|
|
639
|
+
end
|
|
640
|
+
|
|
641
|
+
def frozen(list)
|
|
642
|
+
list.empty? ? Freezer::EMPTY_ARRAY : list.dup.freeze
|
|
643
|
+
end
|
|
644
|
+
end
|
|
645
|
+
|
|
646
|
+
# The block argument of {Message#oneof}
|
|
647
|
+
class Oneof
|
|
648
|
+
# @api private
|
|
649
|
+
def initialize(message, oneof)
|
|
650
|
+
@message = message
|
|
651
|
+
@oneof = oneof
|
|
652
|
+
end
|
|
653
|
+
|
|
654
|
+
# A member field; the arguments of {Message#field}
|
|
655
|
+
def field(name, number, type, type_name = nil, **kwargs)
|
|
656
|
+
@message.field(name, number, type, type_name, oneof: @oneof, **kwargs)
|
|
657
|
+
end
|
|
658
|
+
|
|
659
|
+
# A member group; the arguments of {Message#group}
|
|
660
|
+
def group(name, number, **kwargs, &block)
|
|
661
|
+
@message.group(name, number, oneof: @oneof, **kwargs, &block)
|
|
662
|
+
end
|
|
663
|
+
end
|
|
664
|
+
|
|
665
|
+
# The block argument of {Scope#extend}
|
|
666
|
+
class Extend
|
|
667
|
+
# @api private
|
|
668
|
+
def initialize(scope, extendee)
|
|
669
|
+
@scope = scope
|
|
670
|
+
@extendee = extendee
|
|
671
|
+
end
|
|
672
|
+
|
|
673
|
+
# An extension field; the arguments of {Message#field}
|
|
674
|
+
# @return [Schema::Field]
|
|
675
|
+
def field(name, number, type, type_name = nil, **kwargs)
|
|
676
|
+
@scope.extension(name, number, type, type_name, extendee: @extendee, **kwargs)
|
|
677
|
+
end
|
|
678
|
+
end
|
|
679
|
+
|
|
680
|
+
# An enum under construction: the block argument of {Scope#enum}
|
|
681
|
+
class Enum
|
|
682
|
+
def inspect
|
|
683
|
+
"#<#{self.class.name} #{@descriptor.full_name}>"
|
|
684
|
+
end
|
|
685
|
+
|
|
686
|
+
# @return [Schema::Enum]
|
|
687
|
+
attr_reader :descriptor
|
|
688
|
+
|
|
689
|
+
# @api private
|
|
690
|
+
def initialize(file_draft, descriptor, scope, visibility, options)
|
|
691
|
+
@file_draft = file_draft
|
|
692
|
+
@descriptor = descriptor
|
|
693
|
+
@scope = scope
|
|
694
|
+
@visibility = visibility
|
|
695
|
+
@values = []
|
|
696
|
+
@reserved_ranges = []
|
|
697
|
+
@reserved_names = []
|
|
698
|
+
file_draft.set_options(descriptor, options)
|
|
699
|
+
end
|
|
700
|
+
|
|
701
|
+
# A value. Its full name is a sibling of the enum's, as in C++.
|
|
702
|
+
# @return [Schema::EnumValue]
|
|
703
|
+
def value(name, number, options: nil)
|
|
704
|
+
name = name.to_s
|
|
705
|
+
raise Error, "#{@descriptor.full_name}: value #{name} needs an integer number" unless number.is_a?(Integer)
|
|
706
|
+
value = EnumValue.new(name: -name, full_name: -(@scope.empty? ? name : "#{@scope}.#{name}"),
|
|
707
|
+
number: number, enum: @descriptor, index: @values.size)
|
|
708
|
+
@file_draft.set_options(value, options)
|
|
709
|
+
@file_draft.register(value)
|
|
710
|
+
@values << value
|
|
711
|
+
value
|
|
712
|
+
end
|
|
713
|
+
|
|
714
|
+
# Reserved numbers (Integers or inclusive Ranges) and names
|
|
715
|
+
def reserved(*items)
|
|
716
|
+
items.each do |item|
|
|
717
|
+
if item.is_a?(String) || item.is_a?(Symbol)
|
|
718
|
+
@reserved_names << -item.to_s
|
|
719
|
+
else
|
|
720
|
+
first, last = Draft.bounds(item)
|
|
721
|
+
@reserved_ranges << (first..last)
|
|
722
|
+
end
|
|
723
|
+
end
|
|
724
|
+
self
|
|
725
|
+
end
|
|
726
|
+
|
|
727
|
+
# @api private
|
|
728
|
+
def __seal(_options)
|
|
729
|
+
@descriptor.__link(
|
|
730
|
+
reserved_ranges: @reserved_ranges.empty? ? Freezer::EMPTY_ARRAY : @reserved_ranges.dup.freeze,
|
|
731
|
+
reserved_names: @reserved_names.empty? ? Freezer::EMPTY_ARRAY : @reserved_names.dup.freeze,
|
|
732
|
+
visibility: @visibility
|
|
733
|
+
)
|
|
734
|
+
@descriptor.__values(@values.empty? ? Freezer::EMPTY_ARRAY : @values.dup.freeze)
|
|
735
|
+
end
|
|
736
|
+
end
|
|
737
|
+
|
|
738
|
+
# A service under construction: the block argument of {File#service}
|
|
739
|
+
class Service
|
|
740
|
+
def inspect
|
|
741
|
+
"#<#{self.class.name} #{@descriptor.full_name}>"
|
|
742
|
+
end
|
|
743
|
+
|
|
744
|
+
# @return [Schema::Service]
|
|
745
|
+
attr_reader :descriptor
|
|
746
|
+
|
|
747
|
+
# @api private
|
|
748
|
+
def initialize(file_draft, descriptor)
|
|
749
|
+
@file_draft = file_draft
|
|
750
|
+
@descriptor = descriptor
|
|
751
|
+
@rpcs = []
|
|
752
|
+
end
|
|
753
|
+
|
|
754
|
+
# @param name [String, Symbol]
|
|
755
|
+
# @param input_type [String] as written, e.g. ".acme.Request"
|
|
756
|
+
# @param output_type [String]
|
|
757
|
+
# @return [Schema::Method]
|
|
758
|
+
def rpc(name, input_type, output_type, client_streaming: false, server_streaming: false, options: nil)
|
|
759
|
+
name = name.to_s
|
|
760
|
+
method = Schema::Method.new(name: -name, full_name: -"#{@descriptor.full_name}.#{name}",
|
|
761
|
+
service: @descriptor, index: @rpcs.size, input_type_name: -input_type.to_s,
|
|
762
|
+
output_type_name: -output_type.to_s, client_streaming: client_streaming ? true : false,
|
|
763
|
+
server_streaming: server_streaming ? true : false)
|
|
764
|
+
@file_draft.new_method(method, options)
|
|
765
|
+
@rpcs << method
|
|
766
|
+
method
|
|
767
|
+
end
|
|
768
|
+
|
|
769
|
+
# @api private
|
|
770
|
+
def __seal
|
|
771
|
+
@descriptor.__rpcs(@rpcs.empty? ? Freezer::EMPTY_ARRAY : @rpcs.dup.freeze)
|
|
772
|
+
end
|
|
773
|
+
end
|
|
774
|
+
end
|
|
775
|
+
end
|
|
776
|
+
end
|
|
777
|
+
|
|
778
|
+
require_relative "draft/copy"
|
|
779
|
+
require_relative "draft/resolution"
|