11#! /usr/bin/env bash
22
3+ # @todo rename to `brew-query` and add support for `--installed` vs `--available`, as well as --search=...<fuzzy-match>
4+ # @todo add tests
5+
36function brew_installed_test() (
47 source " $DOROTHY /sources/bash.bash"
58 eval-tester --ignore-stderr --status=22 -- brew-installed --help
@@ -23,19 +26,50 @@ function brew_installed() (
2326 -- ...<item>
2427 If provided, only get details for these <item>s. Fail if one of them has not yet been installed.
2528
26- --[no-]formula
27- Output consider formulas. Formulas can be installed by request, or by dependency; use <requested> to filter.
28- --[no-]cask
29- Output consider casks. All casks are only installed by request.
30- --[no-]tap
31- Output consider taps. All taps are only installed by request.
32-
29+ --type=
30+ Output only formulas and casks.
31+ ! The default behaviour.
32+ --formula | --type=formula
33+ Output only formulas. Formulas can be installed by request, or by dependency; use <requested> to filter.
34+ --cask | --type=cask
35+ Output only casks. All casks are only installed by request.
36+ --tap | --type=tap
37+ Output only taps. All taps are only installed by request.
38+
39+ --expects[=<integer>] | --[no-]expects
40+ If provided, expect this amount of results otherwise [91] will be returned.
41+ ! Defaults to the amount of <item>s specified.
3342 --[no-]quiet | --[no-]verbose
34- If <quiet>, do not output anything, just return the exit status.
35-
36- --[no-]requested
37- Only consider formulas that were manually installed, not formulas that were only dependencies.
38- Only compatible with --formula.
43+ If <quiet>, do not output anything.
44+
45+ The following are only compatible with <cask> and <formula>:
46+
47+ --type=
48+ Output packages, regardless of outdated/updated status.
49+ ! The default behaviour.
50+ --[no-]outdated | --status=outdated
51+ Only output outdated packages.
52+ ! If enabled, will default <versions> to \` latest\` .
53+ --[no-]updated | --status=updated
54+ Only output updated packages.
55+ ! If enabled, will default <versions> to \` latest\` .
56+
57+ --[no-]versions | --versions=latest | --versions=current
58+ Output versions alongside the package name.
59+ If \` latest\` , format will be:
60+ \`\`\`
61+ <name> <version> <latest-version>
62+ \`\`\`
63+ If \` current\` , format will be:
64+ \`\`\`
65+ <name> <version>
66+ \`\`\`
67+ ! Note that <version> may not equal <latest-version> and it can still be considered updated, this is because the cask will auto-updates enabled. See <https://github.com/Homebrew/brew/issues/22853>.
68+
69+ The following are only compatible with <formula>:
70+
71+ --[no-]requested | --[no-]explicit
72+ Output only packages that were manually/explicitly installed, not packages that were only dependencies.
3973
4074 QUIRKS:
4175 If <item>s are provided, failure exit status will be returned if any are missing.
@@ -45,17 +79,31 @@ function brew_installed() (
4579 }
4680
4781 # process
48- local item option_quiet=' ' option_requested= ' no ' option_items=() option_type= ' ' # empty, formula, cask, tap
82+ local item option_quiet=' ' option_expects= ' ' option_type= ' ' option_status= ' no ' option_versions= ' ' option_requested= ' no ' option_items=()
4983 while [[ $# -ne 0 ]]; do
5084 item=" $1 "
5185 shift
5286 case " $item " in
5387 ' --help' | ' -h' ) help ;;
88+ # quiet
5489 ' --no-verbose' * | ' --verbose' * ) __flag --source={item} --target={option_quiet} --non-affirmative --coerce ;;
5590 ' --no-quiet' * | ' --quiet' * ) __flag --source={item} --target={option_quiet} --affirmative --coerce ;;
56- ' --type=formula' | ' --type=formulas' | ' --formula' | ' --formulae' ) option_type=formula ;;
57- ' --type=cask' | ' --type=casks' | ' --cask' | ' --casks' ) option_type=cask ;;
58- ' --type=tap' | ' --type=taps' | ' --tap' | ' --taps' ) option_type=tap ;;
91+ # type filter
92+ ' --type=cask' | ' --type=formula' | ' --type=tap' | ' --type=' ) option_type=" ${item#* =} " ;;
93+ ' --no-cask' * | ' --cask' * ) __flag --source={item} --target={option_type} --yes=' cask' --no=' ' --affirmative --coerce ;;
94+ ' --no-formula' * | ' --formula' * ) __flag --source={item} --target={option_type} --yes=' formula' --no=' ' --affirmative --coerce ;;
95+ ' --no-tap' * | ' --tap' * ) __flag --source={item} --target={option_type} --yes=' tap' --no=' ' --affirmative --coerce ;;
96+ # status filter
97+ ' --status=updated' | ' --status=outdated' | ' --status=' ) option_status=" ${item#* =} " ;;
98+ ' --no-updated' * | ' --updated' * ) __flag --source={item} --target={option_status} --yes=' updated' --affirmative --coerce ;;
99+ ' --no-outdated' * | ' --outdated' * ) __flag --source={item} --target={option_status} --yes=' outdated' --affirmative --coerce ;;
100+ # expects
101+ ' --no-expects' * | ' --expects' * ) __flag --source={item} --target={option_expects} --affirmative --no-coerce ;;
102+ # versions
103+ ' --versions=latest' ) option_versions=' latest' ;;
104+ ' --versions=current' ) option_versions=' current' ;;
105+ ' --no-versions' * | ' --versions' * ) __flag --source={item} --target={option_versions} --affirmative --coerce ;;
106+ # requested
59107 ' --no-requested' * | ' --requested' * ) __flag --source={item} --target={option_requested} --affirmative --coerce ;;
60108 ' --' )
61109 option_items+=(" $@ " )
@@ -72,64 +120,95 @@ function brew_installed() (
72120 return 46 # EPFNOSUPPORT 46 Protocol family not supported
73121 fi
74122
75- # =====================================
76- # Action
77-
78- local deno_script_args=()
79- if [[ $option_requested == ' yes' ]]; then
80- if [[ $option_type != ' formula' ]]; then
81- help --help=' <requested> requires <formula>'
123+ # adjust
124+ if [[ -z $option_versions ]]; then
125+ if [[ $option_status != ' no' ]]; then
126+ option_versions=' latest'
127+ else
128+ option_versions=' no'
129+ fi
130+ elif [[ $option_versions == ' yes' ]]; then
131+ if [[ $option_status != ' no' ]]; then
132+ option_versions=' latest'
133+ else
134+ option_versions=' current'
82135 fi
83- deno_script_args+=(' --requested' )
84136 fi
85137
86- local brew_list_cmd=() brew_info_cmd=()
87- if [[ $option_type == ' tap' ]]; then
88- brew_info_cmd=(brew tap-info --json)
89- deno_script_args+=(--type=tap)
90- else
91- brew_list_cmd=(brew list --versions)
92- # --versions is necessary to limit to installed option_packages, as --full-name/-1 doesn't
93- brew_info_cmd=(brew info --json=v2)
94- if [[ -n $option_type ]]; then
95- brew_list_cmd+=(" --$option_type " )
96- brew_info_cmd+=(" --$option_type " )
97- deno_script_args+=(" --type=$option_type " )
98- fi
99- # if no items, only get the installed ones
100- if [[ ${# option_items[@]} -eq 0 ]]; then
101- brew_info_cmd+=(' --installed' )
138+ # verify
139+ if [[ $option_requested == ' yes' && $option_type != ' formula' ]]; then
140+ help --help=' <requested> requires <formula>'
141+ fi
142+
143+ # expects
144+ if [[ -z $option_expects ]]; then
145+ if [[ ${# option_items[@]} -ne 0 ]]; then
146+ option_expects=${# option_items[@]}
102147 fi
148+ elif [[ $option_expects == ' yes' ]]; then
149+ option_expects=${# option_items[@]}
150+ elif [[ $option_expects == ' no' ]]; then
151+ option_expects=' '
152+ else
153+ __affirm_value_is_positive_integer " $option_expects " ' <expects>' || return $?
103154 fi
104155
105- function do_brew_simple {
106- # handles only --cask and --formula, but not --requested
107- if [[ $option_quiet == ' yes' ]]; then
108- " ${brew_list_cmd[@]} " " ${option_items[@]} " & > /dev/null
109- else
110- " ${brew_list_cmd[@]} " " ${option_items[@]} " | cut -d' ' -f1 | echo-unique --stdin
156+ # =====================================
157+ # Action
158+
159+ # to debug, trim `2>/dev/null`
160+
161+ function __parse_and_validate_with_deno {
162+ local deno_script
163+ deno_script=" $( type -P ' brew-installed.ts' ) " || return $?
164+ " $deno_script " --quiet=" $option_quiet " --type=" $option_type " --status=" $option_status " --expects=" $option_expects " --versions=" $option_versions " --requested=" $option_requested " 2> /dev/null || return $?
165+ }
166+ function __validate_ourselves {
167+ local REPLY=' '
168+ __read_whole || return $?
169+ if [[ $option_quiet != ' yes' ]]; then
170+ printf ' %s' " $REPLY " || return $?
171+ fi
172+ if [[ -n $option_expects ]]; then
173+ local results=()
174+ __split --source={REPLY} --target={results} --no-zero-length || return $?
175+ local -i received=${# results[@]}
176+ if [[ $option_expects -ne $received ]]; then
177+ return 91 # ENOMSG 91 No message of desired type
178+ fi
111179 fi
112180 }
113181
114- function do_brew_advanced {
115- # handles --requested, --cask, and --formula
182+ if [[ $option_type == ' tap' ]]; then
116183 __command_required -- ' deno' || return $?
117- local deno_script
118- deno_script=" $( type -P ' brew-installed.ts' ) "
119- if [[ $option_quiet == ' yes' ]]; then
120- " ${brew_info_cmd[@]} " " ${option_items[@]} " | " $deno_script " " ${deno_script_args[@]} " & > /dev/null
184+ brew tap-info --json 2> /dev/null | __parse_and_validate_with_deno || return $?
185+ else
186+ local brew_options=()
187+ # add type option
188+ case " $option_type " in
189+ ' cask' | ' formula' ) brew_options+=(" --$option_type " ) ;;
190+ esac
191+ # add requested option
192+ if [[ $option_status == ' no' && $option_versions != ' latest' && ! ($option_versions == ' current' && $option_requested == ' yes' ) ]]; then
193+ if [[ $option_versions == ' current' ]]; then
194+ brew_options+=(--versions)
195+ else
196+ if [[ $option_requested == ' yes' ]]; then
197+ # incompatible with --versions
198+ brew_options+=(--installed-on-request)
199+ fi
200+ brew_options+=(--full-name -1)
201+ fi
202+ # `brew list --cask "${option_items[@]}"` is a different format, but with `--full-names` and/or `-1` it is fine
203+ brew list " ${brew_options[@]} " " ${option_items[@]} " 2> /dev/null | __validate_ourselves || return $?
121204 else
122- " ${brew_info_cmd[@]} " " ${option_items[@]} " | " $deno_script " " ${deno_script_args[@]} "
205+ __command_required -- ' deno' || return $?
206+ # if no items, restrict to installed (rather than available casks); with items, --installed can't be used as otherwise it discards the items
207+ if [[ ${# option_items[@]} -eq 0 ]]; then
208+ brew_options+=(--installed)
209+ fi
210+ brew info --json=v2 " ${brew_options[@]} " " ${option_items[@]} " 2> /dev/null | __parse_and_validate_with_deno || return $?
123211 fi
124- }
125-
126- # get names of requested packages
127- if [[ $option_requested == ' no' && ${# brew_list_cmd[@]} -ne 0 ]]; then
128- do_brew_simple
129- elif [[ ${# option_items[@]} -eq 0 ]]; then
130- do_brew_advanced
131- elif [[ " $( do_brew_advanced | echo-count-lines --no-inline --stdin) " -ne ${# option_items[@]} ]]; then
132- return 1
133212 fi
134213)
135214
0 commit comments