1use std::convert::Infallible;
2
3use crate::ffi;
4
5#[derive(Debug)]
30#[must_use]
31pub struct SodotError(Repr);
32
33#[derive(Debug)]
38pub(crate) enum Repr {
39 InvalidUuid(String),
41 InvalidHostUrl(String),
43 InvalidSecretShare(String),
45 InvalidMessage(String),
47 InvalidNumberOfParties(String),
49 ExecutorError(String),
51 InvalidKeygenKeypair(String),
53 InvalidKeygenId(String),
55 InvalidDerivationElement(String),
57 InvalidXpriv(String),
59 InvalidSpriv(String),
61 InvalidApiKey(String),
63 InvalidPrivateKey(String),
65 InvalidTweak(String),
67 InvalidXpub(String),
69 InvalidSpub(String),
71 InvalidPublicKey(String),
73 NullPointer(String),
75 PanicError(String),
77 PlatformError(String),
79 Internal(String),
81 UnknownExecutorError { code: i32, msg: String },
83}
84
85impl std::fmt::Display for SodotError {
86 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
87 match &self.0 {
88 Repr::InvalidUuid(msg) => write!(f, "Invalid UUID: {msg}"),
89 Repr::InvalidHostUrl(msg) => write!(f, "Invalid host URL: {msg}"),
90 Repr::InvalidSecretShare(msg) => write!(f, "Invalid secret share: {msg}"),
91 Repr::InvalidMessage(msg) => write!(f, "Invalid message: {msg}"),
92 Repr::InvalidNumberOfParties(msg) => write!(f, "Invalid number of parties: {msg}"),
93 Repr::ExecutorError(msg) => write!(f, "Executor error: {msg}"),
94 Repr::InvalidKeygenKeypair(msg) => write!(f, "Invalid keygen keypair: {msg}"),
95 Repr::InvalidKeygenId(msg) => write!(f, "Invalid keygen ID: {msg}"),
96 Repr::InvalidDerivationElement(msg) => write!(f, "Invalid derivation element: {msg}"),
97 Repr::InvalidXpriv(msg) => write!(f, "Invalid extended private key: {msg}"),
98 Repr::InvalidSpriv(msg) => write!(f, "Invalid secret private key: {msg}"),
99 Repr::InvalidApiKey(msg) => write!(f, "Invalid API key: {msg}"),
100 Repr::InvalidPrivateKey(msg) => write!(f, "Invalid private key: {msg}"),
101 Repr::InvalidTweak(msg) => write!(f, "Invalid tweak: {msg}"),
102 Repr::InvalidXpub(msg) => write!(f, "Invalid extended public key: {msg}"),
103 Repr::InvalidSpub(msg) => write!(f, "Invalid secret public key: {msg}"),
104 Repr::InvalidPublicKey(msg) => write!(f, "Invalid public key: {msg}"),
105 Repr::NullPointer(msg) => write!(f, "Null pointer error: {msg}"),
106 Repr::PanicError(msg) => write!(f, "Panic error: {msg}"),
107 Repr::PlatformError(msg) => write!(f, "Platform error: {msg}"),
108 Repr::Internal(msg) => write!(f, "Internal error: {msg}"),
109 Repr::UnknownExecutorError { code, msg } => write!(f, "Unknown executor error (code: {code}): {msg}"),
110 }
111 }
112}
113
114impl std::error::Error for SodotError {}
115
116impl SodotError {
117 pub(crate) fn internal(message: String) -> Self {
121 SodotError(Repr::Internal(message))
122 }
123
124 #[cfg(test)]
126 pub(crate) fn repr(&self) -> &Repr {
127 &self.0
128 }
129}
130
131impl From<Infallible> for SodotError {
132 fn from(inf: Infallible) -> Self {
133 match inf {}
134 }
135}
136
137impl From<ffi::ResultFFI> for Result<(), SodotError> {
138 fn from(value: ffi::ResultFFI) -> Self {
140 if value.error_code == ffi::SODOT_OK {
141 return Ok(());
142 }
143 let message = value.err_str.as_str();
144 let repr = match value.error_code {
145 ffi::SODOT_INVALID_UUID => Repr::InvalidUuid(message.to_string()),
146 ffi::SODOT_INVALID_HOST_URL => Repr::InvalidHostUrl(message.to_string()),
147 ffi::SODOT_INVALID_SECRET_SHARE_ERR => Repr::InvalidSecretShare(message.to_string()),
148 ffi::SODOT_INVALID_MESSAGE_ERR => Repr::InvalidMessage(message.to_string()),
149 ffi::SODOT_INVALID_NUMBER_OF_PARTIES_ERR => Repr::InvalidNumberOfParties(message.to_string()),
150 ffi::SODOT_EXECUTOR_ERROR => Repr::ExecutorError(message.to_string()),
151 ffi::SODOT_INVALID_KEYGEN_INIT_KEYPAIR_ERR => Repr::InvalidKeygenKeypair(message.to_string()),
152 ffi::SODOT_INVALID_KEYGEN_ID_ERR => Repr::InvalidKeygenId(message.to_string()),
153 ffi::SODOT_INVALID_DERIVATION_ELEM_ERR => Repr::InvalidDerivationElement(message.to_string()),
154 ffi::SODOT_INVALID_XPRIV_ERR => Repr::InvalidXpriv(message.to_string()),
155 ffi::SODOT_INVALID_SPRIV_ERR => Repr::InvalidSpriv(message.to_string()),
156 ffi::SODOT_INVALID_API_KEY => Repr::InvalidApiKey(message.to_string()),
157 ffi::SODOT_INVALID_PRIVATE_KEY => Repr::InvalidPrivateKey(message.to_string()),
158 ffi::SODOT_INVALID_TWEAK => Repr::InvalidTweak(message.to_string()),
159 ffi::SODOT_INVALID_XPUB_ERR => Repr::InvalidXpub(message.to_string()),
160 ffi::SODOT_INVALID_SPUB_ERR => Repr::InvalidSpub(message.to_string()),
161 ffi::SODOT_INVALID_PUBLIC_KEY => Repr::InvalidPublicKey(message.to_string()),
162 ffi::SODOT_NULL_POINTER => Repr::NullPointer(message.to_string()),
163 ffi::SODOT_PANIC_ERR => Repr::PanicError(message.to_string()),
164 ffi::SODOT_PLATFORM_ERR => Repr::PlatformError(message.to_string()),
165 _ => Repr::UnknownExecutorError { code: value.error_code, msg: message.to_string() },
166 };
167 Err(SodotError(repr))
168 }
169}
170
171#[cfg(test)]
172mod tests {
173 use super::*;
174 use crate::ffi::{HeapString, ResultFFI};
175 use rstest::rstest;
176
177 #[rstest]
178 #[case(-1, "uuid")]
179 #[case(-8, "executor")]
180 #[case(-253, "null")]
181 #[case(-254, "panic")]
182 fn test_ffi_error_mapping(#[case] code: i32, #[case] expected: &str) {
183 let result = ResultFFI { error_code: code, err_str: HeapString::NULL };
184 let error = Result::from(result).unwrap_err();
185 let msg = format!("{error}").to_lowercase();
186 assert!(msg.contains(expected), "Error code {code} should contain '{expected}': {msg}");
187 }
188
189 #[rstest]
190 fn test_handle_success() {
191 let result = ResultFFI { error_code: 0, err_str: HeapString::NULL };
192 Result::from(result).unwrap();
193 }
194
195 #[rstest]
196 fn test_error_constructors() {
197 let internal_err = SodotError::internal("internal".to_string());
198 assert!(format!("{internal_err}").to_lowercase().contains("internal"));
199 }
200}