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}