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