diff --git a/Library/Homebrew/cli_parser.rb b/Library/Homebrew/cli_parser.rb index 5ce769343b..d92a2a4ff4 100644 --- a/Library/Homebrew/cli_parser.rb +++ b/Library/Homebrew/cli_parser.rb @@ -13,6 +13,8 @@ module Homebrew @parsed_args = OpenStruct.new # undefine tap to allow --tap argument @parsed_args.instance_eval { undef tap } + @depends = [] + @conflicts = [] instance_eval(&block) end @@ -47,6 +49,14 @@ module Homebrew end end + def depends(primary, secondary, mandatory: false) + @depends << [primary, secondary, mandatory] + end + + def conflicts(primary, secondary) + @conflicts << [primary, secondary] + end + def option_to_name(name) name.sub(/\A--?/, "").tr("-", "_") end @@ -57,6 +67,7 @@ module Homebrew def parse(cmdline_args = ARGV) @parser.parse(cmdline_args) + check_constraint_violations @parsed_args end @@ -82,6 +93,57 @@ module Homebrew else name end end + + def option_passed?(name) + @parsed_args.respond_to?(name) || @parsed_args.respond_to?("#{name}?") + end + + def check_depends + @depends.each do |primary, secondary, required| + primary_passed = option_passed?(primary) + secondary_passed = option_passed?(secondary) + raise OptionDependencyError.new(primary, secondary) if required && primary_passed && + !secondary_passed + raise OptionDependencyError.new(primary, secondary, missing: true) if secondary_passed && + !primary_passed + end + end + + def check_conflicts + @conflicts.each do |primary, secondary| + primary_passed = option_passed?(primary) + secondary_passed = option_passed?(secondary) + raise OptionConflictError.new(primary, secondary) if primary_passed && secondary_passed + end + end + + def check_constraint_violations + check_conflicts + check_depends + end + end + + class OptionDependencyError < RuntimeError + def initialize(arg1, arg2, missing: false) + if !missing + message = <<~EOS + `#{arg1}` and `#{arg2}` should be passed together + EOS + else + message = <<~EOS + `#{arg2}` cannot be passed without `#{arg1}` + EOS + end + super message + end + end + + class OptionConflictError < RuntimeError + def initialize(arg1, arg2) + super <<~EOS + `#{arg1}` and `#{arg2}` should not be passed together + EOS + end end end end diff --git a/Library/Homebrew/dev-cmd/bump-formula-pr.rb b/Library/Homebrew/dev-cmd/bump-formula-pr.rb index daaf10e01a..8a3abba080 100644 --- a/Library/Homebrew/dev-cmd/bump-formula-pr.rb +++ b/Library/Homebrew/dev-cmd/bump-formula-pr.rb @@ -57,6 +57,7 @@ module Homebrew switch "--no-browse" switch :quiet switch :force + switch :verbose switch :debug flag "--url", required: true flag "--sha256", required: true @@ -65,6 +66,9 @@ module Homebrew flag "--revision", required: true flag "--version", required: true flag "--message", required: true + depends :url, :sha256 + depends :tag, :revision, mandatory: true + conflicts :url, :tag end # As this command is simplifying user run commands then let's just use a diff --git a/Library/Homebrew/test/cli_parser_spec.rb b/Library/Homebrew/test/cli_parser_spec.rb index 5f65a80c15..82ab944363 100644 --- a/Library/Homebrew/test/cli_parser_spec.rb +++ b/Library/Homebrew/test/cli_parser_spec.rb @@ -70,4 +70,41 @@ describe Homebrew::CLI::Parser do expect(args.files).to eq %w[random1.txt random2.txt] end end + + describe "test constraints" do + subject(:parser) { + described_class.new do + flag "--flag1" + flag "--flag2" + flag "--flag3" + flag "--flag4" + depends :flag1, :flag2, mandatory: true + depends :flag3, :flag4 + conflicts :flag1, :flag3 + end + } + + it "raises exception on depends mandatory constraint violation" do + expect { parser.parse(["--flag1"]) }.to raise_error(Homebrew::CLI::OptionDependencyError) + end + + it "raises exception on depends constraint violation" do + expect { parser.parse(["--flag2"]) }.to raise_error(Homebrew::CLI::OptionDependencyError) + end + + it "raises exception for conflict violation" do + expect { parser.parse(["--flag1", "--flag3"]) }.to raise_error(Homebrew::CLI::OptionConflictError) + end + + it "raises no exception" do + args = parser.parse(["--flag1", "--flag2"]) + expect(args.flag1).to be true + expect(args.flag2).to be true + end + + it "raises no exception for optional dependency" do + args = parser.parse(["--flag3"]) + expect(args.flag3).to be true + end + end end