Skip to main content

pmpx_plugin/
verb.rs

1//! The verbs a plugin is asked to translate.
2//!
3//! A closed set -- this is all the command line has -- and their numbering is part of the ABI, so it
4//! is pinned against the ABI crate rather than chosen here.
5
6use std::fmt;
7
8use pmpx_plugin_abi as abi;
9
10/// The verbs pmpx recognizes. A closed set -- this is all the command line has.
11///
12/// The numbers correspond one-to-one with the `PMPX_VERB_*` constants, and the order must not change:
13/// a different numbering is an ABI break, and the surface snapshot in the ABI crate records it.
14#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
15#[repr(u32)]
16pub enum Verb {
17    /// Install dependencies. No argument = install everything in the lockfile, with arguments =
18    /// add.
19    Install = abi::PMPX_VERB_INSTALL,
20    /// Remove dependencies.
21    Remove = abi::PMPX_VERB_REMOVE,
22    /// Run a script / target.
23    Run = abi::PMPX_VERB_RUN,
24    /// Build.
25    Build = abi::PMPX_VERB_BUILD,
26    /// Test.
27    Test = abi::PMPX_VERB_TEST,
28    /// Update dependencies.
29    Update = abi::PMPX_VERB_UPDATE,
30    /// Escape hatch: run an arbitrary command. Plugins that do not support it should report an
31    /// error explicitly, see [`PackageManager::command`](crate::PackageManager::command).
32    Exec = abi::PMPX_VERB_EXEC,
33}
34
35impl Verb {
36    /// All verbs, in numbering order.
37    pub const ALL: &'static [Verb] = &[
38        Verb::Install,
39        Verb::Remove,
40        Verb::Run,
41        Verb::Build,
42        Verb::Test,
43        Verb::Update,
44        Verb::Exec,
45    ];
46
47    /// Convert to the number used across the boundary.
48    pub const fn to_abi(self) -> u32 {
49        self as u32
50    }
51
52    /// Reconstruct from a cross-boundary number.
53    ///
54    /// `None` means "a verb this build does not know", and the shell answers that with
55    /// [`PMPX_ERR_UNSUPPORTED_VERB`](abi::PMPX_ERR_UNSUPPORTED_VERB) rather than "invalid
56    /// arguments": that is what keeps a *new* verb additive, because it leaves the host's
57    /// degradation path open for `exec`.
58    pub const fn from_abi(n: u32) -> Option<Verb> {
59        match n {
60            abi::PMPX_VERB_INSTALL => Some(Verb::Install),
61            abi::PMPX_VERB_REMOVE => Some(Verb::Remove),
62            abi::PMPX_VERB_RUN => Some(Verb::Run),
63            abi::PMPX_VERB_BUILD => Some(Verb::Build),
64            abi::PMPX_VERB_TEST => Some(Verb::Test),
65            abi::PMPX_VERB_UPDATE => Some(Verb::Update),
66            abi::PMPX_VERB_EXEC => Some(Verb::Exec),
67            _ => None,
68        }
69    }
70
71    /// The word written on the command line.
72    pub const fn as_str(self) -> &'static str {
73        match self {
74            Verb::Install => "install",
75            Verb::Remove => "remove",
76            Verb::Run => "run",
77            Verb::Build => "build",
78            Verb::Test => "test",
79            Verb::Update => "update",
80            Verb::Exec => "exec",
81        }
82    }
83}
84
85impl fmt::Display for Verb {
86    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
87        f.write_str(self.as_str())
88    }
89}
90
91#[cfg(test)]
92mod tests {
93    use super::*;
94
95    /// Every verb round-trips, and the numbering is the ABI's.
96    #[test]
97    fn every_verb_has_its_number() {
98        for (verb, number) in [
99            (Verb::Install, abi::PMPX_VERB_INSTALL),
100            (Verb::Remove, abi::PMPX_VERB_REMOVE),
101            (Verb::Run, abi::PMPX_VERB_RUN),
102            (Verb::Build, abi::PMPX_VERB_BUILD),
103            (Verb::Test, abi::PMPX_VERB_TEST),
104            (Verb::Update, abi::PMPX_VERB_UPDATE),
105            (Verb::Exec, abi::PMPX_VERB_EXEC),
106        ] {
107            assert_eq!(verb.to_abi(), number);
108            assert_eq!(Verb::from_abi(number), Some(verb));
109        }
110        assert_eq!(Verb::ALL.len(), 7, "the closed set has seven verbs");
111    }
112
113    /// A verb this build does not know is not an error here: the shell decides, and it says
114    /// "unsupported".
115    #[test]
116    fn an_unknown_number_has_no_verb() {
117        assert_eq!(Verb::from_abi(999), None);
118    }
119}