protobuf 1.0.0

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.
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