diff --git a/Library/Homebrew/cmd/search.rb b/Library/Homebrew/cmd/search.rb index ae7908f9d3..ccdefd308d 100644 --- a/Library/Homebrew/cmd/search.rb +++ b/Library/Homebrew/cmd/search.rb @@ -1,165 +1,165 @@ # typed: true # frozen_string_literal: true +require "abstract_command" require "formula" require "missing_formula" require "descriptions" -require "cli/parser" require "search" module Homebrew - module_function + module Cmd + class SearchCmd < AbstractCommand + PACKAGE_MANAGERS = { + repology: ->(query) { "https://repology.org/projects/?search=#{query}" }, + macports: ->(query) { "https://ports.macports.org/search/?q=#{query}" }, + fink: ->(query) { "https://pdb.finkproject.org/pdb/browse.php?summary=#{query}" }, + opensuse: ->(query) { "https://software.opensuse.org/search?q=#{query}" }, + fedora: ->(query) { "https://packages.fedoraproject.org/search?query=#{query}" }, + archlinux: ->(query) { "https://archlinux.org/packages/?q=#{query}" }, + debian: lambda { |query| + "https://packages.debian.org/search?keywords=#{query}&searchon=names&suite=all§ion=all" + }, + ubuntu: lambda { |query| + "https://packages.ubuntu.com/search?keywords=#{query}&searchon=names&suite=all§ion=all" + }, + }.freeze - PACKAGE_MANAGERS = { - repology: ->(query) { "https://repology.org/projects/?search=#{query}" }, - macports: ->(query) { "https://ports.macports.org/search/?q=#{query}" }, - fink: ->(query) { "https://pdb.finkproject.org/pdb/browse.php?summary=#{query}" }, - opensuse: ->(query) { "https://software.opensuse.org/search?q=#{query}" }, - fedora: ->(query) { "https://packages.fedoraproject.org/search?query=#{query}" }, - archlinux: ->(query) { "https://archlinux.org/packages/?q=#{query}" }, - debian: lambda { |query| - "https://packages.debian.org/search?keywords=#{query}&searchon=names&suite=all§ion=all" - }, - ubuntu: lambda { |query| - "https://packages.ubuntu.com/search?keywords=#{query}&searchon=names&suite=all§ion=all" - }, - }.freeze + cmd_args do + description <<~EOS + Perform a substring search of cask tokens and formula names for . If + is flanked by slashes, it is interpreted as a regular expression. + EOS + switch "--formula", "--formulae", + description: "Search for formulae." + switch "--cask", "--casks", + description: "Search for casks." + switch "--desc", + description: "Search for formulae with a description matching and casks with " \ + "a name or description matching ." + switch "--eval-all", + depends_on: "--desc", + description: "Evaluate all available formulae and casks, whether installed or not, to search their " \ + "descriptions. Implied if `HOMEBREW_EVAL_ALL` is set." + switch "--pull-request", + description: "Search for GitHub pull requests containing ." + switch "--open", + depends_on: "--pull-request", + description: "Search for only open GitHub pull requests." + switch "--closed", + depends_on: "--pull-request", + description: "Search for only closed GitHub pull requests." + package_manager_switches = PACKAGE_MANAGERS.keys.map { |name| "--#{name}" } + package_manager_switches.each do |s| + switch s, + description: "Search for in the given database." + end - sig { returns(CLI::Parser) } - def search_args - Homebrew::CLI::Parser.new do - description <<~EOS - Perform a substring search of cask tokens and formula names for . If - is flanked by slashes, it is interpreted as a regular expression. - EOS - switch "--formula", "--formulae", - description: "Search for formulae." - switch "--cask", "--casks", - description: "Search for casks." - switch "--desc", - description: "Search for formulae with a description matching and casks with " \ - "a name or description matching ." - switch "--eval-all", - depends_on: "--desc", - description: "Evaluate all available formulae and casks, whether installed or not, to search their " \ - "descriptions. Implied if `HOMEBREW_EVAL_ALL` is set." - switch "--pull-request", - description: "Search for GitHub pull requests containing ." - switch "--open", - depends_on: "--pull-request", - description: "Search for only open GitHub pull requests." - switch "--closed", - depends_on: "--pull-request", - description: "Search for only closed GitHub pull requests." - package_manager_switches = PACKAGE_MANAGERS.keys.map { |name| "--#{name}" } - package_manager_switches.each do |s| - switch s, - description: "Search for in the given database." + conflicts "--desc", "--pull-request" + conflicts "--open", "--closed" + conflicts(*package_manager_switches) + + named_args :text_or_regex, min: 1 end - conflicts "--desc", "--pull-request" - conflicts "--open", "--closed" - conflicts(*package_manager_switches) + sig { override.void } + def run + return if search_package_manager - named_args :text_or_regex, min: 1 - end - end + query = args.named.join(" ") + string_or_regex = Search.query_regexp(query) - def search - args = search_args.parse + if args.desc? + if !args.eval_all? && !Homebrew::EnvConfig.eval_all? + raise UsageError, "`brew search --desc` needs `--eval-all` passed or `HOMEBREW_EVAL_ALL` set!" + end - return if search_package_manager(args) + Search.search_descriptions(string_or_regex, args) + elsif args.pull_request? + search_pull_requests(query) + else + formulae, casks = Search.search_names(string_or_regex, args) + print_results(formulae, casks, query) + end - query = args.named.join(" ") - string_or_regex = Search.query_regexp(query) + puts "Use `brew desc` to list packages with a short description." if args.verbose? - if args.desc? - if !args.eval_all? && !Homebrew::EnvConfig.eval_all? - raise UsageError, "`brew search --desc` needs `--eval-all` passed or `HOMEBREW_EVAL_ALL` set!" + print_regex_help end - Search.search_descriptions(string_or_regex, args) - elsif args.pull_request? - search_pull_requests(query, args) - else - formulae, casks = Search.search_names(string_or_regex, args) - print_results(formulae, casks, query) - end + private - puts "Use `brew desc` to list packages with a short description." if args.verbose? + def print_regex_help + return unless $stdout.tty? - print_regex_help(args) - end + metacharacters = %w[\\ | ( ) [ ] { } ^ $ * + ?].freeze + return unless metacharacters.any? do |char| + args.named.any? do |arg| + arg.include?(char) && !arg.start_with?("/") + end + end - def print_regex_help(args) - return unless $stdout.tty? + opoo <<~EOS + Did you mean to perform a regular expression search? + Surround your query with /slashes/ to search locally by regex. + EOS + end - metacharacters = %w[\\ | ( ) [ ] { } ^ $ * + ?].freeze - return unless metacharacters.any? do |char| - args.named.any? do |arg| - arg.include?(char) && !arg.start_with?("/") + def search_package_manager + package_manager = PACKAGE_MANAGERS.find { |name,| args[:"#{name}?"] } + return false if package_manager.nil? + + _, url = package_manager + exec_browser url.call(URI.encode_www_form_component(args.named.join(" "))) + true + end + + def search_pull_requests(query) + only = if args.open? && !args.closed? + "open" + elsif args.closed? && !args.open? + "closed" + end + + GitHub.print_pull_requests_matching(query, only) + end + + def print_results(all_formulae, all_casks, query) + count = all_formulae.size + all_casks.size + + if all_formulae.any? + if $stdout.tty? + ohai "Formulae", Formatter.columns(all_formulae) + else + puts all_formulae + end + end + puts if all_formulae.any? && all_casks.any? + if all_casks.any? + if $stdout.tty? + ohai "Casks", Formatter.columns(all_casks) + else + puts all_casks + end + end + + print_missing_formula_help(query, count.positive?) if all_casks.exclude?(query) + + odie "No formulae or casks found for #{query.inspect}." if count.zero? + end + + def print_missing_formula_help(query, found_matches) + return unless $stdout.tty? + + reason = MissingFormula.reason(query, silent: true) + return if reason.nil? + + if found_matches + puts + puts "If you meant #{query.inspect} specifically:" + end + puts reason end end - - opoo <<~EOS - Did you mean to perform a regular expression search? - Surround your query with /slashes/ to search locally by regex. - EOS - end - - def search_package_manager(args) - package_manager = PACKAGE_MANAGERS.find { |name,| args[:"#{name}?"] } - return false if package_manager.nil? - - _, url = package_manager - exec_browser url.call(URI.encode_www_form_component(args.named.join(" "))) - true - end - - def search_pull_requests(query, args) - only = if args.open? && !args.closed? - "open" - elsif args.closed? && !args.open? - "closed" - end - - GitHub.print_pull_requests_matching(query, only) - end - - def print_results(all_formulae, all_casks, query) - count = all_formulae.size + all_casks.size - - if all_formulae.any? - if $stdout.tty? - ohai "Formulae", Formatter.columns(all_formulae) - else - puts all_formulae - end - end - puts if all_formulae.any? && all_casks.any? - if all_casks.any? - if $stdout.tty? - ohai "Casks", Formatter.columns(all_casks) - else - puts all_casks - end - end - - print_missing_formula_help(query, count.positive?) if all_casks.exclude?(query) - - odie "No formulae or casks found for #{query.inspect}." if count.zero? - end - - def print_missing_formula_help(query, found_matches) - return unless $stdout.tty? - - reason = MissingFormula.reason(query, silent: true) - return if reason.nil? - - if found_matches - puts - puts "If you meant #{query.inspect} specifically:" - end - puts reason end end diff --git a/Library/Homebrew/test/cmd/search_spec.rb b/Library/Homebrew/test/cmd/search_spec.rb index 6d14ba8158..78adf8855c 100644 --- a/Library/Homebrew/test/cmd/search_spec.rb +++ b/Library/Homebrew/test/cmd/search_spec.rb @@ -3,7 +3,7 @@ require "cmd/search" require "cmd/shared_examples/args_parse" -RSpec.describe "brew search" do +RSpec.describe Homebrew::Cmd::SearchCmd do it_behaves_like "parseable arguments" it "finds formula in search", :integration_test do