Skip to main content

passless_core/
error.rs

1//! Error types for Passless
2//!
3//! This module defines all error types used in the Passless authenticator.
4//! We use `thiserror` for structured error handling with proper error context.
5
6use std::io;
7
8use thiserror::Error;
9
10/// Result type alias using PasslessError
11pub type Result<T> = std::result::Result<T, Error>;
12
13/// Passless-specific errors
14///
15/// These errors represent domain and infrastructure failures.
16/// They can be converted to keylib::Error when needed for compatibility.
17#[derive(Error, Debug)]
18pub enum Error {
19    /// Storage-related errors
20    #[error("Storage error: {0}")]
21    Storage(String),
22
23    /// Configuration errors
24    #[error("Configuration error: {0}")]
25    Config(String),
26
27    /// UHID device errors
28    #[error("UHID error: {0}")]
29    Uhid(String),
30
31    /// Credential management errors
32    #[error("Credential management error: {0}")]
33    CredentialManagement(String),
34
35    /// User verification failed
36    #[error("User verification failed: {0}")]
37    UserVerificationFailed(String),
38
39    /// Operation cancelled by user
40    #[error("Operation cancelled by user")]
41    Cancelled,
42
43    /// Generic IO error
44    #[error("IO error: {0}")]
45    Io(#[from] io::Error),
46
47    /// Serialization/deserialization error
48    #[error("Serialization error: {0}")]
49    Serialization(String),
50
51    /// Invalid data format
52    #[error("Invalid data: {0}")]
53    InvalidData(String),
54
55    /// Generic other error
56    #[error("{0}")]
57    Other(String),
58}
59
60impl Error {
61    /// Format error for command-line output
62    ///
63    /// Provides clean, user-friendly error messages without Rust Debug formatting
64    pub fn format_cli(&self) -> String {
65        match self {
66            Error::Storage(msg) => format!("Storage error: {}", msg),
67            Error::Config(msg) => format!("Configuration error: {}", msg),
68            Error::Uhid(msg) => format!("UHID error: {}", msg),
69            Error::CredentialManagement(msg) => format!("Credential management error: {}", msg),
70            Error::UserVerificationFailed(msg) => format!("User verification failed: {}", msg),
71            Error::Cancelled => "Operation cancelled by user".to_string(),
72            Error::Io(err) => format!("IO error: {}", err),
73            Error::Serialization(msg) => format!("Serialization error: {}", msg),
74            Error::InvalidData(msg) => format!("Invalid data: {}", msg),
75            Error::Other(msg) => msg.clone(),
76        }
77    }
78}
79
80/// Convert soft_fido2::Error to Error for error handling
81impl From<soft_fido2::Error> for Error {
82    fn from(err: soft_fido2::Error) -> Self {
83        Error::CredentialManagement(format!("{:?}", err))
84    }
85}
86
87/// Convert Error to soft_fido2::Error for compatibility
88impl From<Error> for soft_fido2::Error {
89    fn from(err: Error) -> Self {
90        match err {
91            Error::Storage(ref msg) if msg.contains("not found") => soft_fido2::Error::DoesNotExist,
92            Error::Storage(ref msg) if msg.contains("No more credentials") => {
93                soft_fido2::Error::DoesNotExist
94            }
95            _ => soft_fido2::Error::Other,
96        }
97    }
98}
99
100#[cfg(test)]
101mod tests {
102    use super::*;
103
104    #[test]
105    fn test_soft_fido2_error_conversion() {
106        let err: Error = soft_fido2::Error::DoesNotExist.into();
107        assert!(matches!(err, Error::CredentialManagement(_)));
108
109        let err: soft_fido2::Error = Error::Storage("not found".to_string()).into();
110        assert!(matches!(err, soft_fido2::Error::DoesNotExist));
111    }
112}