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,572 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Protostar
|
|
4
|
+
# The base class of every message class. Subclasses are made by
|
|
5
|
+
# {Types.define} from a {Schema::Message}; they get one reader and writer
|
|
6
|
+
# per field, `has_` and `clear_` for fields with presence, and `which_` for
|
|
7
|
+
# each oneof.
|
|
8
|
+
#
|
|
9
|
+
# person = Person.new(name: "Ada", ids: [1, 2])
|
|
10
|
+
# bytes = person.encode
|
|
11
|
+
# Person.decode(bytes) == person # => true
|
|
12
|
+
#
|
|
13
|
+
# Values are kept in one Array per message, a slot per field. Singular
|
|
14
|
+
# fields with presence hold nil when unset; fields without presence hold
|
|
15
|
+
# their value, the default included. Repeated fields hold an Array and maps
|
|
16
|
+
# a Hash, both allocated on first access.
|
|
17
|
+
class Message
|
|
18
|
+
EMPTY_ARRAY = [].freeze
|
|
19
|
+
EMPTY_HASH = {}.freeze
|
|
20
|
+
|
|
21
|
+
# Needed while a class is defined, never after
|
|
22
|
+
autoload :Accessors, ::File.expand_path("message/accessors", __dir__)
|
|
23
|
+
autoload :Reflection, ::File.expand_path("message/reflection", __dir__)
|
|
24
|
+
|
|
25
|
+
class << self
|
|
26
|
+
# @return [Schema::Message]
|
|
27
|
+
attr_reader :descriptor
|
|
28
|
+
|
|
29
|
+
# @return [Reflection] the class's fields by descriptor, name or number
|
|
30
|
+
attr_reader :reflection
|
|
31
|
+
|
|
32
|
+
# @api private
|
|
33
|
+
attr_reader :__fields, :__by_number, :__by_name, :__template, :__oneofs, :__required
|
|
34
|
+
|
|
35
|
+
# @param bytes [String, IO::Buffer] never mutated
|
|
36
|
+
# @param recursion_limit [Integer] how deep messages may nest
|
|
37
|
+
# @param frozen [Boolean] return a deep-frozen, Ractor-shareable message
|
|
38
|
+
# @return [Message]
|
|
39
|
+
# @raise [DecodeError]
|
|
40
|
+
def decode(bytes, recursion_limit: Codec::Decoder::RECURSION_LIMIT, frozen: false)
|
|
41
|
+
Codec::Decoder.decode(self, bytes, recursion_limit: recursion_limit, frozen: frozen)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# Reads one message prefixed by its varint length
|
|
45
|
+
# @param source [String, IO::Buffer, #getbyte and #read] a String is read
|
|
46
|
+
# from its start; an IO from its current position, leaving it after the message
|
|
47
|
+
# @return [Message, nil] nil when an IO is at its end
|
|
48
|
+
# @raise [DecodeError]
|
|
49
|
+
def decode_delimited(source, **options)
|
|
50
|
+
Codec::Decoder.decode_delimited(self, source, **options)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# @param message [Message] an instance of this class
|
|
54
|
+
# @return [String] binary
|
|
55
|
+
def encode(message)
|
|
56
|
+
unless message.instance_of?(self)
|
|
57
|
+
raise Protostar::TypeError, "#{descriptor.full_name}.encode expects a #{descriptor.full_name}, got #{message.class}"
|
|
58
|
+
end
|
|
59
|
+
message.encode
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# @param json [String]
|
|
63
|
+
# @param options [Hash] see {JSON.decode}
|
|
64
|
+
# @return [Message]
|
|
65
|
+
# @raise [JSON::ParseError]
|
|
66
|
+
def decode_json(json, **options)
|
|
67
|
+
JSON.decode(self, json, **options)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# @param message [Message] an instance of this class
|
|
71
|
+
# @param options [Hash] see {JSON.encode}
|
|
72
|
+
# @return [String]
|
|
73
|
+
def encode_json(message, **options)
|
|
74
|
+
unless message.instance_of?(self)
|
|
75
|
+
raise Protostar::TypeError, "#{descriptor.full_name}.encode_json expects a #{descriptor.full_name}, got #{message.class}"
|
|
76
|
+
end
|
|
77
|
+
JSON.encode(message, **options)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# @api private
|
|
81
|
+
attr_reader :__view_class
|
|
82
|
+
|
|
83
|
+
# @return [Class] the {View} subclass whose instances read this class's messages
|
|
84
|
+
def view_class
|
|
85
|
+
@__view_class || raise(NotImplementedError, "#{self} is abstract; define message classes with Protostar::Types")
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# A read-only view of an encoded message that decodes fields only when read
|
|
89
|
+
# @param bytes [String, IO::Buffer] not copied; must not change while the view is used
|
|
90
|
+
# @param offset [Integer] where the message starts in `bytes`
|
|
91
|
+
# @param length [Integer, nil] its length, by default up to the end of `bytes`
|
|
92
|
+
# @param recursion_limit [Integer] how deep messages may nest
|
|
93
|
+
# @return [View]
|
|
94
|
+
# @raise [DecodeError] when the top level of the message is malformed
|
|
95
|
+
def view(bytes, offset: 0, length: nil, recursion_limit: Codec::Decoder::RECURSION_LIMIT)
|
|
96
|
+
view_class.__open(bytes, offset, length, recursion_limit)
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# A message made with a {Builder} block, or with `new` when there is no
|
|
100
|
+
# block. `fields` are written first; the block may set them again (the
|
|
101
|
+
# bytes never leave the process, so overwrites are allowed).
|
|
102
|
+
#
|
|
103
|
+
# person = Acme::Person.build(name: "Jane") do |pb|
|
|
104
|
+
# pb.address { pb.street "x" }
|
|
105
|
+
# end
|
|
106
|
+
#
|
|
107
|
+
# @param fields [Hash, nil] field names to values, as {#initialize} takes them
|
|
108
|
+
# @yieldparam pb [Builder]
|
|
109
|
+
# @return [Message]
|
|
110
|
+
def build(fields = nil, **more, &block)
|
|
111
|
+
return new(fields, **more) unless block
|
|
112
|
+
Builder.message(self, overwrites: true) do |pb|
|
|
113
|
+
pb.merge!(fields) if fields
|
|
114
|
+
pb.merge!(more) unless more.empty?
|
|
115
|
+
block.call(pb)
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
# @return [Message] a new message with `b` merged into a copy of `a`
|
|
120
|
+
def merge(a, b)
|
|
121
|
+
a.dup.merge!(b)
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
# What a field of this type makes of a value that is neither an
|
|
125
|
+
# instance nor a Hash. The well-known types' mixins override it, so that
|
|
126
|
+
# a Time can be assigned to a Timestamp field.
|
|
127
|
+
# @return [Message, nil] nil when the value cannot become one
|
|
128
|
+
# @api private
|
|
129
|
+
def __coerce(_value)
|
|
130
|
+
nil
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
# @return [Message] a message with every field unset, bypassing argument parsing
|
|
134
|
+
# @api private
|
|
135
|
+
def __empty
|
|
136
|
+
m = allocate
|
|
137
|
+
m.__reset
|
|
138
|
+
m
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
# @api private
|
|
142
|
+
def __define(descriptor:, fields:, oneofs:)
|
|
143
|
+
@descriptor = descriptor
|
|
144
|
+
@__fields = fields
|
|
145
|
+
@__by_number = fields.to_h { |f| [f.number, f] }
|
|
146
|
+
@__by_name = fields.each_with_object({}) { |f, h| h[f.name] = h[f.symbol] = f }
|
|
147
|
+
@__template = fields.map { |f| (f.presence || f.repeated || f.map) ? nil : f.default }
|
|
148
|
+
@__oneofs = oneofs
|
|
149
|
+
@__required = fields.select(&:required)
|
|
150
|
+
[@__fields, @__by_number, @__by_name, @__template, @__oneofs, @__required].each do |o|
|
|
151
|
+
Ractor.make_shareable(o)
|
|
152
|
+
end
|
|
153
|
+
@reflection = Ractor.make_shareable(Reflection.new(self, descriptor))
|
|
154
|
+
self
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
def inspect
|
|
158
|
+
name || "#<Class #{descriptor ? descriptor.full_name : "Protostar::Message"}>"
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
# @param hash [Hash, nil] field names (Symbols or Strings) to values
|
|
163
|
+
# @param fields [Hash] the same, as keywords
|
|
164
|
+
# @raise [ArgumentError] for a name that is not a field
|
|
165
|
+
# @raise [Protostar::TypeError] for a value the field cannot hold
|
|
166
|
+
def initialize(hash = nil, **fields)
|
|
167
|
+
klass = self.class
|
|
168
|
+
raise NotImplementedError, "#{klass} is abstract; define message classes with Protostar::Types" unless klass.__fields
|
|
169
|
+
@values = klass.__template.dup
|
|
170
|
+
@unknown = nil
|
|
171
|
+
__assign(hash) if hash
|
|
172
|
+
__assign(fields) unless fields.empty?
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
# @param name [String, Symbol]
|
|
176
|
+
# @return [Object] what the field's reader returns
|
|
177
|
+
# @raise [ArgumentError] when there is no such field
|
|
178
|
+
def [](name)
|
|
179
|
+
__get(__info(name))
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
# @param name [String, Symbol]
|
|
183
|
+
# @raise [ArgumentError] when there is no such field
|
|
184
|
+
def []=(name, value)
|
|
185
|
+
__set(__info(name).index, value)
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
# @return [Array<UnknownField>] frozen, in the order they were read
|
|
189
|
+
def unknown_fields
|
|
190
|
+
@unknown || EMPTY_ARRAY
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
# @return [String] binary
|
|
194
|
+
def encode
|
|
195
|
+
Codec::Encoder.encode(self)
|
|
196
|
+
end
|
|
197
|
+
alias_method :to_proto, :encode
|
|
198
|
+
|
|
199
|
+
# Writes the message to an IO without building the whole encoding in memory
|
|
200
|
+
# @return [IO] the IO given
|
|
201
|
+
def encode_to(io)
|
|
202
|
+
Codec::Encoder.encode_to(self, io)
|
|
203
|
+
io
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
# @param state [::JSON::State, nil] what the stdlib generator passes; its
|
|
207
|
+
# indentation is honoured, so `JSON.pretty_generate(message)` is indented
|
|
208
|
+
# @param options [Hash] see {JSON.encode}
|
|
209
|
+
# @return [String] the message as proto3 JSON
|
|
210
|
+
def to_json(state = nil, **options)
|
|
211
|
+
json = JSON.encode(self, **options)
|
|
212
|
+
state.respond_to?(:object_nl) ? JSON.layout(json, state) : json
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
# @param options [Hash] see {Text.print}
|
|
216
|
+
# @return [String] the message in protoc's text format
|
|
217
|
+
def to_text(**options)
|
|
218
|
+
Text.print(self, **options)
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
# The message prefixed by its varint length
|
|
222
|
+
# @param io [IO, nil] where to write it
|
|
223
|
+
# @return [String, IO] the bytes when no IO is given, otherwise the IO
|
|
224
|
+
def encode_delimited(io = nil)
|
|
225
|
+
Codec::Encoder.encode_delimited(self, io)
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
# @return [Hash] Symbol keys, every field that is set or not at its
|
|
229
|
+
# default; nested messages as Hashes, enums as Symbols
|
|
230
|
+
def to_h
|
|
231
|
+
__to_h
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
# {#to_h} as Message defines it; well-known types replace to_h (a Struct
|
|
235
|
+
# gives a plain Hash), but nested messages always convert field by field
|
|
236
|
+
# @api private
|
|
237
|
+
def __to_h
|
|
238
|
+
h = {}
|
|
239
|
+
values = @values
|
|
240
|
+
self.class.__fields.each do |info|
|
|
241
|
+
v = values[info.index]
|
|
242
|
+
next if v.nil?
|
|
243
|
+
if info.map
|
|
244
|
+
next if v.empty?
|
|
245
|
+
h[info.symbol] = info.value.message? ? v.transform_values(&:__to_h) : v.dup
|
|
246
|
+
elsif info.repeated
|
|
247
|
+
next if v.empty?
|
|
248
|
+
h[info.symbol] = info.message? ? v.map(&:__to_h) : v.dup
|
|
249
|
+
elsif info.message?
|
|
250
|
+
h[info.symbol] = v.__to_h
|
|
251
|
+
elsif info.presence || !info.zero?(v)
|
|
252
|
+
h[info.symbol] = v
|
|
253
|
+
end
|
|
254
|
+
end
|
|
255
|
+
h
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
# Same class and same field values. Unknown fields are ignored, and NaN
|
|
259
|
+
# equals NaN in float and double fields, as protoc's MessageDifferencer has it.
|
|
260
|
+
def ==(other)
|
|
261
|
+
return other == self if other.is_a?(View)
|
|
262
|
+
return true if equal?(other)
|
|
263
|
+
return false unless other.instance_of?(self.class)
|
|
264
|
+
mine = @values
|
|
265
|
+
theirs = other.__values
|
|
266
|
+
self.class.__fields.each do |info|
|
|
267
|
+
i = info.index
|
|
268
|
+
return false unless Message.__equal_values?(info, mine[i], theirs[i])
|
|
269
|
+
end
|
|
270
|
+
true
|
|
271
|
+
end
|
|
272
|
+
alias_method :eql?, :==
|
|
273
|
+
|
|
274
|
+
def hash
|
|
275
|
+
h = [self.class]
|
|
276
|
+
self.class.__fields.each { |info| h << Message.__hash_value(info, @values[info.index]) }
|
|
277
|
+
h.hash
|
|
278
|
+
end
|
|
279
|
+
|
|
280
|
+
# Deep copy; the copy is not frozen
|
|
281
|
+
def initialize_copy(source)
|
|
282
|
+
super
|
|
283
|
+
@values = @values.map { |v| Message.__copy_value(v) }
|
|
284
|
+
@unknown = @unknown&.dup&.freeze
|
|
285
|
+
end
|
|
286
|
+
|
|
287
|
+
# Deep copy, frozen when the original is (or when asked)
|
|
288
|
+
def clone(freeze: nil)
|
|
289
|
+
copy = dup
|
|
290
|
+
copy.freeze if freeze || (freeze.nil? && frozen?)
|
|
291
|
+
copy
|
|
292
|
+
end
|
|
293
|
+
|
|
294
|
+
# Freezes the message and everything it holds, after which it is Ractor.shareable?
|
|
295
|
+
# @return [self]
|
|
296
|
+
def freeze
|
|
297
|
+
return self if frozen?
|
|
298
|
+
@values.each do |v|
|
|
299
|
+
case v
|
|
300
|
+
when Message then v.freeze
|
|
301
|
+
when Array then v.each(&:freeze).freeze
|
|
302
|
+
when Hash then v.each_value(&:freeze).freeze
|
|
303
|
+
end
|
|
304
|
+
end
|
|
305
|
+
@values.freeze
|
|
306
|
+
@unknown&.freeze
|
|
307
|
+
super
|
|
308
|
+
end
|
|
309
|
+
|
|
310
|
+
# Merges `other` into this message: singular fields set in `other`
|
|
311
|
+
# replace ours, messages merge recursively, repeated fields append, maps
|
|
312
|
+
# overwrite by key, unknown fields append.
|
|
313
|
+
# @return [self]
|
|
314
|
+
def merge!(other)
|
|
315
|
+
unless other.instance_of?(self.class)
|
|
316
|
+
raise Protostar::TypeError, "cannot merge a #{other.class} into a #{self.class.descriptor.full_name}"
|
|
317
|
+
end
|
|
318
|
+
__check_frozen
|
|
319
|
+
theirs = other.__values
|
|
320
|
+
self.class.__fields.each do |info|
|
|
321
|
+
i = info.index
|
|
322
|
+
v = theirs[i]
|
|
323
|
+
next if v.nil?
|
|
324
|
+
if info.map
|
|
325
|
+
next if v.empty?
|
|
326
|
+
(@values[i] ||= {}).merge!(info.value.message? ? v.transform_values(&:dup) : v)
|
|
327
|
+
elsif info.repeated
|
|
328
|
+
next if v.empty?
|
|
329
|
+
(@values[i] ||= []).concat(info.message? ? v.map(&:dup) : v)
|
|
330
|
+
elsif info.message?
|
|
331
|
+
mine = @values[i]
|
|
332
|
+
if mine.nil?
|
|
333
|
+
__clear_siblings(info)
|
|
334
|
+
@values[i] = v.dup
|
|
335
|
+
else
|
|
336
|
+
mine = @values[i] = mine.dup if mine.frozen?
|
|
337
|
+
mine.merge!(v)
|
|
338
|
+
end
|
|
339
|
+
elsif info.presence || !info.zero?(v)
|
|
340
|
+
__clear_siblings(info)
|
|
341
|
+
@values[i] = v
|
|
342
|
+
end
|
|
343
|
+
end
|
|
344
|
+
@unknown = ((@unknown || EMPTY_ARRAY) + other.unknown_fields).freeze unless other.unknown_fields.empty?
|
|
345
|
+
self
|
|
346
|
+
end
|
|
347
|
+
|
|
348
|
+
# Unsets every field and drops unknown fields
|
|
349
|
+
# @return [self]
|
|
350
|
+
def clear!
|
|
351
|
+
__check_frozen
|
|
352
|
+
__reset
|
|
353
|
+
self
|
|
354
|
+
end
|
|
355
|
+
|
|
356
|
+
# @return [Boolean] whether every required field is set, here and in every nested message
|
|
357
|
+
def initialized?
|
|
358
|
+
self.class.__required.each { |info| return false if @values[info.index].nil? }
|
|
359
|
+
self.class.__fields.each do |info|
|
|
360
|
+
v = @values[info.index]
|
|
361
|
+
next if v.nil?
|
|
362
|
+
if info.map
|
|
363
|
+
next unless info.value.message?
|
|
364
|
+
v.each_value { |m| return false unless m.initialized? }
|
|
365
|
+
elsif info.message?
|
|
366
|
+
if info.repeated
|
|
367
|
+
v.each { |m| return false unless m.initialized? }
|
|
368
|
+
else
|
|
369
|
+
return false unless v.initialized?
|
|
370
|
+
end
|
|
371
|
+
end
|
|
372
|
+
end
|
|
373
|
+
true
|
|
374
|
+
end
|
|
375
|
+
|
|
376
|
+
def inspect
|
|
377
|
+
parts = __shown.map { |name, v| "#{name}: #{v.inspect}" }
|
|
378
|
+
name = self.class.descriptor.full_name
|
|
379
|
+
parts.empty? ? "#<#{name}>" : "#<#{name} #{parts.join(", ")}>"
|
|
380
|
+
end
|
|
381
|
+
alias_method :to_s, :inspect
|
|
382
|
+
|
|
383
|
+
# For `pp`: the fields `inspect` shows, one per line with nested
|
|
384
|
+
# messages indented when they do not fit on one
|
|
385
|
+
def pretty_print(q)
|
|
386
|
+
Message.__pretty_print(q, self.class.descriptor.full_name, __shown)
|
|
387
|
+
end
|
|
388
|
+
|
|
389
|
+
# @api private
|
|
390
|
+
def self.__pretty_print(q, name, fields)
|
|
391
|
+
return q.text("#<#{name}>") if fields.empty?
|
|
392
|
+
q.group(2, "#<#{name}", ">") do
|
|
393
|
+
fields.each_with_index do |(field, v), i|
|
|
394
|
+
q.text(",") if i > 0
|
|
395
|
+
q.breakable
|
|
396
|
+
q.text("#{field}: ")
|
|
397
|
+
q.pp(v)
|
|
398
|
+
end
|
|
399
|
+
end
|
|
400
|
+
end
|
|
401
|
+
|
|
402
|
+
# The fields `inspect` shows: set, and not at their zero value
|
|
403
|
+
# @return [Array<Array(String, Object)>]
|
|
404
|
+
# @api private
|
|
405
|
+
def __shown
|
|
406
|
+
shown = []
|
|
407
|
+
self.class.__fields.each do |info|
|
|
408
|
+
v = @values[info.index]
|
|
409
|
+
next if v.nil?
|
|
410
|
+
next if (info.repeated || info.map) && v.empty?
|
|
411
|
+
next if !info.presence && !info.repeated && !info.map && info.zero?(v)
|
|
412
|
+
shown << [info.name, v]
|
|
413
|
+
end
|
|
414
|
+
shown
|
|
415
|
+
end
|
|
416
|
+
|
|
417
|
+
# @api private
|
|
418
|
+
def __values
|
|
419
|
+
@values
|
|
420
|
+
end
|
|
421
|
+
|
|
422
|
+
# @api private
|
|
423
|
+
def __reset
|
|
424
|
+
@values = self.class.__template.dup
|
|
425
|
+
@unknown = nil
|
|
426
|
+
end
|
|
427
|
+
|
|
428
|
+
# Appends an unknown field; the decoder's way in
|
|
429
|
+
# @api private
|
|
430
|
+
def __add_unknown(field)
|
|
431
|
+
u = @unknown
|
|
432
|
+
if u.nil?
|
|
433
|
+
@unknown = [field]
|
|
434
|
+
elsif u.frozen?
|
|
435
|
+
@unknown = [*u, field]
|
|
436
|
+
else
|
|
437
|
+
u << field
|
|
438
|
+
end
|
|
439
|
+
end
|
|
440
|
+
|
|
441
|
+
# @api private
|
|
442
|
+
def __seal_unknown
|
|
443
|
+
@unknown&.freeze
|
|
444
|
+
end
|
|
445
|
+
|
|
446
|
+
# @api private
|
|
447
|
+
def __set(index, value)
|
|
448
|
+
__check_frozen
|
|
449
|
+
info = self.class.__fields[index]
|
|
450
|
+
if value.nil?
|
|
451
|
+
@values[index] = (info.presence || info.repeated || info.map) ? nil : info.default
|
|
452
|
+
else
|
|
453
|
+
value = Coercion.field(info, value)
|
|
454
|
+
__clear_siblings(info)
|
|
455
|
+
@values[index] = value
|
|
456
|
+
end
|
|
457
|
+
end
|
|
458
|
+
|
|
459
|
+
# @api private
|
|
460
|
+
def __clear(index)
|
|
461
|
+
__check_frozen
|
|
462
|
+
@values[index] = nil
|
|
463
|
+
end
|
|
464
|
+
|
|
465
|
+
# The stored Array or Hash of a repeated or map field, made on first use
|
|
466
|
+
# @api private
|
|
467
|
+
def __container(index)
|
|
468
|
+
info = self.class.__fields[index]
|
|
469
|
+
if frozen?
|
|
470
|
+
info.map ? EMPTY_HASH : EMPTY_ARRAY
|
|
471
|
+
else
|
|
472
|
+
@values[index] = info.map ? {} : []
|
|
473
|
+
end
|
|
474
|
+
end
|
|
475
|
+
|
|
476
|
+
# @api private
|
|
477
|
+
def __get(info)
|
|
478
|
+
v = @values[info.index]
|
|
479
|
+
if info.repeated || info.map
|
|
480
|
+
v || __container(info.index)
|
|
481
|
+
elsif v.nil?
|
|
482
|
+
info.default
|
|
483
|
+
else
|
|
484
|
+
v
|
|
485
|
+
end
|
|
486
|
+
end
|
|
487
|
+
|
|
488
|
+
# @api private
|
|
489
|
+
def self.__equal_values?(info, a, b)
|
|
490
|
+
if info.map
|
|
491
|
+
a ||= EMPTY_HASH
|
|
492
|
+
b ||= EMPTY_HASH
|
|
493
|
+
return a == b unless float_kind?(info.value.kind)
|
|
494
|
+
a.size == b.size && a.all? { |k, v| b.key?(k) && float_equal?(v, b[k]) }
|
|
495
|
+
elsif info.repeated
|
|
496
|
+
a ||= EMPTY_ARRAY
|
|
497
|
+
b ||= EMPTY_ARRAY
|
|
498
|
+
return a == b unless float_kind?(info.kind)
|
|
499
|
+
a.size == b.size && a.each_index.all? { |i| float_equal?(a[i], b[i]) }
|
|
500
|
+
elsif float_kind?(info.kind)
|
|
501
|
+
float_equal?(a, b)
|
|
502
|
+
else
|
|
503
|
+
a == b
|
|
504
|
+
end
|
|
505
|
+
end
|
|
506
|
+
|
|
507
|
+
# @api private
|
|
508
|
+
def self.__hash_value(info, v)
|
|
509
|
+
if info.map
|
|
510
|
+
return nil if v.nil? || v.empty?
|
|
511
|
+
float_kind?(info.value.kind) ? v.transform_values { |f| hashable_float(f) } : v
|
|
512
|
+
elsif info.repeated
|
|
513
|
+
return nil if v.nil? || v.empty?
|
|
514
|
+
float_kind?(info.kind) ? v.map { |f| hashable_float(f) } : v
|
|
515
|
+
elsif float_kind?(info.kind)
|
|
516
|
+
hashable_float(v)
|
|
517
|
+
else
|
|
518
|
+
v
|
|
519
|
+
end
|
|
520
|
+
end
|
|
521
|
+
|
|
522
|
+
# @api private
|
|
523
|
+
def self.__copy_value(v)
|
|
524
|
+
case v
|
|
525
|
+
when Message then v.dup
|
|
526
|
+
when Array then v.map { |e| e.is_a?(Message) ? e.dup : e }
|
|
527
|
+
when Hash then v.transform_values { |e| e.is_a?(Message) ? e.dup : e }
|
|
528
|
+
else v
|
|
529
|
+
end
|
|
530
|
+
end
|
|
531
|
+
|
|
532
|
+
def self.float_kind?(kind)
|
|
533
|
+
kind == FieldInfo::DOUBLE || kind == FieldInfo::FLOAT
|
|
534
|
+
end
|
|
535
|
+
|
|
536
|
+
def self.float_equal?(a, b)
|
|
537
|
+
a == b || (a.is_a?(Float) && b.is_a?(Float) && a.nan? && b.nan?)
|
|
538
|
+
end
|
|
539
|
+
|
|
540
|
+
def self.hashable_float(f)
|
|
541
|
+
(f.is_a?(Float) && f.nan?) ? :nan : f
|
|
542
|
+
end
|
|
543
|
+
private_class_method :float_kind?, :float_equal?, :hashable_float
|
|
544
|
+
|
|
545
|
+
private
|
|
546
|
+
|
|
547
|
+
def __info(name)
|
|
548
|
+
self.class.__by_name[name] || raise(ArgumentError, "#{self.class.descriptor.full_name} has no field #{name.inspect}")
|
|
549
|
+
end
|
|
550
|
+
|
|
551
|
+
def __assign(hash)
|
|
552
|
+
by_name = self.class.__by_name
|
|
553
|
+
hash.each do |key, value|
|
|
554
|
+
info = by_name[key]
|
|
555
|
+
raise ArgumentError, "#{self.class.descriptor.full_name} has no field #{key.inspect}" unless info
|
|
556
|
+
__set(info.index, value)
|
|
557
|
+
end
|
|
558
|
+
end
|
|
559
|
+
|
|
560
|
+
def __clear_siblings(info)
|
|
561
|
+
siblings = info.siblings
|
|
562
|
+
siblings&.each { |s| @values[s] = nil }
|
|
563
|
+
end
|
|
564
|
+
|
|
565
|
+
def __check_frozen
|
|
566
|
+
raise FrozenError.new("can't modify frozen #{self.class.descriptor.full_name}", receiver: self) if frozen?
|
|
567
|
+
end
|
|
568
|
+
end
|
|
569
|
+
end
|
|
570
|
+
|
|
571
|
+
require_relative "message/field_info"
|
|
572
|
+
require_relative "message/coercion"
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "protostar"
|
|
4
|
+
require "protostar/google/protobuf/compiler/plugin_pb"
|
|
5
|
+
|
|
6
|
+
module Protostar
|
|
7
|
+
# `protoc-gen-protostar`: a protoc plugin writing the same files as
|
|
8
|
+
# `protostar compile`. The parameter selects the outputs, comma-separated:
|
|
9
|
+
# `ruby` (protostar's native Ruby, the default), `google` (protoc's
|
|
10
|
+
# google-protobuf Ruby), `rbs` and `grpc`.
|
|
11
|
+
#
|
|
12
|
+
# protoc --plugin=protoc-gen-protostar --protostar_out=ruby,grpc:lib acme/service.proto
|
|
13
|
+
module Plugin
|
|
14
|
+
Compiler = Protostar::Compiler
|
|
15
|
+
Emitters = Compiler::Emitters
|
|
16
|
+
Request = Protostar::Google::Protobuf::Compiler::CodeGeneratorRequest
|
|
17
|
+
Response = Protostar::Google::Protobuf::Compiler::CodeGeneratorResponse
|
|
18
|
+
Feature = Response::Feature
|
|
19
|
+
OUTPUTS = %w[ruby google rbs grpc].freeze
|
|
20
|
+
|
|
21
|
+
module_function
|
|
22
|
+
|
|
23
|
+
# Reads a CodeGeneratorRequest from `input` and writes the response to `output`
|
|
24
|
+
def run(input = $stdin, output = $stdout)
|
|
25
|
+
input.binmode
|
|
26
|
+
output.binmode
|
|
27
|
+
output.write(respond(input.read))
|
|
28
|
+
output.flush
|
|
29
|
+
0
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# @param bytes [String] a serialized CodeGeneratorRequest
|
|
33
|
+
# @return [String] a serialized CodeGeneratorResponse
|
|
34
|
+
def respond(bytes)
|
|
35
|
+
response = Response.new(
|
|
36
|
+
supported_features: Feature::FEATURE_PROTO3_OPTIONAL | Feature::FEATURE_SUPPORTS_EDITIONS,
|
|
37
|
+
minimum_edition: Schema::Features::EDITIONS[:EDITION_PROTO2],
|
|
38
|
+
maximum_edition: Schema::Features::EDITIONS[:EDITION_2024]
|
|
39
|
+
)
|
|
40
|
+
begin
|
|
41
|
+
generate(Request.decode(bytes)).each do |name, content|
|
|
42
|
+
response.file << Response::File.new(name: name, content: content)
|
|
43
|
+
end
|
|
44
|
+
rescue Protostar::Error => e
|
|
45
|
+
response.error = e.message
|
|
46
|
+
response.file.clear
|
|
47
|
+
end
|
|
48
|
+
response.encode
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# @param request [Request]
|
|
52
|
+
# @return [Hash{String => String}] file name to content
|
|
53
|
+
def generate(request)
|
|
54
|
+
outputs = request.parameter.split(",").map(&:strip).reject(&:empty?)
|
|
55
|
+
outputs = ["ruby"] if outputs.empty?
|
|
56
|
+
unknown = outputs - OUTPUTS
|
|
57
|
+
raise Protostar::Error, "unknown output #{unknown.first.inspect}; use #{OUTPUTS.join(", ")}" unless unknown.empty?
|
|
58
|
+
if outputs.include?("ruby") && outputs.include?("google")
|
|
59
|
+
raise Protostar::Error, "ruby and google both write *_pb.rb; ask for one of them"
|
|
60
|
+
end
|
|
61
|
+
pool = Schema::Pool.new
|
|
62
|
+
# protoc has already warned about extension numbers used twice
|
|
63
|
+
request.proto_file.each { |proto| pool.add(Schema::File.from_proto(proto, pool: pool), warn: false) }
|
|
64
|
+
files = request.file_to_generate.map do |name|
|
|
65
|
+
pool.file(name) || raise(Protostar::Error, "#{name} is not among the request's proto files")
|
|
66
|
+
end
|
|
67
|
+
generated = {}
|
|
68
|
+
outputs.each do |output|
|
|
69
|
+
files.each do |file|
|
|
70
|
+
if output == "rbs" && file.syntax == :editions && !outputs.include?("ruby")
|
|
71
|
+
raise Protostar::Error, "#{file.name}: #{CLI::RBS_EDITIONS}"
|
|
72
|
+
end
|
|
73
|
+
begin
|
|
74
|
+
generated.merge!(emitter(output, pool, outputs).emit([file]))
|
|
75
|
+
rescue Emitters::Error => e
|
|
76
|
+
raise Protostar::Error, "#{file.name}: #{e.message}"
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
generated
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def emitter(output, pool, outputs)
|
|
84
|
+
case output
|
|
85
|
+
when "ruby" then Emitters::Ruby.new
|
|
86
|
+
when "google" then Emitters::GoogleRuby.new(pool: pool)
|
|
87
|
+
when "rbs" then outputs.include?("ruby") ? Emitters::ProtostarRbs.new : Emitters::Rbs.new
|
|
88
|
+
when "grpc" then Emitters::Grpc.new
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
require "protostar/cli"
|