use crate::Credential;
#[derive(Debug)]
#[non_exhaustive]
pub enum Error {
PlatformFailure(Box<dyn std::error::Error + Send + Sync>),
NoStorageAccess(Box<dyn std::error::Error + Send + Sync>),
NoEntry,
BadEncoding(Vec<u8>),
TooLong(String, u32),
Invalid(String, String),
Ambiguous(Vec<Box<Credential>>),
}
pub type Result<T> = std::result::Result<T, Error>;
impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
Error::PlatformFailure(err) => write!(f, "Platform secure storage failure: {err}"),
Error::NoStorageAccess(err) => {
write!(f, "Couldn't access platform secure storage: {err}")
}
Error::NoEntry => write!(f, "No matching entry found in secure storage"),
Error::BadEncoding(_) => write!(f, "Data is not UTF-8 encoded"),
Error::TooLong(name, len) => write!(
f,
"Attribute '{name}' is longer than platform limit of {len} chars"
),
Error::Invalid(attr, reason) => {
write!(f, "Attribute {attr} is invalid: {reason}")
}
Error::Ambiguous(items) => {
write!(
f,
"Entry is matched by {} credentials: {items:?}",
items.len(),
)
}
}
}
}
impl std::error::Error for Error {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Error::PlatformFailure(err) => Some(err.as_ref()),
Error::NoStorageAccess(err) => Some(err.as_ref()),
_ => None,
}
}
}
pub fn decode_password(bytes: Vec<u8>) -> Result<String> {
String::from_utf8(bytes).map_err(|err| Error::BadEncoding(err.into_bytes()))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_bad_password() {
for bytes in [b"\x80".to_vec(), b"\xbf".to_vec(), b"\xed\xa0\xa0".to_vec()] {
match decode_password(bytes.clone()) {
Err(Error::BadEncoding(str)) => assert_eq!(str, bytes),
Err(other) => panic!("Bad password ({bytes:?}) decode gave wrong error: {other}"),
Ok(s) => panic!("Bad password ({bytes:?}) decode gave results: {s:?}"),
}
}
}
}