diff --git a/lib/spoom/cli/srb.rb b/lib/spoom/cli/srb.rb index 9c1f85a5..4767f390 100644 --- a/lib/spoom/cli/srb.rb +++ b/lib/spoom/cli/srb.rb @@ -4,6 +4,7 @@ require_relative "srb/bump" require_relative "srb/coverage" require_relative "srb/lsp" +require_relative "srb/sigs" require_relative "srb/tc" module Spoom @@ -19,6 +20,9 @@ class Main < Thor desc "bump", "Change Sorbet sigils from one strictness to another when no errors" subcommand "bump", Spoom::Cli::Srb::Bump + desc "sigs", "Translate signatures from/to RBI and RBS" + subcommand "sigs", Spoom::Cli::Srb::Sigs + desc "tc", "Run typechecking with advanced options" subcommand "tc", Spoom::Cli::Srb::Tc end diff --git a/lib/spoom/cli/srb/sigs.rb b/lib/spoom/cli/srb/sigs.rb new file mode 100644 index 00000000..82ece93a --- /dev/null +++ b/lib/spoom/cli/srb/sigs.rb @@ -0,0 +1,49 @@ +# typed: true +# frozen_string_literal: true + +require "spoom/sorbet/translate_sigs" + +module Spoom + module Cli + module Srb + class Sigs < Thor + include Helper + + desc "translate", "Translate signatures from/to RBI and RBS" + option :from, type: :string, aliases: :f, desc: "From format", enum: ["rbi"], default: "rbi" + option :to, type: :string, aliases: :t, desc: "To format", enum: ["rbs"], default: "rbs" + def translate(*paths) + from = options[:from] + to = options[:to] + paths << "." if paths.empty? + + files = paths.flat_map do |path| + if File.file?(path) + [path] + else + Dir.glob("#{path}/**/*.rb") + end + end + + if files.empty? + say_error("No files to translate") + exit(1) + end + + say("Translating signatures from `#{from}` to `#{to}` in `#{files.size}` files...\n\n") + + files.each do |file| + contents = File.read(file) + contents = Spoom::Sorbet::TranslateSigs.rbi_to_rbs(contents) + File.write(file, contents) + rescue RBI::ParseError => error + say_warning("Can't parse #{file}: #{error.message}") + next + end + + say("Translated signatures in `#{files.size}` files.") + end + end + end + end +end diff --git a/test/spoom/cli/srb/sigs_test.rb b/test/spoom/cli/srb/sigs_test.rb new file mode 100644 index 00000000..5131be48 --- /dev/null +++ b/test/spoom/cli/srb/sigs_test.rb @@ -0,0 +1,85 @@ +# typed: true +# frozen_string_literal: true + +require "test_with_project" + +module Spoom + module Cli + module Srb + class SigsTest < TestWithProject + def setup + @project.bundle_install! + end + + def test_only_supports_translation_from_rbi + result = @project.spoom("srb sigs translate --from rbs") + + assert_equal(<<~ERR, result.err) + Expected '--from' to be one of rbi; got rbs + ERR + refute(result.status) + end + + def test_only_supports_translation_to_rbs + result = @project.spoom("srb sigs translate --to rbi") + + assert_equal(<<~ERR, result.err) + Expected '--to' to be one of rbs; got rbi + ERR + refute(result.status) + end + + def test_no_files + result = @project.spoom("srb sigs translate --no-color") + + assert_equal(<<~OUT, result.err) + Error: No files to translate + OUT + refute(result.status) + end + + def test_only_selected_files + @project.write!("a/file1.rb", <<~RB) + sig { void } + def foo; end + RB + + @project.write!("a/file2.rb", <<~RB) + sig { void } + def foo; end + RB + + @project.write!("b/file1.rb", <<~RB) + sig { void } + def foo; end + RB + + result = @project.spoom("srb sigs translate --no-color a/file1.rb b/") + + assert_empty(result.err) + assert_equal(<<~OUT, result.out) + Translating signatures from `rbi` to `rbs` in `2` files... + + Translated signatures in `2` files. + OUT + assert(result.status) + + assert_equal(<<~RB, @project.read("a/file1.rb")) + #: -> void + def foo; end + RB + + assert_equal(<<~RB, @project.read("a/file2.rb")) + sig { void } + def foo; end + RB + + assert_equal(<<~RB, @project.read("b/file1.rb")) + #: -> void + def foo; end + RB + end + end + end + end +end