csaf-core 1.5.28

CSAF storage, validation, sidecar generation, import/export
// SPDX-License-Identifier: Apache-2.0
// Copyright (c) 2026 Pierre Gronau, ndaal in Cologne

//! Error types for the CSAF core crate.

use thiserror::Error;

use crate::listen_port::ListenPortError;

/// Central error type for CSAF core operations.
#[derive(Debug, Error)]
pub enum CsafError {
    /// Storage (redb) error.
    #[error("Storage error: {0}")]
    Storage(String),

    /// Database (`SQLite`) error.
    #[error("Database error: {0}")]
    Database(#[from] rusqlite::Error),

    /// Serialization/deserialization error.
    #[error("Serialization error: {0}")]
    Serialization(#[from] serde_json::Error),

    /// CSAF validation failed with one or more errors.
    #[error("Validation failed: {0}")]
    Validation(String),

    /// File I/O error.
    #[error("I/O error: {0}")]
    Io(#[from] std::io::Error),

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

    /// A configured port that the listen-port rule refuses
    /// (`skills/rust-ip-ports`). The server exits with status 64 for it and
    /// for nothing else. `or_off` adds ", or off" to the message for a port
    /// that can be switched off.
    #[error("{key}: {source}{}", if *.or_off { ", or off" } else { "" })]
    ListenPort {
        /// The config key that held the refused value.
        key: &'static str,
        /// Which part of the rule the value broke.
        source: ListenPortError,
        /// Whether `off` is also accepted for this key.
        or_off: bool,
    },

    /// Document not found.
    #[error("Document not found: {0}")]
    NotFound(String),

    /// Duplicate document.
    #[error("Document already exists: {0}")]
    Duplicate(String),

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

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

impl CsafError {
    /// Whether this error is a configured port the rule refuses, which is the
    /// only error the server maps to exit status 64.
    #[must_use]
    pub const fn is_refused_port(&self) -> bool {
        matches!(self, Self::ListenPort { .. })
    }
}

impl From<redb::Error> for CsafError {
    fn from(e: redb::Error) -> Self {
        Self::Storage(e.to_string())
    }
}

impl From<redb::DatabaseError> for CsafError {
    fn from(e: redb::DatabaseError) -> Self {
        Self::Storage(e.to_string())
    }
}

impl From<redb::TableError> for CsafError {
    fn from(e: redb::TableError) -> Self {
        Self::Storage(e.to_string())
    }
}

impl From<redb::TransactionError> for CsafError {
    fn from(e: redb::TransactionError) -> Self {
        Self::Storage(e.to_string())
    }
}

impl From<redb::CommitError> for CsafError {
    fn from(e: redb::CommitError) -> Self {
        Self::Storage(e.to_string())
    }
}

impl From<redb::StorageError> for CsafError {
    fn from(e: redb::StorageError) -> Self {
        Self::Storage(e.to_string())
    }
}

/// Result type alias using `CsafError`.
pub type Result<T> = std::result::Result<T, CsafError>;