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,145 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Protostar
|
|
4
|
+
class Message
|
|
5
|
+
# Everything the runtime and the codec need about one field of a message
|
|
6
|
+
# class, resolved once when the class is defined: the slot it is stored
|
|
7
|
+
# in, its kind as a small Integer to dispatch on, precomputed tag bytes,
|
|
8
|
+
# the classes and enum tables it refers to. Frozen and shareable.
|
|
9
|
+
# @api private
|
|
10
|
+
class FieldInfo
|
|
11
|
+
# Kinds are FieldDescriptorProto.Type numbers
|
|
12
|
+
DOUBLE = 1
|
|
13
|
+
FLOAT = 2
|
|
14
|
+
INT64 = 3
|
|
15
|
+
UINT64 = 4
|
|
16
|
+
INT32 = 5
|
|
17
|
+
FIXED64 = 6
|
|
18
|
+
FIXED32 = 7
|
|
19
|
+
BOOL = 8
|
|
20
|
+
STRING = 9
|
|
21
|
+
GROUP = 10
|
|
22
|
+
MESSAGE = 11
|
|
23
|
+
BYTES = 12
|
|
24
|
+
UINT32 = 13
|
|
25
|
+
ENUM = 14
|
|
26
|
+
SFIXED32 = 15
|
|
27
|
+
SFIXED64 = 16
|
|
28
|
+
SINT32 = 17
|
|
29
|
+
SINT64 = 18
|
|
30
|
+
|
|
31
|
+
KINDS = {
|
|
32
|
+
double: DOUBLE, float: FLOAT, int64: INT64, uint64: UINT64, int32: INT32, fixed64: FIXED64,
|
|
33
|
+
fixed32: FIXED32, bool: BOOL, string: STRING, group: GROUP, message: MESSAGE, bytes: BYTES,
|
|
34
|
+
uint32: UINT32, enum: ENUM, sfixed32: SFIXED32, sfixed64: SFIXED64, sint32: SINT32, sint64: SINT64
|
|
35
|
+
}.freeze
|
|
36
|
+
|
|
37
|
+
# Byte width and unpack directive of the fixed-width kinds
|
|
38
|
+
FIXED = {
|
|
39
|
+
DOUBLE => [8, "E"], FLOAT => [4, "e"], FIXED64 => [8, "Q<"], FIXED32 => [4, "V"],
|
|
40
|
+
SFIXED32 => [4, "l<"], SFIXED64 => [8, "q<"]
|
|
41
|
+
}.transform_values(&:freeze).freeze
|
|
42
|
+
|
|
43
|
+
# @return [Schema::Field]
|
|
44
|
+
attr_reader :descriptor
|
|
45
|
+
# @return [String]
|
|
46
|
+
attr_reader :name
|
|
47
|
+
# @return [Symbol]
|
|
48
|
+
attr_reader :symbol
|
|
49
|
+
# @return [Integer]
|
|
50
|
+
attr_reader :number
|
|
51
|
+
# @return [Integer, nil] the slot in the message's value array; nil for map keys and values
|
|
52
|
+
attr_reader :index
|
|
53
|
+
# @return [Integer] one of the kind constants
|
|
54
|
+
attr_reader :kind
|
|
55
|
+
# @return [Integer] wire type of one unpacked value
|
|
56
|
+
attr_reader :wire_type
|
|
57
|
+
# @return [Boolean]
|
|
58
|
+
attr_reader :repeated, :packed, :packable, :presence, :map, :closed_enum, :utf8_validation, :required
|
|
59
|
+
# @return [Array<Integer>, nil] slots of the other members of the field's real oneof
|
|
60
|
+
attr_reader :siblings
|
|
61
|
+
# @return [Class, nil] for message and group fields
|
|
62
|
+
attr_reader :message_class
|
|
63
|
+
# @return [Module, nil] for enum fields
|
|
64
|
+
attr_reader :enum_module
|
|
65
|
+
# @return [Hash{Integer => Symbol}, nil] enum number to its first name
|
|
66
|
+
attr_reader :enum_names
|
|
67
|
+
# @return [Hash{Symbol => Integer}, nil] every enum name, aliases included, to its number
|
|
68
|
+
attr_reader :enum_numbers
|
|
69
|
+
# @return [Object] the value an unset field reads as
|
|
70
|
+
attr_reader :default
|
|
71
|
+
# @return [String] the tag of one unpacked value, varint-encoded
|
|
72
|
+
attr_reader :tag
|
|
73
|
+
# @return [String, nil] the length-delimited tag, for packable fields
|
|
74
|
+
attr_reader :packed_tag
|
|
75
|
+
# @return [String, nil] the end-group tag, for groups
|
|
76
|
+
attr_reader :end_tag
|
|
77
|
+
# @return [FieldInfo, nil] for map fields
|
|
78
|
+
attr_reader :key, :value
|
|
79
|
+
|
|
80
|
+
# @param field [Schema::Field]
|
|
81
|
+
# @param index [Integer, nil]
|
|
82
|
+
# @param classes [#call] resolves a Schema::Message or Schema::Enum to its Class or Module
|
|
83
|
+
# @param open_enums [Boolean] treat a closed enum as open
|
|
84
|
+
def initialize(field, index, classes, siblings: nil, open_enums: false)
|
|
85
|
+
@descriptor = field
|
|
86
|
+
@name = field.name
|
|
87
|
+
@symbol = field.name.to_sym
|
|
88
|
+
@number = field.number
|
|
89
|
+
@index = index
|
|
90
|
+
@kind = KINDS.fetch(field.type)
|
|
91
|
+
@wire_type = field.wire_type
|
|
92
|
+
@map = field.map?
|
|
93
|
+
@repeated = field.repeated? && !@map
|
|
94
|
+
@packed = field.packed?
|
|
95
|
+
@packable = field.packable?
|
|
96
|
+
@presence = field.presence?
|
|
97
|
+
@required = field.required?
|
|
98
|
+
@utf8_validation = field.utf8_validation?
|
|
99
|
+
@siblings = siblings
|
|
100
|
+
@tag = Wire.tag_bytes(@number, @wire_type)
|
|
101
|
+
@packed_tag = Wire.tag_bytes(@number, Wire::LENGTH_DELIMITED) if @packable
|
|
102
|
+
@end_tag = Wire.tag_bytes(@number, Wire::END_GROUP) if @kind == GROUP
|
|
103
|
+
if @map
|
|
104
|
+
@key = FieldInfo.new(field.map_key, nil, classes)
|
|
105
|
+
@value = FieldInfo.new(field.map_value, nil, classes, open_enums: open_enums)
|
|
106
|
+
elsif field.message_type
|
|
107
|
+
@message_class = classes.call(field.message_type)
|
|
108
|
+
end
|
|
109
|
+
if field.enum_type
|
|
110
|
+
@enum_module = classes.call(field.enum_type)
|
|
111
|
+
@enum_names = @enum_module.__names
|
|
112
|
+
@enum_numbers = @enum_module.__numbers
|
|
113
|
+
@closed_enum = !open_enums && field.enum_type.closed?
|
|
114
|
+
else
|
|
115
|
+
@closed_enum = false
|
|
116
|
+
end
|
|
117
|
+
@default = field.default unless @map || @repeated
|
|
118
|
+
# an enum default may name an alias; values are stored by their first name
|
|
119
|
+
@default = @enum_names[@enum_numbers.fetch(@default)] if @enum_names && @default
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
# @return [Boolean] whether the field is a message or group
|
|
123
|
+
def message?
|
|
124
|
+
@kind == MESSAGE || @kind == GROUP
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
# @return [Boolean] whether a singular value equals the type's zero
|
|
128
|
+
# value, so that a field without presence is left out on encode.
|
|
129
|
+
# Negative zero is not a default, as in protoc.
|
|
130
|
+
def zero?(value)
|
|
131
|
+
case @kind
|
|
132
|
+
when DOUBLE, FLOAT then value == 0 && (1.0 / value) > 0
|
|
133
|
+
when STRING, BYTES then value.empty?
|
|
134
|
+
when BOOL then value == false
|
|
135
|
+
when ENUM then (value.is_a?(Symbol) ? @enum_numbers[value] : value) == 0
|
|
136
|
+
else value == 0
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
def inspect
|
|
141
|
+
"#<#{self.class.name} #{@descriptor.full_name} slot=#{@index}>"
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
end
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Protostar
|
|
4
|
+
class Message
|
|
5
|
+
# Reading and writing a message class's fields by descriptor, name or
|
|
6
|
+
# number rather than through the accessors, for code that walks messages
|
|
7
|
+
# it does not know in advance: serializers, builders, form mappers.
|
|
8
|
+
# Every message class has one, made when the class is defined, frozen and
|
|
9
|
+
# shareable:
|
|
10
|
+
#
|
|
11
|
+
# r = Acme::Person.reflection
|
|
12
|
+
# r.fields.map(&:name) # => ["name", "id", "emails", "address"]
|
|
13
|
+
# r.get(person, :name) # => "Ada"
|
|
14
|
+
# r.set(person, "id", 7)
|
|
15
|
+
# r.set?(person, :address) # => false
|
|
16
|
+
# r.message_class(:address) # => Acme::Address
|
|
17
|
+
# r.each(person) { |field, value| ... } # fields that are set, in declaration order
|
|
18
|
+
#
|
|
19
|
+
# It lives on the class (`Klass.reflection`), where no field accessor can
|
|
20
|
+
# take its name. Values go through the same coercion and checks as the
|
|
21
|
+
# accessors: `set(m, f, v)` is `m.f = v`, `get(m, f)` is `m.f`.
|
|
22
|
+
class Reflection
|
|
23
|
+
# @return [Class] the message class it reflects
|
|
24
|
+
attr_reader :klass
|
|
25
|
+
# @return [Schema::Message]
|
|
26
|
+
attr_reader :descriptor
|
|
27
|
+
|
|
28
|
+
# @api private
|
|
29
|
+
def initialize(klass, descriptor)
|
|
30
|
+
@klass = klass
|
|
31
|
+
@descriptor = descriptor
|
|
32
|
+
@by_number = klass.__by_number
|
|
33
|
+
@by_name = klass.__by_name
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# @return [Array<Schema::Field>] in declaration order
|
|
37
|
+
def fields
|
|
38
|
+
@descriptor.fields
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# @return [Array<Schema::Oneof>] real oneofs; a proto3 `optional` field's
|
|
42
|
+
# synthetic oneof is left out, as it is no choice
|
|
43
|
+
def oneofs
|
|
44
|
+
@descriptor.real_oneofs
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# @param key [Schema::Field, String, Symbol, Integer] a field, its name or its number
|
|
48
|
+
# @return [Schema::Field, nil]
|
|
49
|
+
def field(key)
|
|
50
|
+
info(key)&.descriptor
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# @param message [Message] an instance of the class
|
|
54
|
+
# @param key [Schema::Field, String, Symbol, Integer]
|
|
55
|
+
# @return [Object] what the field's reader returns
|
|
56
|
+
# @raise [ArgumentError] when there is no such field
|
|
57
|
+
def get(message, key)
|
|
58
|
+
message.__get(info!(key))
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# @param message [Message]
|
|
62
|
+
# @param key [Schema::Field, String, Symbol, Integer]
|
|
63
|
+
# @param value [Object] nil clears the field
|
|
64
|
+
# @raise [ArgumentError] when there is no such field
|
|
65
|
+
# @raise [Protostar::TypeError] for a value the field cannot hold
|
|
66
|
+
def set(message, key, value)
|
|
67
|
+
message.__set(info!(key).index, value)
|
|
68
|
+
message
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# @param message [Message]
|
|
72
|
+
# @param key [Schema::Field, String, Symbol, Integer]
|
|
73
|
+
# @return [Boolean] set, for a field with presence; not empty, for a
|
|
74
|
+
# repeated or map field; not its zero value, for any other
|
|
75
|
+
def set?(message, key)
|
|
76
|
+
info = info!(key)
|
|
77
|
+
v = message.__values[info.index]
|
|
78
|
+
return false if v.nil?
|
|
79
|
+
return !v.empty? if info.repeated || info.map
|
|
80
|
+
info.presence || !info.zero?(v)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# @param message [Message]
|
|
84
|
+
# @param key [Schema::Field, String, Symbol, Integer]
|
|
85
|
+
def clear(message, key)
|
|
86
|
+
message.__set(info!(key).index, nil)
|
|
87
|
+
message
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# @param message [Message]
|
|
91
|
+
# @param oneof [Schema::Oneof, String, Symbol]
|
|
92
|
+
# @return [Schema::Field, nil] the member that is set
|
|
93
|
+
def which(message, oneof)
|
|
94
|
+
oneof = @descriptor.oneofs.find { |o| o.name == oneof.to_s } unless oneof.is_a?(Schema::Oneof)
|
|
95
|
+
raise ArgumentError, "#{@descriptor.full_name} has no oneof #{oneof.inspect}" unless oneof
|
|
96
|
+
oneof.fields.find { |f| !message.__values[@by_number.fetch(f.number).index].nil? }
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# @param key [Schema::Field, String, Symbol, Integer] a message or group field,
|
|
100
|
+
# or a map field whose values are messages
|
|
101
|
+
# @return [Class, nil] the class its values are instances of
|
|
102
|
+
def message_class(key)
|
|
103
|
+
info = info!(key)
|
|
104
|
+
info.map ? info.value.message_class : info.message_class
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
# @param key [Schema::Field, String, Symbol, Integer] an enum field, or a map field of enum values
|
|
108
|
+
# @return [Module, nil] the enum's module
|
|
109
|
+
def enum_module(key)
|
|
110
|
+
info = info!(key)
|
|
111
|
+
info.map ? info.value.enum_module : info.enum_module
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
# Yields each field that is {#set?}, in declaration order
|
|
115
|
+
# @param message [Message]
|
|
116
|
+
# @yieldparam field [Schema::Field]
|
|
117
|
+
# @yieldparam value [Object] what the reader returns
|
|
118
|
+
# @return [Enumerator] without a block
|
|
119
|
+
def each(message)
|
|
120
|
+
return enum_for(:each, message) unless block_given?
|
|
121
|
+
@klass.__fields.each do |info|
|
|
122
|
+
next unless set?(message, info.descriptor)
|
|
123
|
+
yield info.descriptor, message.__get(info)
|
|
124
|
+
end
|
|
125
|
+
nil
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def inspect
|
|
129
|
+
"#<#{self.class.name} #{@descriptor.full_name}>"
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
private
|
|
133
|
+
|
|
134
|
+
def info(key)
|
|
135
|
+
case key
|
|
136
|
+
when Integer then @by_number[key]
|
|
137
|
+
when Schema::Field
|
|
138
|
+
found = @by_number[key.number]
|
|
139
|
+
found if found&.descriptor.equal?(key)
|
|
140
|
+
else @by_name[key]
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def info!(key)
|
|
145
|
+
info(key) || raise(ArgumentError, "#{@descriptor.full_name} has no field #{key.inspect}")
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
end
|