naru-config 0.6.3

A security-first configuration manager with encryption and audit logging
use thiserror::Error;

#[derive(Error, Debug)]
pub enum NaruError {
    #[error("IO error: {0}")]
    Io(#[from] std::io::Error),

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

    #[error("YAML error: {0}")]
    Yaml(#[from] serde_yaml::Error),

    #[error("TOML error: {0}")]
    Toml(#[from] toml::de::Error),

    #[error("Configuration error: {0}")]
    Config(String),

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

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

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

    #[error("Missing encryption key. Please set NARU_ENCRYPTION_KEY environment variable.")]
    MissingEncryptionKey,

    #[error("Invalid key: {0}")]
    InvalidKey(String),

    #[error("Invalid environment name: {0}")]
    InvalidEnvironment(String),

    #[error("Environment '{0}' not found.")]
    EnvironmentNotFound(String),

    #[error("Key '{0}' not found in environment '{1}'.")]
    KeyNotFound(String, String),

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

    #[error("Schema field '{0}' not found.")]
    SchemaFieldNotFound(String),

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

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

    #[error("Audit log integrity check failed. The log may have been tampered with.")]
    AuditIntegrityFailure,

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

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

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

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

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

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

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

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

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

    #[error("Project not initialized. Run 'naru init' first.")]
    ProjectNotInitialized,

    #[error("Project already initialized.")]
    ProjectAlreadyInitialized,

    #[error("Unsupported format: {0}")]
    UnsupportedFormat(String),

    #[error("Invalid value: {0}")]
    InvalidValue(String),
}

impl NaruError {
    pub fn config(msg: impl Into<String>) -> Self {
        NaruError::Config(msg.into())
    }

    pub fn validation(msg: impl Into<String>) -> Self {
        NaruError::Validation(msg.into())
    }

    pub fn encryption(msg: impl Into<String>) -> Self {
        NaruError::Encryption(msg.into())
    }

    pub fn decryption(msg: impl Into<String>) -> Self {
        NaruError::Decryption(msg.into())
    }

    pub fn invalid_key(msg: impl Into<String>) -> Self {
        NaruError::InvalidKey(msg.into())
    }

    pub fn invalid_environment(msg: impl Into<String>) -> Self {
        NaruError::InvalidEnvironment(msg.into())
    }

    pub fn environment_not_found(env: impl Into<String>) -> Self {
        NaruError::EnvironmentNotFound(env.into())
    }

    pub fn key_not_found(key: impl Into<String>, env: impl Into<String>) -> Self {
        NaruError::KeyNotFound(key.into(), env.into())
    }

    pub fn schema(msg: impl Into<String>) -> Self {
        NaruError::Schema(msg.into())
    }

    pub fn schema_field_not_found(field: impl Into<String>) -> Self {
        NaruError::SchemaFieldNotFound(field.into())
    }

    pub fn file(msg: impl Into<String>) -> Self {
        NaruError::File(msg.into())
    }

    pub fn security(msg: impl Into<String>) -> Self {
        NaruError::Security(msg.into())
    }

    pub fn lock(msg: impl Into<String>) -> Self {
        NaruError::Lock(msg.into())
    }

    pub fn serialization(msg: impl Into<String>) -> Self {
        NaruError::Serialization(msg.into())
    }

    pub fn deserialization(msg: impl Into<String>) -> Self {
        NaruError::Deserialization(msg.into())
    }

    pub fn backup(msg: impl Into<String>) -> Self {
        NaruError::Backup(msg.into())
    }

    pub fn restore(msg: impl Into<String>) -> Self {
        NaruError::Restore(msg.into())
    }

    pub fn import(msg: impl Into<String>) -> Self {
        NaruError::Import(msg.into())
    }

    pub fn export(msg: impl Into<String>) -> Self {
        NaruError::Export(msg.into())
    }

    pub fn conversion(msg: impl Into<String>) -> Self {
        NaruError::Conversion(msg.into())
    }

    pub fn cryptography(msg: impl Into<String>) -> Self {
        NaruError::Cryptography(msg.into())
    }

    pub fn unsupported_format(format: impl Into<String>) -> Self {
        NaruError::UnsupportedFormat(format.into())
    }

    pub fn invalid_value(msg: impl Into<String>) -> Self {
        NaruError::InvalidValue(msg.into())
    }
}

impl From<NaruError> for std::io::Error {
    fn from(err: NaruError) -> Self {
        std::io::Error::other(err.to_string())
    }
}

impl serde::ser::Error for NaruError {
    fn custom<T: std::fmt::Display>(msg: T) -> Self {
        NaruError::Serialization(msg.to_string())
    }
}

impl serde::de::Error for NaruError {
    fn custom<T: std::fmt::Display>(msg: T) -> Self {
        NaruError::Deserialization(msg.to_string())
    }
}

#[allow(dead_code)]
pub type NaruResult<T> = Result<T, NaruError>;

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

    #[test]
    fn test_error_messages() {
        let err = NaruError::environment_not_found("production");
        assert_eq!(err.to_string(), "Environment 'production' not found.");

        let err = NaruError::key_not_found("API_KEY", "development");
        assert_eq!(
            err.to_string(),
            "Key 'API_KEY' not found in environment 'development'."
        );

        let err = NaruError::validation("Invalid email format");
        assert_eq!(err.to_string(), "Validation error: Invalid email format");
    }

    #[test]
    fn test_error_conversion() {
        let io_err: std::io::Error = NaruError::config("test").into();
        assert_eq!(io_err.kind(), std::io::ErrorKind::Other);
    }

    #[test]
    fn test_builder_methods() {
        assert_eq!(
            NaruError::config("msg").to_string(),
            "Configuration error: msg"
        );
        assert_eq!(
            NaruError::validation("msg").to_string(),
            "Validation error: msg"
        );
        assert_eq!(
            NaruError::encryption("msg").to_string(),
            "Encryption error: msg"
        );
        assert_eq!(
            NaruError::decryption("msg").to_string(),
            "Decryption error: msg"
        );
        assert_eq!(
            NaruError::invalid_key("msg").to_string(),
            "Invalid key: msg"
        );
        assert_eq!(
            NaruError::invalid_environment("msg").to_string(),
            "Invalid environment name: msg"
        );
    }

    #[test]
    fn test_result_type_alias() {
        fn returns_result() -> NaruResult<String> {
            Ok("success".to_string())
        }

        assert!(returns_result().is_ok());
    }
}