Repository: astral-sh/uv
File: crates/uv-keyring/src/error.rs (Rust)

Code before:
    BadEncoding(Vec<u8>),
    /// This indicates that one of the entry's credential
    /// attributes exceeded a
    /// length limit in the underlying platform.  The
    /// attached values give the name of the attribute and
    /// the platform length limit that was exceeded.
    TooLong(String, u32),
    /// This indicates that one of the entry's required credential
    /// attributes was invalid.  The
    /// attached value gives the name of the attribute
    /// and the reason it's invalid.
    Invalid(String, String),
    /// This indicates that there is more than one credential found in the store
    /// that matches the entry.  Its value is a vector of the matching credentials.
    Ambiguous(Vec<Box<Credential>>),
    /// This indicates that there was no default credential builder to use;
    /// the client must set one before creating entries.
    NoDefaultCredentialBuilder,
}

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 {
            Self::PlatformFailure(err) => write!(f, "Platform secure storage failure: {err}"),
            Self::NoStorageAccess(err) => {
                write!(f, "Couldn't access platform secure storage: {err}")
            }
            Self::NoEntry => write!(f, "No matching entry found in secure storage"),
            Self::BadEncoding(_) => write!(f, "Data is not UTF-8 encoded"),
            Self::TooLong(name, len) => write!(
                f,
                "Attribute '{name}' is longer than platform limit of {len} chars"
            ),
            Self::Invalid(attr, reason) => {
                write!(f, "Attribute {attr} is invalid: {reason}")
            }
            Self::Ambiguous(items) => {
                write!(
                    f,
                    "Entry is matched by {} credentials: {items:?}",
                    items.len(),
                )
            }
            Self::NoDefaultCredentialBuilder => {
                write!(
                    f,
                    "No default credential builder is available; set one before creating entries"
                )
            }

Review comment (bug):
The original motivation here was actually that it was annoying that we shared the inner error here twice when displaying chains to the user.

Code after:
    /// This indicates that the retrieved password blob was not
    /// a UTF-8 string.  The underlying bytes are available
    /// for examination in the attached value.
    #[error("Data is not UTF-8 encoded")]
    BadEncoding(Vec<u8>),
    /// This indicates that one of the entry's credential
    /// attributes exceeded a
    /// length limit in the underlying platform.  The
    /// attached values give the name of the attribute and
    /// the platform length limit that was exceeded.
    #[error("Attribute '{0}' is longer than platform limit of {1} chars")]
    TooLong(String, u32),
    /// This indicates that one of the entry's required credential
    /// attributes was invalid.  The
    /// attached value gives the name of the attribute
    /// and the reason it's invalid.
    #[error("Attribute {0} is invalid: {1}")]
    Invalid(String, String),
    /// This indicates that there is more than one credential found in the store
    /// that matches the entry.  Its value is a vector of the matching credentials.
    #[error("Entry is matched by multiple credentials: {0:?}")]
    Ambiguous(Vec<Box<Credential>>),
    /// This indicates that there was no default credential builder to use;
    /// the client must set one before creating entries.
    #[error("No default credential builder is available; set one before creating entries")]
    NoDefaultCredentialBuilder,
}

pub type Result<T> = std::result::Result<T, Error>;

/// Try to interpret a byte vector as a password string
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() {
        // malformed sequences here taken from:
        // https://www.cl.cam.ac.uk/~mgk25/ucs/examples/UTF-8-test.txt
        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:?}"),
            }
        }
    }