1#![doc = doc_self!()]
2
3use std::sync::LazyLock;
4
5use async_trait::async_trait;
6use indoc::indoc;
7use tap::prelude::*;
8
9use super::{DryRunStrategy, NoCacheStrategy, Pm, PmHelper, PromptStrategy, Strategy};
10use crate::{config::Config, error::Result, exec::Cmd};
11
12macro_rules! doc_self {
13 () => {
14 indoc! {"
15 The [Homebrew Package Manager](https://brew.sh/).
16 "}
17 };
18}
19use doc_self;
20
21#[doc = doc_self!()]
22#[derive(Debug)]
23pub struct Brew {
24 cfg: Config,
25}
26
27static STRAT_PROMPT: LazyLock<Strategy> = LazyLock::new(|| Strategy {
28 prompt: PromptStrategy::CustomPrompt,
29 ..Strategy::default()
30});
31
32static STRAT_INSTALL: LazyLock<Strategy> = LazyLock::new(|| Strategy {
33 prompt: PromptStrategy::CustomPrompt,
34 no_cache: NoCacheStrategy::Scc,
35 ..Strategy::default()
36});
37
38impl Brew {
39 #[must_use]
40 #[allow(missing_docs)]
41 pub const fn new(cfg: Config) -> Self {
42 Self { cfg }
43 }
44}
45
46#[async_trait]
47impl Pm for Brew {
48 fn name(&self) -> &'static str {
50 "brew"
51 }
52
53 fn cfg(&self) -> &Config {
54 &self.cfg
55 }
56
57 async fn q(&self, kws: &[&str], flags: &[&str]) -> Result<()> {
59 if kws.is_empty() {
60 self.run(Cmd::new(["brew", "list"]).flags(flags)).await
61 } else {
62 self.qs(kws, flags).await
63 }
64 }
65
66 async fn qc(&self, kws: &[&str], flags: &[&str]) -> Result<()> {
68 self.run(Cmd::new(["brew", "log"]).kws(kws).flags(flags))
69 .await
70 }
71
72 async fn qi(&self, kws: &[&str], flags: &[&str]) -> Result<()> {
74 self.si(kws, flags).await
75 }
76
77 async fn qii(&self, kws: &[&str], flags: &[&str]) -> Result<()> {
80 Cmd::new(["brew", "uses", "--installed"])
81 .kws(kws)
82 .flags(flags)
83 .pipe(|cmd| self.run(cmd))
84 .await
85 }
86
87 async fn ql(&self, kws: &[&str], flags: &[&str]) -> Result<()> {
89 self.run(Cmd::new(["brew", "list"]).kws(kws).flags(flags))
93 .await
94 }
95
96 async fn qs(&self, kws: &[&str], flags: &[&str]) -> Result<()> {
101 self.search_regex(Cmd::new(["brew", "list", "--formula"]).flags(flags), kws)
103 .await?;
104 if cfg!(target_os = "macos") {
105 self.search_regex(Cmd::new(["brew", "list", "--cask"]).flags(flags), kws)
106 .await?;
107 }
108
109 Ok(())
110 }
111
112 async fn qu(&self, kws: &[&str], flags: &[&str]) -> Result<()> {
114 self.run(Cmd::new(["brew", "outdated"]).kws(kws).flags(flags))
115 .await
116 }
117
118 async fn r(&self, kws: &[&str], flags: &[&str]) -> Result<()> {
120 Cmd::new(["brew", "uninstall"])
121 .kws(kws)
122 .flags(flags)
123 .pipe(|cmd| self.run_with(cmd, self.default_mode(), &STRAT_PROMPT))
124 .await
125 }
126
127 async fn rn(&self, kws: &[&str], flags: &[&str]) -> Result<()> {
130 Cmd::new(["brew", "uninstall", "--zap", "-f"])
131 .kws(kws)
132 .flags(flags)
133 .pipe(|cmd| self.run_with(cmd, self.default_mode(), &STRAT_PROMPT))
134 .await
135 }
136
137 async fn rns(&self, kws: &[&str], flags: &[&str]) -> Result<()> {
141 self.rn(kws, flags).await?;
142 Cmd::new(["brew", "autoremove"])
143 .flags(flags)
144 .pipe(|cmd| self.run_with(cmd, self.default_mode(), &STRAT_PROMPT))
145 .await
146 }
147
148 async fn rs(&self, kws: &[&str], flags: &[&str]) -> Result<()> {
151 self.r(kws, flags).await?;
152 Cmd::new(["brew", "autoremove"])
153 .flags(flags)
154 .pipe(|cmd| self.run_with(cmd, self.default_mode(), &STRAT_PROMPT))
155 .await
156 }
157
158 async fn s(&self, kws: &[&str], flags: &[&str]) -> Result<()> {
160 Cmd::new(if self.cfg.needed {
161 ["brew", "install"]
162 } else {
163 ["brew", "reinstall"]
167 })
168 .kws(kws)
169 .flags(flags)
170 .pipe(|cmd| self.run_with(cmd, self.default_mode(), &STRAT_INSTALL))
171 .await
172 }
173
174 async fn sc(&self, kws: &[&str], flags: &[&str]) -> Result<()> {
177 let strat = Strategy {
178 dry_run: DryRunStrategy::with_flags(["--dry-run"]),
179 prompt: PromptStrategy::CustomPrompt,
180 ..Strategy::default()
181 };
182 Cmd::new(["brew", "cleanup"])
183 .kws(kws)
184 .flags(flags)
185 .pipe(|cmd| self.run_with(cmd, self.default_mode(), &strat))
186 .await
187 }
188
189 async fn scc(&self, kws: &[&str], flags: &[&str]) -> Result<()> {
191 let strat = Strategy {
192 dry_run: DryRunStrategy::with_flags(["--dry-run"]),
193 prompt: PromptStrategy::CustomPrompt,
194 ..Strategy::default()
195 };
196 Cmd::new(["brew", "cleanup", "-s"])
197 .kws(kws)
198 .flags(flags)
199 .pipe(|cmd| self.run_with(cmd, self.default_mode(), &strat))
200 .await
201 }
202
203 async fn sccc(&self, kws: &[&str], flags: &[&str]) -> Result<()> {
205 let strat = Strategy {
206 dry_run: DryRunStrategy::with_flags(["--dry-run"]),
207 prompt: PromptStrategy::CustomPrompt,
208 ..Strategy::default()
209 };
210 Cmd::new(["brew", "cleanup", "--prune=all"])
211 .kws(kws)
212 .flags(flags)
213 .pipe(|cmd| self.run_with(cmd, self.default_mode(), &strat))
214 .await
215 }
216
217 async fn si(&self, kws: &[&str], flags: &[&str]) -> Result<()> {
219 self.run(Cmd::new(["brew", "info"]).kws(kws).flags(flags))
220 .await
221 }
222
223 async fn sii(&self, kws: &[&str], flags: &[&str]) -> Result<()> {
226 Cmd::new(["brew", "uses", "--eval-all"])
227 .kws(kws)
228 .flags(flags)
229 .pipe(|cmd| self.run(cmd))
230 .await
231 }
232
233 async fn ss(&self, kws: &[&str], flags: &[&str]) -> Result<()> {
236 self.run(Cmd::new(["brew", "search"]).kws(kws).flags(flags))
237 .await
238 }
239
240 async fn su(&self, kws: &[&str], flags: &[&str]) -> Result<()> {
242 Cmd::new(["brew", "upgrade"])
243 .kws(kws)
244 .flags(flags)
245 .pipe(|cmd| self.run_with(cmd, self.default_mode(), &STRAT_INSTALL))
246 .await
247 }
248
249 async fn suy(&self, kws: &[&str], flags: &[&str]) -> Result<()> {
252 self.sy(&[], flags).await?;
253 self.su(kws, flags).await
254 }
255
256 async fn sw(&self, kws: &[&str], flags: &[&str]) -> Result<()> {
259 Cmd::new(["brew", "fetch"])
260 .kws(kws)
261 .flags(flags)
262 .pipe(|cmd| self.run_with(cmd, self.default_mode(), &STRAT_PROMPT))
263 .await
264 }
265
266 async fn sy(&self, kws: &[&str], flags: &[&str]) -> Result<()> {
268 self.run(Cmd::new(["brew", "update"]).flags(flags)).await?;
269 if !kws.is_empty() {
270 self.s(kws, flags).await?;
271 }
272 Ok(())
273 }
274}