Skip to main content

audio_plugin_bsd/
error.rs

1//! Plugin-loader errors and the crate [`Result`] alias.
2
3use thiserror::Error;
4
5/// Errors raised while loading, ABI-verifying, or sandboxing an audio plugin.
6#[derive(Debug, Clone, PartialEq, Eq, Error)]
7pub enum PluginError {
8    /// The plugin's ABI version is incompatible with the host (major differs).
9    #[error("ABI version mismatch: host={host:#010x}, plugin={plugin:#010x}")]
10    AbiVersionMismatch {
11        /// The host's encoded ABI version word.
12        host: u32,
13        /// The plugin's encoded ABI version word.
14        plugin: u32,
15    },
16
17    /// A shared library could not be loaded (`dlopen` / `LoadLibrary` failed).
18    #[error("failed to load plugin library: {0}")]
19    LibraryLoad(String),
20
21    /// A required `extern "C"` symbol was not found in the plugin library.
22    #[error("required symbol missing from plugin: {0}")]
23    SymbolMissing(String),
24
25    /// A Capsicum / `pdfork` sandbox operation failed (FreeBSD only).
26    #[error("sandbox error: {0}")]
27    Sandbox(String),
28
29    /// The supplied plugin path was invalid (non-UTF-8, empty, not a file, ...).
30    #[error("invalid plugin path: {0}")]
31    InvalidPath(String),
32
33    /// An operation was attempted on a plugin that is not currently loaded.
34    #[error("plugin is not loaded")]
35    NotLoaded,
36
37    /// Plugin metadata failed validation (bad UTF-8, missing required fields, ...).
38    #[error("invalid plugin metadata: {0}")]
39    InvalidMetadata(String),
40}
41
42/// Convenience alias used by consumers of this crate.
43pub type Result<T> = core::result::Result<T, PluginError>;
44
45#[cfg(test)]
46mod tests {
47    use super::{PluginError, Result};
48
49    #[test]
50    fn abi_version_mismatch_display_includes_both_words() {
51        let msg = PluginError::AbiVersionMismatch {
52            host: 0x0001_0000,
53            plugin: 0x0002_0000,
54        }
55        .to_string();
56        assert_eq!(
57            msg,
58            "ABI version mismatch: host=0x00010000, plugin=0x00020000"
59        );
60    }
61
62    #[test]
63    fn library_load_display_includes_detail() {
64        let msg = PluginError::LibraryLoad("libfoo.so: undefined symbol".into()).to_string();
65        assert_eq!(
66            msg,
67            "failed to load plugin library: libfoo.so: undefined symbol"
68        );
69    }
70
71    #[test]
72    fn symbol_missing_display_includes_symbol_name() {
73        let msg = PluginError::SymbolMissing("audio_plugin_abi_magic".into()).to_string();
74        assert_eq!(
75            msg,
76            "required symbol missing from plugin: audio_plugin_abi_magic"
77        );
78    }
79
80    #[test]
81    fn sandbox_display_includes_detail() {
82        let msg = PluginError::Sandbox("cap_enter: not in capability mode".into()).to_string();
83        assert_eq!(msg, "sandbox error: cap_enter: not in capability mode");
84    }
85
86    #[test]
87    fn invalid_path_display_includes_path() {
88        let msg = PluginError::InvalidPath("/etc/plugin.so".into()).to_string();
89        assert_eq!(msg, "invalid plugin path: /etc/plugin.so");
90    }
91
92    #[test]
93    fn not_loaded_display_is_human_readable() {
94        let msg = PluginError::NotLoaded.to_string();
95        assert_eq!(msg, "plugin is not loaded");
96    }
97
98    #[test]
99    fn invalid_metadata_display_includes_detail() {
100        let msg = PluginError::InvalidMetadata("name is empty".into()).to_string();
101        assert_eq!(msg, "invalid plugin metadata: name is empty");
102    }
103
104    #[test]
105    fn result_alias_ok_carries_value() {
106        let ok: Result<u32> = Ok(42);
107        assert_eq!(ok.map(|v| v + 1), Ok(43));
108    }
109
110    #[test]
111    fn result_alias_err_carries_plugin_error() {
112        let err: Result<u32> = Err(PluginError::NotLoaded);
113        assert_eq!(err.err(), Some(PluginError::NotLoaded));
114    }
115
116    #[test]
117    fn clone_and_partial_eq_hold() {
118        let a = PluginError::AbiVersionMismatch { host: 1, plugin: 2 };
119        assert_eq!(a.clone(), a);
120        // Across-variant inequality (both wrap the integer `1`-ish detail).
121        assert_ne!(
122            PluginError::LibraryLoad("x".into()),
123            PluginError::SymbolMissing("x".into()),
124        );
125    }
126}