Skip to main content

pacaptr/pm/
emerge.rs

1#![doc = doc_self!()]
2
3use std::sync::LazyLock;
4
5use async_trait::async_trait;
6use indoc::indoc;
7use itertools::Itertools;
8
9use super::{NoCacheStrategy, Pm, PmHelper, PromptStrategy, Strategy};
10use crate::{config::Config, error::Result, exec::Cmd};
11
12macro_rules! doc_self {
13    () => {
14        indoc! {"
15            The [Portage Package Manager](https://wiki.gentoo.org/wiki/Portage).
16        "}
17    };
18}
19use doc_self;
20
21#[doc = doc_self!()]
22#[derive(Debug)]
23pub struct Emerge {
24    cfg: Config,
25}
26
27static STRAT_ASK: LazyLock<Strategy> = LazyLock::new(|| Strategy {
28    prompt: PromptStrategy::native_confirm(["--ask"]),
29    ..Strategy::default()
30});
31
32static STRAT_INTERACTIVE: LazyLock<Strategy> = LazyLock::new(|| Strategy {
33    prompt: PromptStrategy::native_confirm(["--interactive"]),
34    ..Strategy::default()
35});
36
37static STRAT_INSTALL: LazyLock<Strategy> = LazyLock::new(|| Strategy {
38    prompt: PromptStrategy::native_confirm(["--ask"]),
39    no_cache: NoCacheStrategy::Scc,
40    ..Strategy::default()
41});
42
43impl Emerge {
44    #[must_use]
45    #[allow(missing_docs)]
46    pub const fn new(cfg: Config) -> Self {
47        Self { cfg }
48    }
49}
50
51#[async_trait]
52impl Pm for Emerge {
53    /// Gets the name of the package manager.
54    fn name(&self) -> &'static str {
55        "emerge"
56    }
57
58    fn cfg(&self) -> &Config {
59        &self.cfg
60    }
61
62    /// Q generates a list of installed packages.
63    async fn q(&self, kws: &[&str], flags: &[&str]) -> Result<()> {
64        self.qs(kws, flags).await
65    }
66
67    /// Qi displays local package information: name, version, description, etc.
68    async fn qi(&self, kws: &[&str], flags: &[&str]) -> Result<()> {
69        self.si(kws, flags).await
70    }
71
72    /// Ql displays files provided by local package.
73    async fn ql(&self, kws: &[&str], flags: &[&str]) -> Result<()> {
74        self.run(Cmd::new(["qlist"]).kws(kws).flags(flags)).await
75    }
76
77    /// Qo queries the package which provides FILE.
78    async fn qo(&self, kws: &[&str], flags: &[&str]) -> Result<()> {
79        self.run(Cmd::new(["qfile"]).kws(kws).flags(flags)).await
80    }
81
82    /// Qs searches locally installed package for names or descriptions.
83    // According to https://www.archlinux.org/pacman/pacman.8.html#_query_options_apply_to_em_q_em_a_id_qo_a,
84    // when including multiple search terms, only packages with descriptions
85    // matching ALL of those terms are returned.
86    async fn qs(&self, kws: &[&str], flags: &[&str]) -> Result<()> {
87        self.run(Cmd::new(["qlist", "-I"]).kws(kws).flags(flags))
88            .await
89    }
90
91    /// Qu lists packages which have an update available.
92    async fn qu(&self, _kws: &[&str], flags: &[&str]) -> Result<()> {
93        self.run(Cmd::new(["emerge", "-uDNp", "@world"]).flags(flags))
94            .await
95    }
96
97    /// R removes a single package, leaving all of its dependencies installed.
98    async fn r(&self, kws: &[&str], flags: &[&str]) -> Result<()> {
99        self.run_with(
100            Cmd::with_sudo(["emerge", "--unmerge"])
101                .kws(kws)
102                .flags(flags),
103            self.default_mode(),
104            &STRAT_ASK,
105        )
106        .await
107    }
108
109    /// Rs removes a package and its dependencies which are not required by any
110    /// other installed package, and not explicitly installed by the user.
111    async fn rs(&self, kws: &[&str], flags: &[&str]) -> Result<()> {
112        self.run_with(
113            Cmd::with_sudo(["emerge", "--depclean"])
114                .kws(kws)
115                .flags(flags),
116            self.default_mode(),
117            &STRAT_ASK,
118        )
119        .await
120    }
121
122    /// S installs one or more packages by name.
123    async fn s(&self, kws: &[&str], flags: &[&str]) -> Result<()> {
124        self.run_with(
125            Cmd::with_sudo(["emerge"]).kws(kws).flags(flags),
126            self.default_mode(),
127            &STRAT_INSTALL,
128        )
129        .await
130    }
131
132    /// Sc removes all the cached packages that are not currently installed, and
133    /// the unused sync database.
134    async fn sc(&self, kws: &[&str], flags: &[&str]) -> Result<()> {
135        self.run_with(
136            Cmd::with_sudo(["eclean-dist"]).kws(kws).flags(flags),
137            self.default_mode(),
138            &STRAT_INTERACTIVE,
139        )
140        .await
141    }
142
143    /// Scc removes all files from the cache.
144    async fn scc(&self, kws: &[&str], flags: &[&str]) -> Result<()> {
145        self.sc(kws, flags).await
146    }
147
148    /// Si displays remote package information: name, version, description, etc.
149    async fn si(&self, kws: &[&str], flags: &[&str]) -> Result<()> {
150        let kws = kws.iter().map(|kw| format!("^{kw}$")).collect_vec();
151        self.run(Cmd::new(["emerge", "-s"]).kws(kws).flags(flags))
152            .await
153    }
154
155    /// Ss searches for package(s) by searching the expression in name,
156    /// description, short description.
157    async fn ss(&self, kws: &[&str], flags: &[&str]) -> Result<()> {
158        self.run(Cmd::new(["qsearch"]).kws(kws).flags(flags)).await
159    }
160
161    /// Su updates outdated packages.
162    async fn su(&self, kws: &[&str], flags: &[&str]) -> Result<()> {
163        self.run_with(
164            Cmd::with_sudo(["emerge", "-uDN"])
165                .kws(if kws.is_empty() { &["@world"][..] } else { kws })
166                .flags(flags),
167            self.default_mode(),
168            &STRAT_INSTALL,
169        )
170        .await
171    }
172
173    /// Suy refreshes the local package database, then updates outdated
174    /// packages.
175    async fn suy(&self, kws: &[&str], flags: &[&str]) -> Result<()> {
176        self.sy(&[], flags).await?;
177        self.su(kws, flags).await
178    }
179
180    /// Sy refreshes the local package database.
181    async fn sy(&self, kws: &[&str], flags: &[&str]) -> Result<()> {
182        self.run(Cmd::with_sudo(["emerge", "--sync"]).flags(flags))
183            .await?;
184        if !kws.is_empty() {
185            self.s(kws, flags).await?;
186        }
187        Ok(())
188    }
189}