1use std::io;
7use std::path::PathBuf;
8
9use thiserror::Error;
10
11pub type Result<T> = std::result::Result<T, Error>;
13
14#[derive(Error, Debug)]
19pub enum Error {
20 #[error("Storage error: {0}")]
22 Storage(String),
23
24 #[error("Configuration error: {0}")]
26 Config(String),
27
28 #[error("UHID error: {0}")]
30 Uhid(String),
31
32 #[error("Credential management error: {0}")]
34 CredentialManagement(String),
35
36 #[error("User verification failed: {0}")]
38 UserVerificationFailed(String),
39
40 #[error("Operation cancelled by user")]
42 Cancelled,
43
44 #[error("IO error: {0}")]
46 Io(#[from] io::Error),
47
48 #[error("Serialization error: {0}")]
50 Serialization(String),
51
52 #[error("Invalid data: {0}")]
54 InvalidData(String),
55
56 #[error("another Passless instance is already using backend state at {path}")]
58 AlreadyRunning { path: PathBuf },
59
60 #[error("{0}")]
62 Other(String),
63}
64
65impl Error {
66 pub fn format_cli(&self) -> String {
70 match self {
71 Error::Storage(msg) => format!("Storage error: {}", msg),
72 Error::Config(msg) => format!("Configuration error: {}", msg),
73 Error::Uhid(msg) => format!("UHID error: {}", msg),
74 Error::CredentialManagement(msg) => format!("Credential management error: {}", msg),
75 Error::UserVerificationFailed(msg) => format!("User verification failed: {}", msg),
76 Error::Cancelled => "Operation cancelled by user".to_string(),
77 Error::Io(err) => format!("IO error: {}", err),
78 Error::Serialization(msg) => format!("Serialization error: {}", msg),
79 Error::InvalidData(msg) => format!("Invalid data: {}", msg),
80 Error::AlreadyRunning { path } => format!(
81 "another Passless instance is already using backend state:\n\
82 \x20 {}\n\
83 \n\
84 Stop the existing process or disable the duplicate systemd user service.\n\
85 \n\
86 To diagnose:\n\
87 \x20 systemctl --user status passless\n\
88 \x20 pgrep -a passless\n\
89 \x20 journalctl --user -u passless",
90 path.display()
91 ),
92 Error::Other(msg) => msg.clone(),
93 }
94 }
95}
96
97impl From<soft_fido2::Error> for Error {
99 fn from(err: soft_fido2::Error) -> Self {
100 Error::CredentialManagement(format!("{:?}", err))
101 }
102}
103
104impl From<Error> for soft_fido2::Error {
106 fn from(err: Error) -> Self {
107 match err {
108 Error::Storage(ref msg) if msg.contains("not found") => soft_fido2::Error::DoesNotExist,
109 Error::Storage(ref msg) if msg.contains("No more credentials") => {
110 soft_fido2::Error::DoesNotExist
111 }
112 _ => soft_fido2::Error::Other,
113 }
114 }
115}
116
117#[cfg(test)]
118mod tests {
119 use super::*;
120
121 #[test]
122 fn test_soft_fido2_error_conversion() {
123 let err: Error = soft_fido2::Error::DoesNotExist.into();
124 assert!(matches!(err, Error::CredentialManagement(_)));
125
126 let err: soft_fido2::Error = Error::Storage("not found".to_string()).into();
127 assert!(matches!(err, soft_fido2::Error::DoesNotExist));
128 }
129}