diff --git a/lib/kangaru/input_parsers/argument_parser.rb b/lib/kangaru/input_parsers/argument_parser.rb new file mode 100644 index 0000000..e281144 --- /dev/null +++ b/lib/kangaru/input_parsers/argument_parser.rb @@ -0,0 +1,30 @@ +module Kangaru + module InputParsers + class ArgumentParser < InputParser + using Patches::Inflections + + def parse + grouped_argument_tokens.to_h do |tokens| + key = parse_key(tokens.shift || raise) + value = parse_value(tokens) + + [key, value] + end + end + + private + + def grouped_argument_tokens + argument_tokens.slice_before(ARGUMENT_TOKEN).to_a + end + + def parse_key(key) + key.gsub(/^-+/, "").to_snakecase.to_sym + end + + def parse_value(tokens) + tokens.empty? || tokens.join(" ") + end + end + end +end diff --git a/sig/kangaru/input_parsers/argument_parser.rbs b/sig/kangaru/input_parsers/argument_parser.rbs new file mode 100644 index 0000000..00da2cc --- /dev/null +++ b/sig/kangaru/input_parsers/argument_parser.rbs @@ -0,0 +1,15 @@ +module Kangaru + module InputParsers + class ArgumentParser < InputParser + def parse: -> Hash[Symbol, untyped] + + private + + def grouped_argument_tokens: -> Array[InputParser::tokens] + + def parse_key: (String) -> Symbol + + def parse_value: (Array[untyped]) -> untyped + end + end +end diff --git a/spec/kangaru/input_parsers/argument_parser_spec.rb b/spec/kangaru/input_parsers/argument_parser_spec.rb new file mode 100644 index 0000000..7a3473f --- /dev/null +++ b/spec/kangaru/input_parsers/argument_parser_spec.rb @@ -0,0 +1,111 @@ +RSpec.describe Kangaru::InputParsers::ArgumentParser do + subject(:argument_parser) { described_class.new(tokens) } + + let(:tokens) { [command_tokens, argument_tokens].flatten.compact } + + describe "#parse" do + subject(:arguments) { argument_parser.parse } + + shared_examples :parses_arguments_hash do |options| + let(:expected_hash) { options[:to] } + + it "returns a hash" do + expect(arguments).to be_a(Hash) + end + + it "has symbol keys" do + expect(arguments.keys).to all be_a(Symbol) + end + + it "is the expected hash" do + expect(arguments).to eq(expected_hash) + end + end + + context "when input does not contain command tokens" do + let(:command_tokens) { nil } + + context "and single unary argument is passed" do + context "and terse flag form given" do + let(:argument_tokens) { ["-f"] } + + include_examples :parses_arguments_hash, to: { f: true } + end + + context "and verbose flag form given" do + let(:argument_tokens) { ["--foo-bar"] } + + include_examples :parses_arguments_hash, to: { foo_bar: true } + end + end + + context "and single binary argument is passed" do + context "and terse flag form given" do + let(:argument_tokens) { %w[-t Hello world] } + + include_examples :parses_arguments_hash, to: { t: "Hello world" } + end + + context "and verbose flag form given" do + let(:argument_tokens) { %w[--full-title Hello world] } + + include_examples :parses_arguments_hash, + to: { full_title: "Hello world" } + end + end + + context "and multiple arguments are passed" do + let(:argument_tokens) do + %w[--title This is the title -d Foobar --cleanup -f] + end + + include_examples :parses_arguments_hash, to: { + title: "This is the title", d: "Foobar", cleanup: true, f: true + } + end + end + + context "when input contains command tokens" do + let(:command_tokens) { %w[foo bar baz] } + + context "and single unary argument is passed" do + context "and terse flag form given" do + let(:argument_tokens) { ["-f"] } + + include_examples :parses_arguments_hash, to: { f: true } + end + + context "and verbose flag form given" do + let(:argument_tokens) { ["--foo-bar"] } + + include_examples :parses_arguments_hash, to: { foo_bar: true } + end + end + + context "and single binary argument is passed" do + context "and terse flag form given" do + let(:argument_tokens) { %w[-t Hello world] } + + include_examples :parses_arguments_hash, to: { t: "Hello world" } + end + + context "and verbose flag form given" do + let(:argument_tokens) { %w[--full-title Hello world] } + + include_examples :parses_arguments_hash, + to: { full_title: "Hello world" } + end + end + + context "and multiple arguments are passed" do + let(:argument_tokens) do + %w[--title This is the title -d Foobar --cleanup -f] + end + + include_examples :parses_arguments_hash, to: { + title: "This is the title", d: "Foobar", cleanup: true, f: true + } + end + end + end +end