protobuf 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (113) hide show
  1. data/.gitignore +5 -0
  2. data/Gemfile +3 -0
  3. data/Gemfile.lock +28 -0
  4. data/README.md +216 -0
  5. data/Rakefile +1 -0
  6. data/bin/rpc_server +117 -0
  7. data/bin/rprotoc +46 -0
  8. data/examples/addressbook.pb.rb +55 -0
  9. data/examples/addressbook.proto +24 -0
  10. data/examples/reading_a_message.rb +32 -0
  11. data/examples/writing_a_message.rb +46 -0
  12. data/lib/protobuf.rb +6 -0
  13. data/lib/protobuf/common/exceptions.rb +11 -0
  14. data/lib/protobuf/common/logger.rb +64 -0
  15. data/lib/protobuf/common/util.rb +59 -0
  16. data/lib/protobuf/common/wire_type.rb +10 -0
  17. data/lib/protobuf/compiler/compiler.rb +52 -0
  18. data/lib/protobuf/compiler/nodes.rb +323 -0
  19. data/lib/protobuf/compiler/proto.y +216 -0
  20. data/lib/protobuf/compiler/proto2.ebnf +79 -0
  21. data/lib/protobuf/compiler/proto_parser.rb +1425 -0
  22. data/lib/protobuf/compiler/template/rpc_bin.erb +4 -0
  23. data/lib/protobuf/compiler/template/rpc_client.erb +18 -0
  24. data/lib/protobuf/compiler/template/rpc_service.erb +25 -0
  25. data/lib/protobuf/compiler/template/rpc_service_implementation.erb +42 -0
  26. data/lib/protobuf/compiler/visitors.rb +302 -0
  27. data/lib/protobuf/descriptor/descriptor.proto +286 -0
  28. data/lib/protobuf/descriptor/descriptor.rb +55 -0
  29. data/lib/protobuf/descriptor/descriptor_builder.rb +143 -0
  30. data/lib/protobuf/descriptor/descriptor_proto.rb +138 -0
  31. data/lib/protobuf/descriptor/enum_descriptor.rb +33 -0
  32. data/lib/protobuf/descriptor/field_descriptor.rb +49 -0
  33. data/lib/protobuf/descriptor/file_descriptor.rb +37 -0
  34. data/lib/protobuf/message/decoder.rb +83 -0
  35. data/lib/protobuf/message/encoder.rb +46 -0
  36. data/lib/protobuf/message/enum.rb +62 -0
  37. data/lib/protobuf/message/extend.rb +8 -0
  38. data/lib/protobuf/message/field.rb +701 -0
  39. data/lib/protobuf/message/message.rb +402 -0
  40. data/lib/protobuf/message/protoable.rb +38 -0
  41. data/lib/protobuf/rpc/buffer.rb +74 -0
  42. data/lib/protobuf/rpc/client.rb +268 -0
  43. data/lib/protobuf/rpc/client_connection.rb +225 -0
  44. data/lib/protobuf/rpc/error.rb +34 -0
  45. data/lib/protobuf/rpc/error/client_error.rb +31 -0
  46. data/lib/protobuf/rpc/error/server_error.rb +43 -0
  47. data/lib/protobuf/rpc/rpc.pb.rb +107 -0
  48. data/lib/protobuf/rpc/server.rb +183 -0
  49. data/lib/protobuf/rpc/service.rb +244 -0
  50. data/lib/protobuf/rpc/stat.rb +70 -0
  51. data/lib/protobuf/version.rb +3 -0
  52. data/proto/rpc.proto +73 -0
  53. data/protobuf.gemspec +25 -0
  54. data/script/mk_parser +2 -0
  55. data/spec/functional/embedded_service_spec.rb +7 -0
  56. data/spec/proto/test.pb.rb +31 -0
  57. data/spec/proto/test.proto +31 -0
  58. data/spec/proto/test_service.rb +30 -0
  59. data/spec/proto/test_service_impl.rb +17 -0
  60. data/spec/spec_helper.rb +26 -0
  61. data/spec/unit/client_spec.rb +128 -0
  62. data/spec/unit/common/logger_spec.rb +121 -0
  63. data/spec/unit/enum_spec.rb +13 -0
  64. data/spec/unit/message_spec.rb +67 -0
  65. data/spec/unit/server_spec.rb +27 -0
  66. data/spec/unit/service_spec.rb +75 -0
  67. data/test/check_unbuild.rb +30 -0
  68. data/test/data/data.bin +3 -0
  69. data/test/data/data_source.py +14 -0
  70. data/test/data/types.bin +0 -0
  71. data/test/data/types_source.py +22 -0
  72. data/test/data/unk.png +0 -0
  73. data/test/proto/addressbook.pb.rb +66 -0
  74. data/test/proto/addressbook.proto +33 -0
  75. data/test/proto/addressbook_base.pb.rb +58 -0
  76. data/test/proto/addressbook_base.proto +26 -0
  77. data/test/proto/addressbook_ext.pb.rb +20 -0
  78. data/test/proto/addressbook_ext.proto +6 -0
  79. data/test/proto/collision.pb.rb +17 -0
  80. data/test/proto/collision.proto +5 -0
  81. data/test/proto/ext_collision.pb.rb +24 -0
  82. data/test/proto/ext_collision.proto +8 -0
  83. data/test/proto/ext_range.pb.rb +22 -0
  84. data/test/proto/ext_range.proto +7 -0
  85. data/test/proto/float_default.proto +10 -0
  86. data/test/proto/lowercase.pb.rb +30 -0
  87. data/test/proto/lowercase.proto +9 -0
  88. data/test/proto/merge.pb.rb +39 -0
  89. data/test/proto/merge.proto +15 -0
  90. data/test/proto/nested.pb.rb +30 -0
  91. data/test/proto/nested.proto +9 -0
  92. data/test/proto/optional_field.pb.rb +35 -0
  93. data/test/proto/optional_field.proto +12 -0
  94. data/test/proto/packed.pb.rb +22 -0
  95. data/test/proto/packed.proto +6 -0
  96. data/test/proto/rpc.proto +6 -0
  97. data/test/proto/types.pb.rb +84 -0
  98. data/test/proto/types.proto +37 -0
  99. data/test/test_addressbook.rb +56 -0
  100. data/test/test_compiler.rb +325 -0
  101. data/test/test_descriptor.rb +122 -0
  102. data/test/test_enum_value.rb +41 -0
  103. data/test/test_extension.rb +36 -0
  104. data/test/test_lowercase.rb +11 -0
  105. data/test/test_message.rb +128 -0
  106. data/test/test_optional_field.rb +103 -0
  107. data/test/test_packed_field.rb +40 -0
  108. data/test/test_parse.rb +15 -0
  109. data/test/test_repeated_types.rb +132 -0
  110. data/test/test_serialize.rb +61 -0
  111. data/test/test_standard_message.rb +96 -0
  112. data/test/test_types.rb +226 -0
  113. metadata +261 -0
@@ -0,0 +1,24 @@
1
+ package tutorial;
2
+
3
+ message Person {
4
+ required string name = 1;
5
+ required int32 id = 2;
6
+ optional string email = 3;
7
+
8
+ enum PhoneType {
9
+ MOBILE = 0;
10
+ HOME = 1;
11
+ WORK = 2;
12
+ }
13
+
14
+ message PhoneNumber {
15
+ required string number = 1;
16
+ optional PhoneType type = 2 [default = HOME];
17
+ }
18
+
19
+ repeated PhoneNumber phone = 4;
20
+ }
21
+
22
+ message AddressBook {
23
+ repeated Person person = 1;
24
+ }
@@ -0,0 +1,32 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'addressbook.pb'
4
+
5
+ def list_people(address_book)
6
+ address_book.person.each do |person|
7
+ puts "Person ID: #{person.id}"
8
+ puts " Name: #{person.name}"
9
+ puts " E-mail: #{person.email}" unless person.email.empty?
10
+ person.phone.each do |phone_number|
11
+ print(case phone_number.type
12
+ when Tutorial::Person::PhoneType::MOBILE
13
+ ' Mobile phone #: '
14
+ when Tutorial::Person::PhoneType::HOME
15
+ ' Home phone #: '
16
+ when Tutorial::Person::PhoneType::WORK
17
+ ' Work phone #: '
18
+ end)
19
+ puts phone_number.number
20
+ end
21
+ end
22
+ end
23
+
24
+ unless ARGV.size == 1
25
+ puts "Usage: #{$0} ADDRESS_BOOK_FILE"
26
+ exit
27
+ end
28
+
29
+ address_book = Tutorial::AddressBook.new
30
+ address_book.parse_from_file ARGV[0]
31
+
32
+ list_people address_book
@@ -0,0 +1,46 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'addressbook.pb'
4
+
5
+ def prompt_for_address(person)
6
+ print 'Enter person ID number: '
7
+ person.id = STDIN.gets.strip.to_i
8
+ print 'Enter name: '
9
+ person.name = STDIN.gets.strip
10
+ print 'Enter email address (blank for none): '
11
+ email = STDIN.gets.strip
12
+ person.email = email unless email.empty?
13
+
14
+ loop do
15
+ print 'Enter a phone number (or leave blank to finish): '
16
+ break if (number = STDIN.gets.strip).empty?
17
+
18
+ person.phone << Tutorial::Person::PhoneNumber.new
19
+ person.phone.last.number = number
20
+
21
+ print 'Is this a mobile, home, or work phone? '
22
+ person.phone.last.type =
23
+ case type = STDIN.gets.strip
24
+ when 'mobile'
25
+ Tutorial::Person::PhoneType::MOBILE
26
+ when 'home'
27
+ Tutorial::Person::PhoneType::HOME
28
+ when 'work'
29
+ Tutorial::Person::PhoneType::WORK
30
+ else
31
+ puts 'Unknown phone type; leaving as default value.'
32
+ nil
33
+ end
34
+ end
35
+ end
36
+
37
+ unless ARGV.size == 1
38
+ puts "Usage: #{$0} ADDRESS_BOOK_FILE"
39
+ exit
40
+ end
41
+
42
+ address_book = Tutorial::AddressBook.new
43
+ address_book.parse_from_file ARGV[0] if File.exist? ARGV[0]
44
+ address_book.person << Tutorial::Person.new
45
+ prompt_for_address address_book.person.last
46
+ address_book.serialize_to_file ARGV[0]
data/lib/protobuf.rb ADDED
@@ -0,0 +1,6 @@
1
+ module Protobuf
2
+ end
3
+
4
+ require 'protobuf/rpc/client'
5
+ require 'protobuf/rpc/server'
6
+ require 'protobuf/rpc/service'
@@ -0,0 +1,11 @@
1
+ module Protobuf
2
+
3
+ class Error < StandardError; end
4
+
5
+ class InvalidWireType < Error; end
6
+
7
+ class NotInitializedError < Error; end
8
+
9
+ class TagCollisionError < Error; end
10
+
11
+ end
@@ -0,0 +1,64 @@
1
+ require 'logger'
2
+
3
+ module Protobuf
4
+ class Logger < ::Logger
5
+
6
+ class << self
7
+ attr_accessor :file, :level
8
+
9
+ # One-line file/level configuration
10
+ def configure options
11
+ self.file = options[:file] if options[:file]
12
+ self.level = options[:level] if options[:level]
13
+ end
14
+
15
+ def configured?
16
+ ! instance.nil?
17
+ end
18
+
19
+ # Use to reset the instance
20
+ def reset_device!
21
+ self.file = self.level = @__instance = nil
22
+ end
23
+
24
+ # Singleton instance
25
+ def instance
26
+ @__instance ||= begin
27
+ log = nil
28
+ if @file and @level
29
+ log = new(self.file)
30
+ log.level = self.level
31
+ end
32
+ log
33
+ end
34
+ end
35
+
36
+ # Stub out the log methods for Protobuf::Logger as singleton methods
37
+ [:debug, :info, :warn, :error, :fatal, :any, :add, :log].each do |m|
38
+ define_method(m) do |*params, &block|
39
+ instance && instance.__send__(m, *params, &block)
40
+ end
41
+ end
42
+ end
43
+
44
+ #
45
+ # LogMethods module for log method including, e.g.:
46
+ #
47
+ # class MyClass
48
+ # include Protobuf::Logger::LogMethods
49
+ # ...
50
+ # end
51
+ #
52
+ # Produce a module to allow "include" in other classes to avoid
53
+ # cluttering the namespace of the including class with the other methods defined above
54
+ #
55
+ module LogMethods
56
+ [:debug, :info, :warn, :error, :fatal, :any, :add, :log].each do |m|
57
+ define_method("log_#{m}") do |*params, &block|
58
+ Protobuf::Logger.__send__(m, *params, &block)
59
+ end
60
+ end
61
+ end
62
+
63
+ end
64
+ end
@@ -0,0 +1,59 @@
1
+ module Protobuf
2
+ module Util
3
+ module_function
4
+
5
+ # Takes a string or symbol and camelizes it:
6
+ # Expects: some_long_name
7
+ # Returns: SomeLongName
8
+ def camelize(str)
9
+ if (str.is_a? Array)
10
+ str.map{|p| camelize(p.to_s) }.join('::')
11
+ else
12
+ str.to_s.gsub(/(?:\A|_)(\w)/) { $1.upcase }
13
+ end
14
+ end
15
+
16
+ # Expects: SomeLongName
17
+ # Returns: some_long_name
18
+ def underscore(str)
19
+ str.to_s.gsub(/\B[A-Z]/, '_\&').downcase
20
+ end
21
+
22
+ # Expects: SomeModule::Path
23
+ # Returns: some_module/path
24
+ def module_to_path(str)
25
+ pkg = str.to_s.split('::')
26
+ pkg.map{|e| underscore(e) }.join('/')
27
+ end
28
+
29
+ # Expects: PackageA.PackageB
30
+ # Returns: package_a/package_b
31
+ def package_to_path(str)
32
+ str.to_s.split('.').map{|e| underscore(e) }.join('/')
33
+ end
34
+
35
+ # Takes a class constant and converts it to a string resembling a java package path
36
+ # Expects: ModA::ModB::MyService
37
+ # Returns: mod_a.mod_b.MyService
38
+ def packagize(klass)
39
+ klass = klass.to_s.split('::') unless klass.is_a? Array
40
+ klass_name = klass.pop
41
+ klass.map{|e| underscore(e) }.join('.') + ".#{klass_name}"
42
+ end
43
+
44
+ # The reverse of packagize. Takes a string resembling a java package path
45
+ # and converts it into a module constant
46
+ # Expects: mod_a.mod_b.MyService
47
+ # Returns: ModA::ModB::MyService
48
+ def moduleize(str)
49
+ str = str.join('.') if str.is_a? Array
50
+ str.split('.').map{|e| camelize(e) }.join('::')
51
+ end
52
+
53
+ def constantize(klass)
54
+ constants = moduleize(klass).split('::')
55
+ constants.inject(Module.const_get(constants.shift)) {|const, obj| const.const_get(obj) }
56
+ end
57
+
58
+ end
59
+ end
@@ -0,0 +1,10 @@
1
+ module Protobuf
2
+ module WireType
3
+ VARINT = 0
4
+ FIXED64 = 1
5
+ LENGTH_DELIMITED = 2
6
+ START_GROUP = 3
7
+ END_GROUP = 4
8
+ FIXED32 = 5
9
+ end
10
+ end
@@ -0,0 +1,52 @@
1
+ require 'fileutils'
2
+ require 'protobuf/compiler/proto_parser'
3
+ require 'protobuf/compiler/nodes'
4
+ require 'protobuf/compiler/visitors'
5
+
6
+ module Protobuf
7
+ class Compiler
8
+ def self.compile(proto_file, proto_dir='.', out_dir='.', file_create=true)
9
+ self.new.compile(proto_file, proto_dir, out_dir, file_create)
10
+ end
11
+
12
+ def compile(proto_file, proto_dir='.', out_dir='.', file_create=true)
13
+ create_message(proto_file, proto_dir, out_dir, file_create)
14
+ create_rpc(proto_file, proto_dir, out_dir, file_create)
15
+ end
16
+
17
+ def create_message(proto_file, proto_dir='.', out_dir='.', file_create=true)
18
+ rb_file = File.join(out_dir, File.basename(proto_file).sub(/\.[^\0]*\z/, '') + '.pb.rb')
19
+ proto_path = validate_existence(proto_file, proto_dir)
20
+
21
+ message_visitor = Visitor::CreateMessageVisitor.new(proto_file, proto_dir, out_dir)
22
+ File.open(proto_path) do |file|
23
+ message_visitor.visit(ProtoParser.new.parse(file))
24
+ end
25
+ message_visitor.create_files(rb_file, out_dir, file_create)
26
+ end
27
+
28
+ def create_rpc(proto_file, proto_dir='.', out_dir='.', file_create=true)
29
+ message_file = File.join(out_dir, File.basename(proto_file).sub(/\.[^\0]*\z/, '') + '.pb.rb')
30
+ proto_path = validate_existence(proto_file, proto_dir)
31
+
32
+ rpc_visitor = Visitor::CreateRpcVisitor.new
33
+ File.open(proto_path) do |file|
34
+ rpc_visitor.visit(ProtoParser.new.parse(file))
35
+ end
36
+ rpc_visitor.create_files(message_file, out_dir, file_create)
37
+ end
38
+
39
+ def validate_existence(path, base_dir)
40
+ if File.exist?(path)
41
+ path
42
+ else
43
+ newpath = File.join(base_dir, path)
44
+ if File.exist?(newpath)
45
+ newpath
46
+ else
47
+ raise ArgumentError, "File does not exist: #{path}"
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,323 @@
1
+ require 'protobuf/common/util'
2
+ require 'protobuf/descriptor/descriptor_proto'
3
+
4
+ module Protobuf
5
+ module Node
6
+ class Base
7
+ def define_in_the_file(visitor)
8
+ visitor.write("defined_in __FILE__") if visitor.attach_proto?
9
+ end
10
+
11
+ def accept_message_visitor(visitor)
12
+ end
13
+
14
+ def accept_rpc_visitor(vistor)
15
+ end
16
+
17
+ def accept_descriptor_visitor(visitor)
18
+ end
19
+ end
20
+
21
+ class ProtoNode < Base
22
+ attr_reader :children
23
+
24
+ def initialize(children)
25
+ @children = children || []
26
+ end
27
+
28
+ def accept_message_visitor(visitor)
29
+ visitor.write('### Generated by rprotoc. DO NOT EDIT!')
30
+ visitor.write("### <proto file: #{visitor.proto_file}>") if visitor.attach_proto?
31
+ visitor.write(visitor.commented_proto_contents) if visitor.attach_proto?
32
+ visitor.write(<<-EOS)
33
+ require 'protobuf/message/message'
34
+ require 'protobuf/message/enum'
35
+ require 'protobuf/message/extend'
36
+ EOS
37
+ @children.each {|child| child.accept_message_visitor(visitor) }
38
+ visitor.close_ruby
39
+ end
40
+
41
+ def accept_rpc_visitor(visitor)
42
+ @children.each {|child| child.accept_rpc_visitor(visitor) }
43
+ end
44
+
45
+ def accept_descriptor_visitor(visitor)
46
+ descriptor = Google::Protobuf::FileDescriptorProto.new(:name => visitor.filename)
47
+ visitor.file_descriptor = descriptor
48
+ visitor.in_context(descriptor) do
49
+ @children.each {|child| child.accept_descriptor_visitor(visitor) }
50
+ end
51
+ end
52
+ end
53
+
54
+ class ImportNode < Base
55
+ def initialize(path)
56
+ @path = path
57
+ end
58
+
59
+ def accept_message_visitor(visitor)
60
+ visitor.write("require '#{visitor.required_message_from_proto(@path)}'")
61
+ end
62
+
63
+ def accept_descriptor_visitor(visitor)
64
+ visitor.current_descriptor.dependency << @path
65
+ end
66
+ end
67
+
68
+ class PackageNode < Base
69
+ def initialize(path_list)
70
+ @path_list = path_list
71
+ end
72
+
73
+ def accept_message_visitor(visitor)
74
+ visitor.package = @path_list.dup
75
+ @path_list.each do |path|
76
+ visitor.write("module #{Util.camelize(path)}")
77
+ visitor.increment
78
+ end
79
+ end
80
+
81
+ def accept_rpc_visitor(visitor)
82
+ visitor.package = @path_list.dup
83
+ end
84
+
85
+ def accept_descriptor_visitor(visitor)
86
+ visitor.current_descriptor.package = @path_list.join('.')
87
+ end
88
+ end
89
+
90
+ class OptionNode < Base
91
+ def initialize(name_list, value)
92
+ @name_list, @value = name_list, value
93
+ end
94
+
95
+ def accept_message_visitor(visitor)
96
+ visitor.write("::Protobuf::OPTIONS[:#{@name_list.join('.').inspect}] = #{@value.inspect}")
97
+ end
98
+
99
+ def accept_descriptor_visitor(visitor)
100
+ visitor.add_option(@name_list.join('.'), @value)
101
+ end
102
+ end
103
+
104
+ class MessageNode < Base
105
+ def initialize(name, children)
106
+ @name, @children = name, children
107
+ end
108
+
109
+ def accept_message_visitor(visitor)
110
+ class_name = @name.to_s
111
+ class_name.gsub!(/\A[a-z]/) {|c| c.upcase}
112
+ visitor.write("class #{class_name} < ::Protobuf::Message")
113
+ visitor.in_context(self.class) do
114
+ define_in_the_file(visitor)
115
+ @children.each {|child| child.accept_message_visitor(visitor) }
116
+ end
117
+ visitor.write('end')
118
+ end
119
+
120
+ def accept_descriptor_visitor(visitor)
121
+ descriptor = Google::Protobuf::DescriptorProto.new(:name => @name.to_s)
122
+ visitor.descriptor = descriptor
123
+ visitor.in_context(descriptor) do
124
+ @children.each {|child| child.accept_descriptor_visitor(visitor) }
125
+ end
126
+ end
127
+ end
128
+
129
+ class ExtendNode < Base
130
+ def initialize(name, children)
131
+ @name, @children = name, children
132
+ end
133
+
134
+ def accept_message_visitor(visitor)
135
+ name = @name.is_a?(Array) ? @name.join : name.to_s
136
+ visitor.write("class #{name} < ::Protobuf::Message")
137
+ visitor.in_context(self.class) do
138
+ define_in_the_file(visitor)
139
+ @children.each {|child| child.accept_message_visitor(visitor) }
140
+ end
141
+ visitor.write('end')
142
+ end
143
+
144
+ def accept_descriptor_visitor(visitor)
145
+ # TODO: how should i handle this?
146
+ end
147
+ end
148
+
149
+ class EnumNode < Base
150
+ def initialize(name, children)
151
+ @name, @children = name, children
152
+ end
153
+
154
+ def accept_message_visitor(visitor)
155
+ visitor.write("class #{@name} < ::Protobuf::Enum")
156
+ visitor.in_context(self.class) do
157
+ define_in_the_file(visitor)
158
+ @children.each {|child| child.accept_message_visitor(visitor) }
159
+ end
160
+ visitor.write('end')
161
+ end
162
+
163
+ def accept_descriptor_visitor(visitor)
164
+ descriptor = Google::Protobuf::EnumDescriptorProto.new(:name => @name.to_s)
165
+ visitor.enum_descriptor = descriptor
166
+ visitor.in_context(descriptor) do
167
+ @children.each {|child| child.accept_descriptor_visitor(visitor) }
168
+ end
169
+ end
170
+ end
171
+
172
+ class EnumFieldNode < Base
173
+ def initialize(name, value)
174
+ @name, @value = name, value
175
+ end
176
+
177
+ def accept_message_visitor(visitor)
178
+ visitor.write("define :#{@name}, #{@value}")
179
+ end
180
+
181
+ def accept_descriptor_visitor(visitor)
182
+ descriptor = Google::Protobuf::EnumValueDescriptorProto.new(:name => @name.to_s, :number => @value)
183
+ visitor.enum_value_descriptor = descriptor
184
+ end
185
+ end
186
+
187
+ class ServiceNode < Base
188
+ def initialize(name, children)
189
+ @name, @children = name, children
190
+ end
191
+
192
+ def accept_message_visitor(visitor)
193
+ # do nothing
194
+ end
195
+
196
+ def accept_rpc_visitor(visitor)
197
+ visitor.current_service = @name
198
+ @children.each {|child| child.accept_rpc_visitor(visitor) }
199
+ end
200
+
201
+ def accept_descriptor_visitor(visitor)
202
+ descriptor = Google::Protobuf::ServiceDescriptorProto.new(:name => @name.to_s)
203
+ visitor.service_descriptor = descriptor
204
+ visitor.in_context(descriptor) do
205
+ @children.each {|child| child.accept_descriptor_visitor(visitor) }
206
+ end
207
+ end
208
+ end
209
+
210
+ class RpcNode < Base
211
+ def initialize(name, request, response)
212
+ @name, @request, @response = name, request, response
213
+ end
214
+
215
+ def accept_message_visitor(visitor)
216
+ # do nothing
217
+ end
218
+
219
+ def accept_rpc_visitor(visitor)
220
+ visitor.add_rpc(@name, @request, @response)
221
+ end
222
+
223
+ def accept_descriptor_visitor(visitor)
224
+ descriptor = Google::Protobuf::MethodDescriptorProto.new(:name => @name.to_s, :input_type => @request.to_s, :output_type => @response.to_s)
225
+ visitor.method_descriptor = descriptor
226
+ end
227
+ end
228
+
229
+ class GroupNode < Base
230
+ def initialize(label, name, value, children)
231
+ @label, @name, @value, @children = label, name, value, children
232
+ end
233
+
234
+ def accept_message_visitor(visitor)
235
+ raise NotImplementedError
236
+ end
237
+
238
+ def accept_descriptor_visitor(visitor)
239
+ raise NotImplementedError
240
+ end
241
+ end
242
+
243
+ class FieldNode < Base
244
+ def initialize(label, type, name, value, opts={})
245
+ @label, @type, @name, @value, @opts = label, type, name, value, opts
246
+ end
247
+
248
+ def accept_message_visitor(visitor)
249
+ opts = @opts.empty? ? '' : ", #{@opts.map{|k, v| ":#{k} => #{v.inspect}" }.join(', ')}"
250
+ if visitor.context.first == ExtendNode
251
+ opts << ', :extension => true'
252
+ end
253
+ type = if @type.is_a?(Array)
254
+ then (@type.size > 1) ? "'#{@type.map{|e| Util.camelize(e) }.join('::')}'" : @type[0]
255
+ else @type
256
+ end
257
+ visitor.write("#{@label} :#{type}, :#{@name}, #{@value}#{opts}")
258
+ end
259
+
260
+ def accept_descriptor_visitor(visitor)
261
+ descriptor = Google::Protobuf::FieldDescriptorProto.new(:name => @name.to_s, :number => @value)
262
+ descriptor.label = Google::Protobuf::FieldDescriptorProto::Label.const_get("LABEL_#{@label.to_s.upcase}")
263
+ descriptor.type = Google::Protobuf::FieldDescriptorProto::Type.const_get("TYPE_#{@type.to_s.upcase}") if predefined_type?
264
+ descriptor.type_name = @type.is_a?(Array) ? @type.join : @type.to_s
265
+ @opts.each do |key, val|
266
+ case key.to_sym
267
+ when :default
268
+ descriptor.default_value = val.to_s
269
+ end
270
+ end
271
+ visitor.field_descriptor = descriptor
272
+ end
273
+
274
+ private
275
+
276
+ def predefined_type?
277
+ # TODO: constantize
278
+ %w{double float int64 uint64 int32 fixed64 fixed32 bool string group message bytes uint32 enum sfixed32 sfixed64 sint32 sint64}.include?(@type.to_s)
279
+ end
280
+ end
281
+
282
+ class ExtensionsNode < Base
283
+ def initialize(range)
284
+ @range = range
285
+ end
286
+
287
+ def accept_message_visitor(visitor)
288
+ visitor.write("extensions #{@range.first.to_s}")
289
+ end
290
+
291
+ def accept_descriptor_visitor(visitor)
292
+ descriptor = Google::Protobuf::DescriptorProto::ExtensionRange.new(:start => @range.first.low)
293
+ case @range.first.high
294
+ when NilClass then # ignore
295
+ when :max then descriptor.end = 1
296
+ else descriptor.end = @range.first.high
297
+ end
298
+ visitor.extension_range_descriptor = descriptor
299
+ end
300
+ end
301
+
302
+ class ExtensionRangeNode < Base
303
+ attr_reader :low, :high
304
+
305
+ def initialize(low, high=nil)
306
+ @low, @high = low, high
307
+ end
308
+
309
+ #def accept_message_visitor(visitor)
310
+ #end
311
+
312
+ def to_s
313
+ if @high.nil?
314
+ @low.to_s
315
+ elsif @high == :max
316
+ "#{@low}..::Protobuf::Extend::MAX"
317
+ else
318
+ "#{@low}..#{@high}"
319
+ end
320
+ end
321
+ end
322
+ end
323
+ end