1use std::ffi::c_void;
14
15#[repr(u32)]
26#[derive(Debug, Clone, Copy, PartialEq, Eq)]
27#[allow(non_camel_case_types)]
28#[non_exhaustive]
29pub enum ErrorCode {
30 UNKNOWN = 1,
31 NULL_POINTER = 2,
32 INVALID_UTF8 = 3,
33
34 WRONG_TYPE = 10,
35 VALUE_NOT_FOUND = 11,
36 INSUFFICIENT_BUFFER = 12,
37
38 UNKNOWN_PROVIDER = 13,
39
40 PLUGIN_LOAD_FAILED = 20,
41 PLUGIN_SYMBOL_ERROR = 21,
42 PLUGIN_INITIALIZATION_FAILED = 22,
43 PLUGIN_INTERFACE_VERSION_UNSUPPORTED = 23,
44 PLUGIN_NAME_COLLISION = 24,
45 PLUGIN_MISSING_INTERFACE = 25,
46 PLUGIN_INTERNAL_ERROR = 26,
47
48 UNSUPPORTED_ALGORITHM = 50,
49
50 PLUGIN_GENERIC = 27,
55}
56
57impl ErrorCode {
58 pub fn into_wire(self) -> u32 {
60 self as u32
61 }
62}
63
64impl From<ErrorCode> for u32 {
65 fn from(code: ErrorCode) -> Self {
66 code.into_wire()
67 }
68}
69
70#[derive(Debug)]
73pub struct PluginError {
74 code: ErrorCode,
75 message: String,
76}
77
78impl PluginError {
79 pub fn new(code: ErrorCode, message: impl Into<String>) -> Self {
81 Self {
82 code,
83 message: message.into(),
84 }
85 }
86
87 pub fn msg(message: impl Into<String>) -> Self {
89 Self::new(ErrorCode::PLUGIN_GENERIC, message)
90 }
91
92 pub fn code(&self) -> ErrorCode {
94 self.code
95 }
96
97 pub fn message(&self) -> &str {
99 &self.message
100 }
101
102 pub fn into_wire(self) -> u32 {
104 self.code.into_wire()
105 }
106}
107
108impl std::fmt::Display for PluginError {
109 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
110 write!(f, "{}: {}", self.code_as_str(), self.message)
111 }
112}
113
114impl std::error::Error for PluginError {}
115
116impl PluginError {
117 fn code_as_str(&self) -> &'static str {
118 match self.code {
119 ErrorCode::UNKNOWN => "unknown",
120 ErrorCode::NULL_POINTER => "null_pointer",
121 ErrorCode::INVALID_UTF8 => "invalid_utf8",
122 ErrorCode::WRONG_TYPE => "wrong_type",
123 ErrorCode::VALUE_NOT_FOUND => "value_not_found",
124 ErrorCode::INSUFFICIENT_BUFFER => "insufficient_buffer",
125 ErrorCode::UNKNOWN_PROVIDER => "unknown_provider",
126 ErrorCode::PLUGIN_LOAD_FAILED => "plugin_load_failed",
127 ErrorCode::PLUGIN_SYMBOL_ERROR => "plugin_symbol_error",
128 ErrorCode::PLUGIN_INITIALIZATION_FAILED => "plugin_initialization_failed",
129 ErrorCode::PLUGIN_INTERFACE_VERSION_UNSUPPORTED => {
130 "plugin_interface_version_unsupported"
131 }
132 ErrorCode::PLUGIN_NAME_COLLISION => "plugin_name_collision",
133 ErrorCode::PLUGIN_MISSING_INTERFACE => "plugin_missing_interface",
134 ErrorCode::PLUGIN_INTERNAL_ERROR => "plugin_internal_error",
135 ErrorCode::UNSUPPORTED_ALGORITHM => "unsupported_algorithm",
136 ErrorCode::PLUGIN_GENERIC => "plugin_generic",
137 }
138 }
139}
140
141pub type PluginResult<T> = std::result::Result<T, PluginError>;
143
144#[doc(hidden)]
149pub fn to_wire_code<T>(result: PluginResult<T>) -> u32 {
150 match result {
151 Ok(_) => 0,
152 Err(e) => e.into_wire(),
153 }
154}
155
156#[doc(hidden)]
162pub fn box_state<T>(value: T) -> *mut c_void {
163 crate::OpaqueHandle::new(value)
164}
165
166#[cfg(test)]
167mod tests {
168 use super::*;
169
170 #[test]
171 fn error_code_into_wire_matches_discriminant() {
172 assert_eq!(ErrorCode::UNKNOWN.into_wire(), 1);
173 assert_eq!(ErrorCode::PLUGIN_GENERIC.into_wire(), 27);
174 assert_eq!(ErrorCode::UNSUPPORTED_ALGORITHM.into_wire(), 50);
175 }
176
177 #[test]
178 fn plugin_error_carries_code_and_message() {
179 let e = PluginError::new(ErrorCode::INSUFFICIENT_BUFFER, "need 32 bytes");
180 assert_eq!(e.code(), ErrorCode::INSUFFICIENT_BUFFER);
181 assert_eq!(e.message(), "need 32 bytes");
182 let s = format!("{e}");
183 assert!(s.contains("insufficient_buffer"));
184 assert!(s.contains("need 32 bytes"));
185 }
186
187 #[test]
188 fn to_wire_code_success_is_zero() {
189 let r: PluginResult<()> = Ok(());
190 assert_eq!(to_wire_code(r), 0);
191 }
192
193 #[test]
194 fn to_wire_code_failure_is_error_code() {
195 let r: PluginResult<()> = Err(PluginError::new(ErrorCode::NULL_POINTER, "x"));
196 assert_eq!(to_wire_code(r), ErrorCode::NULL_POINTER.into_wire());
197 }
198}