require 'active_support/core_ext/module/aliasing' require 'protobuf/generators/file_generator' module Protobuf class CodeGenerator CodeGeneratorFatalError = Class.new(RuntimeError) def self.fatal(message) fail CodeGeneratorFatalError, message end def self.print_tag_warning_suppress STDERR.puts "Suppress tag warning output with PB_NO_TAG_WARNINGS=1." def self.print_tag_warning_suppress; end # rubocop:disable Lint/DuplicateMethods, Lint/NestedMethodDefinition end def self.warn(message) STDERR.puts("[WARN] #{message}") end private attr_accessor :request public def initialize(request_bytes) @request_bytes = request_bytes self.request = ::CSGoogle::Protobuf::Compiler::CodeGeneratorRequest.decode(request_bytes) end def eval_unknown_extensions! request.proto_file.each do |file_descriptor| ::Protobuf::Generators::FileGenerator.new(file_descriptor).eval_unknown_extensions! end self.request = ::CSGoogle::Protobuf::Compiler::CodeGeneratorRequest.decode(@request_bytes) end def generate_file(file_descriptor) ::Protobuf::Generators::FileGenerator.new(file_descriptor).generate_output_file end def response_bytes generated_files = request.proto_file.map do |file_descriptor| generate_file(file_descriptor) end ::CSGoogle::Protobuf::Compiler::CodeGeneratorResponse.encode( :file => generated_files, :supported_features => supported_features, ) end def supported_features # The only available feature is proto3 with optional fields. # This is backwards compatible with proto2 optional fields. ::CSGoogle::Protobuf::Compiler::CodeGeneratorResponse::Feature::FEATURE_PROTO3_OPTIONAL.to_i end Protobuf::Field::BaseField.module_eval do def define_set_method! end def set_without_options(message_instance, bytes) return message_instance[name] = decode(bytes) unless repeated? if map? hash = message_instance[name] entry = decode(bytes) # decoded value could be nil for an # enum value that is not recognized hash[entry.key] = entry.value unless entry.value.nil? return hash[entry.key] end return message_instance[name] << decode(bytes) unless packed? array = message_instance[name] stream = StringIO.new(bytes) if wire_type == ::Protobuf::WireType::VARINT array << decode(Varint.decode(stream)) until stream.eof? elsif wire_type == ::Protobuf::WireType::FIXED64 array << decode(stream.read(8)) until stream.eof? elsif wire_type == ::Protobuf::WireType::FIXED32 array << decode(stream.read(4)) until stream.eof? end end # Sets a MessageField that is known to be an option. # We must allow fields to be set one at a time, as option syntax allows us to # set each field within the option using a separate "option" line. def set_with_options(message_instance, bytes) if message_instance[name].is_a?(::Protobuf::Message) gp = CSGoogle::Protobuf if message_instance.is_a?(gp::EnumOptions) || message_instance.is_a?(gp::EnumValueOptions) || message_instance.is_a?(gp::FieldOptions) || message_instance.is_a?(gp::FileOptions) || message_instance.is_a?(gp::MethodOptions) || message_instance.is_a?(gp::ServiceOptions) || message_instance.is_a?(gp::MessageOptions) original_field = message_instance[name] decoded_field = decode(bytes) decoded_field.each_field do |subfield, subvalue| option_set(original_field, subfield, subvalue) { decoded_field.field?(subfield.tag) } end return end end set_without_options(message_instance, bytes) end alias_method :set, :set_with_options def option_set(message_field, subfield, subvalue) return unless yield if subfield.repeated? message_field[subfield.tag].concat(subvalue) elsif message_field[subfield.tag] && subvalue.is_a?(::Protobuf::Message) subvalue.each_field do |f, v| option_set(message_field[subfield.tag], f, v) { subvalue.field?(f.tag) } end else message_field[subfield.tag] = subvalue end end end end end