kpx 0.0.1

Pure Rust KeePassXC browser integration client with a simple synchronous API
Documentation
use std::{fmt, io, string::FromUtf8Error};

use thiserror::Error;

/// Errors returned by the KeePassXC client library.
#[derive(Debug, Error)]
pub enum KpxError {
    #[error("I/O error: {0}")]
    Io(#[from] io::Error),

    #[error("JSON serialization error: {0}")]
    Serialization(#[from] serde_json::Error),

    #[error("base64 error: {0}")]
    Base64(#[from] base64::DecodeError),

    #[error("encryption error")]
    Crypto,

    #[error("UTF-8 error: {0}")]
    Utf8(#[from] FromUtf8Error),

    #[error("unexpected empty response from KeePassXC")]
    EmptyResponse,

    #[error("missing field '{0}' in response")]
    MissingField(&'static str),

    #[error("unexpected action in response, expected '{expected}', got '{received}'")]
    UnexpectedAction { expected: String, received: String },

    #[error("association rejected: {reason}")]
    AssociationRejected { reason: String },

    #[error("KeePassXC returned an error{code}: {message}", code = DisplayRemoteErrorCode(*.code))]
    RemoteError { code: Option<i32>, message: String },

    #[error("KeePassXC host key unavailable; call `change_public_keys` first")]
    MissingHostKey,
}

impl From<crypto_box::aead::Error> for KpxError {
    fn from(_: crypto_box::aead::Error) -> Self {
        KpxError::Crypto
    }
}

#[derive(Copy, Clone, Debug)]
struct DisplayRemoteErrorCode(Option<i32>);

impl fmt::Display for DisplayRemoteErrorCode {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        if let Some(code) = self.0 {
            write!(f, " ({code})")
        } else {
            Ok(())
        }
    }
}