require ‘shellwords’ require ‘bundler/setup’

namespace :protip do

desc 'compile a single .proto file to Ruby'
task :compile, [:filename, :proto_path, :ruby_path, :rbi_path, :protoc_gen_rbi_path] do |t, args|
  proto_path = [args[:proto_path] || 'definitions'].flatten.compact.reject{|path| path == ''}
  proto_path << File.join(Gem.loaded_specs['protip'].full_gem_path, 'definitions')

  ruby_path = args[:ruby_path] || 'lib'

  filename = args[:filename] || raise(ArgumentError.new 'filename argument is required')

  command = "bundle exec grpc_tools_ruby_protoc #{proto_path.map{|p| "--proto_path=#{Shellwords.escape p}"}.join ' '} --ruby_out=#{Shellwords.escape ruby_path}"
  command << " --plugin=protoc-gen-rbi=#{args[:protoc_gen_rbi_path]}" if args[:protoc_gen_rbi_path]
  command << " --rbi_out=#{args[:rbi_path]}" if args[:rbi_path]
  command << " #{Shellwords.escape filename}"
  puts command
  system command

  ## ridiculous hack around missing options in Ruby, remove when https://github.com/google/protobuf/issues/1198 is resolved
  package_match = File.read(filename).match(/package "?([a-zA-Z0-9\.]+)"?;/)
  package = (package_match ? package_match[1] : nil)
  ruby_file = filename.gsub(/^#{proto_path.first}\/?/, "#{ruby_path}/").gsub(/\.proto$/, '_pb.rb') # Relies on a relative filename and proto path, which protoc requires anyway at this point
  raise "cannot find generated Ruby file (#{ruby_file})" unless File.exists?(ruby_file)

  # Push/pop message names as we move through the protobuf file
  message_name_stack = []
  first_match = true
  File.open filename, 'r' do |f|
    f.each_line do |line|
      if line.include? '{'
        match = line.match(/message\s([a-zA-Z0-9]+)/)
        message_name_stack << (match ? match[1] : nil)
      end

      # figure out the field name and enum name if this line is like
      # +protip.messages.EnumValue value = 3 [(protip_enum) = "Foo"]+
      match = line.match /\s*[a-zA-Z0-9\.]+\s+([a-zA-Z0-9_]+).+\[\s*\(\s*protip_enum\s*\)\s*=\s*"([a-zA-Z0-9\.]+)"\s*\]/
      if match
        message_name = "#{package ? "#{package}." : ''}#{message_name_stack.compact.join('.')}"
        field_name = match[1]
        enum_name = match[2]
        File.open ruby_file, 'a' do |f|
          if first_match
            f.puts <<-RBY

# – Protip hack until github.com/google/protobuf/issues/1198 is resolved RBY

end
first_match = false
f.puts <<-RBY

Google::Protobuf::DescriptorPool.generated_pool.lookup(“#{message_name}”).lookup(“#{field_name}”).instance_variable_set(:“@_protip_enum_value”, “#{enum_name}”) RBY

        end
      end

      if line.include? '}'
        message_name_stack.pop
      end
    end
  end

end

end