Class: Google::Protobuf::Internal::FileBuilder
- Inherits:
-
Object
- Object
- Google::Protobuf::Internal::FileBuilder
- Defined in:
- lib/google/protobuf/descriptor_dsl.rb
Instance Method Summary collapse
- #add_enum(name, &block) ⇒ Object
- #add_message(name, &block) ⇒ Object
- #build ⇒ Object
- #fix_nesting ⇒ Object
- #get_parent_msg(msgs_by_name, name, parent_name) ⇒ Object
-
#infer_package(names) ⇒ Object
The DSL can omit a package name; here we infer what the package is if was not specified.
-
#initialize(pool, name, options = {}) ⇒ FileBuilder
constructor
A new instance of FileBuilder.
- #internal_file_proto ⇒ Object
- #rewrite_enum_default(field) ⇒ Object
-
#rewrite_enum_defaults ⇒ Object
Historically we allowed enum defaults to be specified as a number.
-
#split_parent_name(msg_or_enum) ⇒ Object
We have to do some relatively complicated logic here for backward compatibility.
Constructor Details
#initialize(pool, name, options = {}) ⇒ FileBuilder
Returns a new instance of FileBuilder.
76 77 78 79 80 81 82 |
# File 'lib/google/protobuf/descriptor_dsl.rb', line 76 def initialize(pool, name, ={}) @pool = pool @file_proto = Google::Protobuf::FileDescriptorProto.new( name: name, syntax: .fetch(:syntax, "proto3") ) end |
Instance Method Details
#add_enum(name, &block) ⇒ Object
90 91 92 |
# File 'lib/google/protobuf/descriptor_dsl.rb', line 90 def add_enum(name, &block) EnumBuilder.new(name, @file_proto).instance_eval(&block) end |
#add_message(name, &block) ⇒ Object
84 85 86 87 88 |
# File 'lib/google/protobuf/descriptor_dsl.rb', line 84 def (name, &block) builder = MessageBuilder.new(name, self, @file_proto) builder.instance_eval(&block) builder.internal_add_synthetic_oneofs end |
#build ⇒ Object
282 283 284 285 286 |
# File 'lib/google/protobuf/descriptor_dsl.rb', line 282 def build rewrite_enum_defaults fix_nesting return @file_proto end |
#fix_nesting ⇒ Object
240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 |
# File 'lib/google/protobuf/descriptor_dsl.rb', line 240 def fix_nesting # Calculate and update package. msgs_by_name = @file_proto..map { |msg| [msg.name, msg] }.to_h enum_names = @file_proto.enum_type.map { |enum_proto| enum_proto.name } package = infer_package(msgs_by_name.keys + enum_names) if package @file_proto.package = package end # Update nesting based on package. final_msgs = Google::Protobuf::RepeatedField.new(:message, Google::Protobuf::DescriptorProto) final_enums = Google::Protobuf::RepeatedField.new(:message, Google::Protobuf::EnumDescriptorProto) # Note: We don't iterate over msgs_by_name.values because we want to # preserve order as listed in the DSL. @file_proto..each { |msg| parent_name, msg.name = split_parent_name(msg) if parent_name == package final_msgs << msg else get_parent_msg(msgs_by_name, msg.name, parent_name).nested_type << msg end } @file_proto.enum_type.each { |enum| parent_name, enum.name = split_parent_name(enum) if parent_name == package final_enums << enum else get_parent_msg(msgs_by_name, enum.name, parent_name).enum_type << enum end } @file_proto. = final_msgs @file_proto.enum_type = final_enums end |
#get_parent_msg(msgs_by_name, name, parent_name) ⇒ Object
232 233 234 235 236 237 238 |
# File 'lib/google/protobuf/descriptor_dsl.rb', line 232 def get_parent_msg(msgs_by_name, name, parent_name) parent_msg = msgs_by_name[parent_name] if parent_msg.nil? raise "To define name #{name}, there must be a message named #{parent_name} to enclose it" end return parent_msg end |
#infer_package(names) ⇒ Object
The DSL can omit a package name; here we infer what the package is if was not specified.
101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 |
# File 'lib/google/protobuf/descriptor_dsl.rb', line 101 def infer_package(names) # Package is longest common prefix ending in '.', if any. if not names.empty? min, max = names.minmax last_common_dot = nil min.size.times { |i| if min[i] != max[i] then break end if min[i] == "." then last_common_dot = i end } if last_common_dot return min.slice(0, last_common_dot) end end nil end |
#internal_file_proto ⇒ Object
278 279 280 |
# File 'lib/google/protobuf/descriptor_dsl.rb', line 278 def internal_file_proto @file_proto end |
#rewrite_enum_default(field) ⇒ Object
118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 |
# File 'lib/google/protobuf/descriptor_dsl.rb', line 118 def rewrite_enum_default(field) if field.type != :TYPE_ENUM or !field.has_default_value? or !field.has_type_name? return end value = field.default_value type_name = field.type_name if value.empty? or value[0].ord < "0".ord or value[0].ord > "9".ord return end if type_name.empty? || type_name[0] != "." return end type_name = type_name[1..-1] as_int = Integer(value) rescue return enum_desc = @pool.lookup(type_name) if enum_desc.is_a?(Google::Protobuf::EnumDescriptor) # Enum was defined in a previous file. name = enum_desc.lookup_value(as_int) if name # Update the default value in the proto. field.default_value = name end else # See if enum was defined in this file. @file_proto.enum_type.each { |enum_proto| if enum_proto.name == type_name enum_proto.value.each { |enum_value_proto| if enum_value_proto.number == as_int # Update the default value in the proto. field.default_value = enum_value_proto.name return end } # We found the right enum, but no value matched. return end } end end |
#rewrite_enum_defaults ⇒ Object
Historically we allowed enum defaults to be specified as a number. In retrospect this was a mistake as descriptors require defaults to be specified as a label. This can make a difference if multiple labels have the same number.
Here we do a pass over all enum defaults and rewrite numeric defaults by looking up their labels. This is complicated by the fact that the enum definition can live in either the symtab or the file_proto.
We take advantage of the fact that this is called before enums or messages are nested in other messages, so we only have to iterate one level deep.
175 176 177 178 179 180 181 |
# File 'lib/google/protobuf/descriptor_dsl.rb', line 175 def rewrite_enum_defaults @file_proto..each { |msg| msg.field.each { |field| rewrite_enum_default(field) } } end |
#split_parent_name(msg_or_enum) ⇒ Object
We have to do some relatively complicated logic here for backward compatibility.
In descriptor.proto, messages are nested inside other messages if that is what the original .proto file looks like. For example, suppose we have this foo.proto:
package foo; message Bar
message Baz {
}
The descriptor for this must look like this:
file {
name: "test.proto"
package: "foo"
message_type {
name: "Bar"
nested_type {
name: "Baz"
}
}
}
However, the Ruby generated code has always generated messages in a flat, non-nested way:
Google::Protobuf::DescriptorPool.generated_pool.build do
"foo.Bar" do
end
"foo.Bar.Baz" do
end
end
Here we need to do a translation where we turn this generated code into the above descriptor. We need to infer that “foo” is the package name, and not a message itself. */
222 223 224 225 226 227 228 229 230 |
# File 'lib/google/protobuf/descriptor_dsl.rb', line 222 def split_parent_name(msg_or_enum) name = msg_or_enum.name idx = name.rindex(?.) if idx return name[0...idx], name[idx+1..-1] else return nil, name end end |