Skip to main content

pmpx_plugin/
error.rs

1//! The three errors a plugin can report.
2//!
3//! Deliberately only three: that is what the host needs to distinguish -- the verb is not
4//! supported (so it can degrade to passing through verbatim), the arguments are wrong, and
5//! everything else.
6//!
7//! **Only the code crosses the boundary.** The text stays here: it is for [rror!](crate::error)
8//! and for a plugin's own tests, and a host that shows something else is not contradicting this.
9//! (The ABI has no message channel; adding one would be a new capability, not a change to this
10//! type.)
11
12use std::fmt;
13
14use crate::Verb;
15
16/// The errors a plugin can report.
17/// Deliberately only three -- because the host only needs to distinguish three: the verb is not
18/// supported (so it can degrade to passing through verbatim), the arguments are wrong, and
19/// everything else. The human-readable description goes in the payload and the host prints it to
20/// stderr as-is.
21#[derive(Debug, Clone, PartialEq, Eq)]
22pub enum PluginError {
23    /// This backend does not support that verb. For `pmpx exec` the host degrades to passing
24    /// through verbatim.
25    UnsupportedVerb(Verb),
26
27    /// Invalid arguments.
28    InvalidArgs(String),
29
30    /// Anything else. Includes a plugin panic -- the host only needs to know "it blew up".
31    Other(String),
32}
33
34impl PluginError {
35    /// Construct "unsupported verb".
36    pub fn unsupported_verb(verb: Verb) -> Self {
37        PluginError::UnsupportedVerb(verb)
38    }
39
40    /// Construct "invalid arguments".
41    pub fn invalid_args(message: impl Into<String>) -> Self {
42        PluginError::InvalidArgs(message.into())
43    }
44
45    /// Construct some other error.
46    pub fn other(message: impl Into<String>) -> Self {
47        PluginError::Other(message.into())
48    }
49
50    /// The corresponding cross-boundary error code.
51    pub fn code(&self) -> u32 {
52        match self {
53            PluginError::UnsupportedVerb(_) => pmpx_plugin_abi::PMPX_ERR_UNSUPPORTED_VERB,
54            PluginError::InvalidArgs(_) => pmpx_plugin_abi::PMPX_ERR_INVALID_ARGS,
55            PluginError::Other(_) => pmpx_plugin_abi::PMPX_ERR_INTERNAL,
56        }
57    }
58
59    /// Human-readable description.
60    pub fn message(&self) -> String {
61        match self {
62            PluginError::UnsupportedVerb(v) => format!("unsupported verb {v}"),
63            PluginError::InvalidArgs(m) => m.clone(),
64            PluginError::Other(m) => m.clone(),
65        }
66    }
67}
68
69impl fmt::Display for PluginError {
70    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
71        f.write_str(&self.message())
72    }
73}
74
75// Hand-written rather than thiserror: this crate is deliberately dependency-free, and all `Error`
76// needs is the line below.
77impl std::error::Error for PluginError {}
78
79impl From<String> for PluginError {
80    fn from(message: String) -> Self {
81        PluginError::Other(message)
82    }
83}
84
85impl From<&str> for PluginError {
86    fn from(message: &str) -> Self {
87        PluginError::Other(message.to_string())
88    }
89}
90
91impl From<std::io::Error> for PluginError {
92    fn from(e: std::io::Error) -> Self {
93        PluginError::Other(e.to_string())
94    }
95}