Skip to main content

tocat_api/
error.rs

1//! Errors crossing the plugin boundary.
2//!
3//! Deliberately string-based rather than a rich enum: a WASM guest can only
4//! hand back bytes, so anything richer would be lossy on one side of the
5//! boundary and misleading on the other.
6
7use std::fmt;
8
9pub type Result<T, E = PluginError> = std::result::Result<T, E>;
10
11#[derive(Debug, Clone, PartialEq, Eq)]
12pub enum PluginError {
13    /// No plugin with this name is registered.
14    Unknown {
15        name: String,
16        available: Vec<String>,
17    },
18    /// The plugin rejected its configuration.
19    Config { plugin: String, message: String },
20    /// The plugin failed while processing bytes.
21    Runtime { plugin: String, message: String },
22    /// The host could not satisfy a request made by a plugin.
23    Host(String),
24}
25
26impl PluginError {
27    /// Build an unknown plugin error
28    ///
29    /// Carries the registry's contents, because "is that feature enabled?" and
30    /// "did I put an endpoint in a plugin slot?" are the two things anyone
31    /// actually wants to know here.
32    pub fn unknown<S: Into<String>>(
33        name: impl Into<String>,
34        available: impl IntoIterator<Item = S>,
35    ) -> Self {
36        Self::Unknown {
37            name: name.into(),
38            available: available.into_iter().map(Into::into).collect(),
39        }
40    }
41
42    /// Build an invalid config error
43    pub fn config(plugin: impl Into<String>, message: impl fmt::Display) -> Self {
44        Self::Config {
45            plugin: plugin.into(),
46            message: message.to_string(),
47        }
48    }
49
50    /// Build a plugin runtime error
51    pub fn runtime(plugin: impl Into<String>, message: impl fmt::Display) -> Self {
52        Self::Runtime {
53            plugin: plugin.into(),
54            message: message.to_string(),
55        }
56    }
57
58    /// Build a host request failure error
59    pub fn host(message: impl fmt::Display) -> Self {
60        Self::Host(message.to_string())
61    }
62
63    #[must_use]
64    pub fn plugin(&self) -> Option<&str> {
65        match self {
66            Self::Unknown { name, .. } => Some(name),
67            Self::Config { plugin, .. } | Self::Runtime { plugin, .. } => Some(plugin),
68            Self::Host(_) => None,
69        }
70    }
71}
72
73impl fmt::Display for PluginError {
74    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
75        match self {
76            Self::Unknown { name, available } => {
77                write!(f, "unknown plugin `{name}`")?;
78
79                if !available.is_empty() {
80                    write!(f, " (compiled in: {})", available.join(", "))?;
81                }
82
83                Ok(())
84            }
85            Self::Config { plugin, message } => {
86                write!(f, "invalid configuration for plugin `{plugin}`: {message}")
87            }
88            Self::Runtime { plugin, message } => write!(f, "plugin `{plugin}` failed: {message}"),
89            Self::Host(message) => write!(f, "host error: {message}"),
90        }
91    }
92}
93
94impl std::error::Error for PluginError {}