• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/ruby
2# Protocol Buffers - Google's data interchange format
3# Copyright 2008 Google Inc.  All rights reserved.
4# https://developers.google.com/protocol-buffers/
5#
6# Redistribution and use in source and binary forms, with or without
7# modification, are permitted provided that the following conditions are
8# met:
9#
10#     * Redistributions of source code must retain the above copyright
11# notice, this list of conditions and the following disclaimer.
12#     * Redistributions in binary form must reproduce the above
13# copyright notice, this list of conditions and the following disclaimer
14# in the documentation and/or other materials provided with the
15# distribution.
16#     * Neither the name of Google Inc. nor the names of its
17# contributors may be used to endorse or promote products derived from
18# this software without specific prior written permission.
19#
20# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31
32require 'google/protobuf/any_pb'
33require 'google/protobuf/duration_pb'
34require 'google/protobuf/field_mask_pb'
35require 'google/protobuf/struct_pb'
36require 'google/protobuf/timestamp_pb'
37
38module Google
39  module Protobuf
40
41    Any.class_eval do
42      def self.pack(msg, type_url_prefix='type.googleapis.com/')
43        any = self.new
44        any.pack(msg, type_url_prefix)
45        any
46      end
47
48      def pack(msg, type_url_prefix='type.googleapis.com/')
49        if type_url_prefix.empty? or type_url_prefix[-1] != '/' then
50          self.type_url = "#{type_url_prefix}/#{msg.class.descriptor.name}"
51        else
52          self.type_url = "#{type_url_prefix}#{msg.class.descriptor.name}"
53        end
54        self.value = msg.to_proto
55      end
56
57      def unpack(klass)
58        if self.is(klass) then
59          klass.decode(self.value)
60        else
61          nil
62        end
63      end
64
65      def type_name
66        return self.type_url.split("/")[-1]
67      end
68
69      def is(klass)
70        return self.type_name == klass.descriptor.name
71      end
72    end
73
74    Timestamp.class_eval do
75      if RUBY_VERSION < "2.5"
76        def to_time
77          Time.at(self.to_f)
78        end
79      else
80        def to_time
81          Time.at(seconds, nanos, :nanosecond)
82        end
83      end
84
85      def from_time(time)
86        self.seconds = time.to_i
87        self.nanos = time.nsec
88      end
89
90      def to_i
91        self.seconds
92      end
93
94      def to_f
95        self.seconds + (self.nanos.quo(1_000_000_000))
96      end
97    end
98
99    Duration.class_eval do
100      def to_f
101        self.seconds + (self.nanos.to_f / 1_000_000_000)
102      end
103    end
104
105    class UnexpectedStructType < Google::Protobuf::Error; end
106
107    Value.class_eval do
108      def to_ruby(recursive = false)
109        case self.kind
110        when :struct_value
111          if recursive
112            self.struct_value.to_h
113          else
114            self.struct_value
115          end
116        when :list_value
117          if recursive
118            self.list_value.to_a
119          else
120            self.list_value
121          end
122        when :null_value
123          nil
124        when :number_value
125          self.number_value
126        when :string_value
127          self.string_value
128        when :bool_value
129          self.bool_value
130        else
131          raise UnexpectedStructType
132        end
133      end
134
135      def from_ruby(value)
136        case value
137        when NilClass
138          self.null_value = 0
139        when Numeric
140          self.number_value = value
141        when String
142          self.string_value = value
143        when TrueClass
144          self.bool_value = true
145        when FalseClass
146          self.bool_value = false
147        when Struct
148          self.struct_value = value
149        when Hash
150          self.struct_value = Struct.from_hash(value)
151        when ListValue
152          self.list_value = value
153        when Array
154          self.list_value = ListValue.from_a(value)
155        else
156          raise UnexpectedStructType
157        end
158      end
159    end
160
161    Struct.class_eval do
162      def [](key)
163        self.fields[key].to_ruby
164      rescue NoMethodError
165        nil
166      end
167
168      def []=(key, value)
169        unless key.is_a?(String)
170          raise UnexpectedStructType, "Struct keys must be strings."
171        end
172        self.fields[key] ||= Google::Protobuf::Value.new
173        self.fields[key].from_ruby(value)
174      end
175
176      def to_h
177        ret = {}
178        self.fields.each { |key, val| ret[key] = val.to_ruby(true) }
179        ret
180      end
181
182      def self.from_hash(hash)
183        ret = Struct.new
184        hash.each { |key, val| ret[key] = val }
185        ret
186      end
187
188      def has_key?(key)
189        self.fields.has_key?(key)
190      end
191    end
192
193    ListValue.class_eval do
194      include Enumerable
195
196      def length
197        self.values.length
198      end
199
200      def [](index)
201        self.values[index].to_ruby
202      end
203
204      def []=(index, value)
205        self.values[index].from_ruby(value)
206      end
207
208      def <<(value)
209        wrapper = Google::Protobuf::Value.new
210        wrapper.from_ruby(value)
211        self.values << wrapper
212      end
213
214      def each
215        self.values.each { |x| yield(x.to_ruby) }
216      end
217
218      def to_a
219        self.values.map { |x| x.to_ruby(true) }
220      end
221
222      def self.from_a(arr)
223        ret = ListValue.new
224        arr.each { |val| ret << val }
225        ret
226      end
227    end
228
229  end
230end
231