lib/protobuf_transpiler.rb in protobuf_transpiler-1.0.0 vs lib/protobuf_transpiler.rb in protobuf_transpiler-1.1.0

- old
+ new

@@ -10,29 +10,29 @@ paths = $LOAD_PATH.map { |p| "#{p}/**/public/**/*.proto" } proto_files = Dir[*paths].join ' ' proto_paths = proto_files .split.map { |p| p.sub %r{(?<=public).*}, '' } .uniq.join ' ' - out_path = "#{Rails.root}/app/stubs/" + out_path = "#{Rails.root}/app/stubs/" FileUtils.mkdir_p out_path `grpc_tools_ruby_protoc --ruby_out=#{out_path} --grpc_out=#{out_path} #{proto_files} -I #{proto_paths}` # remove possibly useless require from stub file unless keep_require Dir['app/stubs/**/*.rb'].each do |fp| f = File.read fp - File.write fp, (f.sub /\n(require.*?'\n)+/, '') + File.write fp, (f.sub %r{\n(require.*?'\n)+}, '') end end # make zeitwerk happy Dir['app/stubs/**'] .filter { |f| File.directory? f } .each { |dir| requires = Dir.chdir dir do curr_dir = Dir.pwd.split('/').last - Dir['*.rb'].map { |s| "require_relative './#{curr_dir}/#{s.sub(/.rb$/, '')}'" } + Dir['*.rb'].map { |s| "require_relative './#{curr_dir}/#{s.sub %r{.rb$}, ''}'" } end File.write "#{dir}.rb", requires.join("\n") } end @@ -50,55 +50,120 @@ .map { |c| Object.const_get c.camelize } stubs_modules.each do |m| out = m .constants + .sort .map { |c| m.const_get c } .each_with_object({ messages: [], services: [] }) { |c, acc| if c.is_a? Class acc[:messages] << class_annotations(c) else acc[:services] << module_annotations(c) end } - types_file, services_file = Dir["app/stubs/#{m.name.downcase}/*.rb"] + types_file, services_file = Dir["app/stubs/#{m.name.underscore}/*.rb"] .sort_by { |s| s.scan('services').count } [types_file, services_file] .zip([out[:messages], out[:services]]) .each { |file, content| annotate_file file, content } end end private - ANNOTATE_DELIMITER = '===== Protobuf Annotation =====' + ANNOTATE_DELIMITER = '# ===== Protobuf Annotation =====' - def class_annotations c - c - .descriptor.entries.map { |d| "\t#{d.name}: #{d.type}" } - .prepend("#{c.name}") - .join "\n" + def class_annotations klass + oneof_fields, oneof_annotations = lambda do |descriptor| + [ + descriptor.each_oneof + .flat_map { |o| o.entries.map(&:name) }, + descriptor.each_oneof + .map { |o| ["#{o.name}:", o.entries.map { |e| "\t| #{e.name}: #{type_handler e}" }].join("\n") } + .map { |s| s.gsub(%r{\t}, "\t\t").prepend("\t") + "\n" } + ] + end.call(klass.descriptor) + + map_fields = lambda do |descriptor, instance| + descriptor.entries + .map(&:name) + .filter { |n| instance[n].class == Google::Protobuf::Map } + end.call(klass.descriptor, klass.new) + + [ + klass.name.to_s, + klass.constants(false).sort + .map { |msg| klass.const_get msg } + .map { |msg_class| class_annotations(msg_class) } + .map { |s| s.gsub(%r{\t}, "\t\t").prepend("\t") } + .prepend("\n"), + oneof_annotations, + klass.descriptor.entries + .reject{|d|oneof_fields.include? d.name} + .map { |d| + "\t#{d.name}: "+ + type_handler(d, map_fields) + } + .join("\n"), + "\n" + + ].join('') end - def module_annotations m - m + def type_handler d, map_fields = [] + case + when d.is_a?(Symbol) + d + when map_fields.include?(d.name) + d.subtype.entries.then { |k,v| "Map<#{k.type}, #{type_handler(v)}>" } + when d.type == :message + d.subtype.msgclass.then{|t| d.label == :repeated ? "[#{t}]" : t} + else + d.type.then{|t| d.label == :repeated ? "[#{t}]" : t} + end.to_s + end + + def module_annotations mod + mod .const_get('Service') - .rpc_descs.map { |_, d| "\t#{d.name}(#{d.input}): #{d.output}" } - .prepend("#{m.name}") + .rpc_descs.sort + .map { |_, d| "\t#{d.name}(#{d.input}): #{d.output}" } + .prepend(mod.name.to_s) .join "\n" end def annotate_file file, content old_content = File.read file content = content.join("\n").gsub(%r{^}, '# ') new_content = if old_content.match? ANNOTATE_DELIMITER # replace annotation content - old_content.sub %r{(?<=#{ANNOTATE_DELIMITER})(.|\n)*?(?=##{ANNOTATE_DELIMITER})}, "\n#{content}\n" + old_content.sub %r{(?<=#{ANNOTATE_DELIMITER}\n)(.|\n)*?(?=\n#{ANNOTATE_DELIMITER})}, "\n#{content}" else # find first spot after comments # add and fill annotation - old_content.sub %r{^[^#]}, "\n# #{ANNOTATE_DELIMITER}\n#{content}\n# #{ANNOTATE_DELIMITER}\n\n" + old_content.sub %r{^[^#]}, "\n#{ANNOTATE_DELIMITER}\n\n#{content}\n#{ANNOTATE_DELIMITER}\n\n" end File.write file, new_content end end end + +class GRPC::RpcDesc::Stream + + def to_s + "stream #{type}" + end +end + +monkey_patch_descriptor = Module.new do + def each_oneof(&) + return super if block_given? + + Enumerator.new do |y| + super do |d| + y << d + end + end + end +end +Google::Protobuf::Descriptor.prepend monkey_patch_descriptor