hardware-enclave 0.1.1

Hardware-backed key management — macOS Secure Enclave, Windows TPM 2.0, Linux TPM/keyring
Documentation
#![allow(dead_code, unused_imports, unused_qualifications, unreachable_patterns)]
use thiserror::Error;

#[derive(Debug, Error)]
pub enum AdapterError {
    #[error("i/o error: {0}")]
    Io(#[from] std::io::Error),

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

    #[error("unable to determine configuration directory")]
    MissingConfigDir,

    #[error("unable to determine home directory")]
    MissingHomeDir,

    #[error("program not found: {0}")]
    ProgramNotFound(String),

    #[error("shell resolution for `{command}` returned an unsupported result: {raw}")]
    UnsupportedShellResolution { command: String, raw: String },

    #[error("command -v failed for `{command}`: {stderr}")]
    CommandVFailed { command: String, stderr: String },

    #[error("no supported integration type was provided")]
    NoSupportedIntegration,

    #[error("storage error: {0}")]
    Storage(String),

    #[error("missing secret for binding `{0}`")]
    MissingSecret(String),

    #[error("configuration override is required for this integration mode")]
    MissingConfigOverride,

    #[error("application `{app}` does not support integration type `{integration}`")]
    UnsupportedIntegration { app: String, integration: String },

    #[error("no available prepared integration candidate matched the application support matrix")]
    NoAvailableIntegrationCandidate,
}

impl From<crate::internal::app_storage::StorageError> for AdapterError {
    fn from(value: crate::internal::app_storage::StorageError) -> Self {
        Self::Storage(value.to_string())
    }
}

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

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn all_variants_display_nonempty_messages() {
        let variants: Vec<AdapterError> = vec![
            AdapterError::Io(std::io::Error::other("io")),
            AdapterError::Json(
                serde_json::from_str::<()>("bad").expect_err("intentionally bad JSON"),
            ),
            AdapterError::MissingConfigDir,
            AdapterError::MissingHomeDir,
            AdapterError::ProgramNotFound("prog".into()),
            AdapterError::UnsupportedShellResolution {
                command: "cmd".into(),
                raw: "raw".into(),
            },
            AdapterError::CommandVFailed {
                command: "cmd".into(),
                stderr: "err".into(),
            },
            AdapterError::NoSupportedIntegration,
            AdapterError::Storage("details".into()),
            AdapterError::MissingSecret("binding".into()),
            AdapterError::MissingConfigOverride,
            AdapterError::UnsupportedIntegration {
                app: "app".into(),
                integration: "T1".into(),
            },
            AdapterError::NoAvailableIntegrationCandidate,
        ];
        for variant in &variants {
            let msg = variant.to_string();
            assert!(!msg.is_empty(), "Display for {variant:?} must not be empty");
        }
    }

    #[test]
    fn from_storage_error_produces_storage_variant() {
        use crate::internal::app_storage::StorageError;
        let storage_err = StorageError::EncryptionFailed("test encryption error".into());
        let adapter_err = AdapterError::from(storage_err);
        assert!(matches!(adapter_err, AdapterError::Storage(_)));
        let msg = adapter_err.to_string();
        assert!(msg.contains("test encryption error"), "message: {msg}");
    }
}