helena 0.1.0

Core types and component interfaces for helena, a latent data-to-waveform generation platform.
Documentation
//! Shared error taxonomy for helena.
//!
//! Every crate in the workspace returns [`Result`]. The variants that gate
//! caller logic are structured ([`Error::Shape`], [`Error::KindMismatch`],
//! [`Error::ClockMismatch`], [`Error::NonFinite`]); the general semantic
//! bucket ([`Error::Validation`]) remains for one-off boundary rejections
//! whose structure no caller branches on. Component-specific failures are
//! mapped into these variants at the crate boundary so callers have a single
//! error type to match on.

use thiserror::Error;

use crate::latent::KindTag;
use crate::time::TimeBase;

/// Convenience alias used throughout the workspace.
pub type Result<T, E = Error> = std::result::Result<T, E>;

/// The workspace-wide error type.
#[derive(Debug, Error)]
#[non_exhaustive]
pub enum Error {
    /// A tensor / array shape did not match what an operation required.
    ///
    /// Carries *shapes*, not element counts, so a `[2, 3]`-vs-`[6]` mismatch
    /// (equal counts, different structure) is reportable.
    #[error("shape mismatch: expected {expected:?}, got {actual:?}")]
    Shape {
        /// Shape the operation required.
        expected: Vec<usize>,
        /// Shape actually supplied.
        actual: Vec<usize>,
    },

    /// A latent of one [`kind`](crate::latent::LatentKind) reached a seam
    /// expecting the other. Statically-typed pipelines cannot produce this;
    /// it arises only at the dynamic serialization edge
    /// ([`AnyLatent::downcast`](crate::latent::AnyLatent::downcast)).
    #[error("latent kind mismatch: expected {expected}, got {actual}")]
    KindMismatch {
        /// Kind the caller required.
        expected: KindTag,
        /// Kind actually present.
        actual: KindTag,
    },

    /// Two clocked values disagreed on their [`TimeBase`] where a joint
    /// construction demanded agreement.
    #[error("time-base mismatch: {left} vs {right}")]
    ClockMismatch {
        /// The first clock.
        left: TimeBase,
        /// The second clock.
        right: TimeBase,
    },

    /// A boundary value carried a NaN or infinity where the domain type
    /// requires finite data.
    #[error("non-finite value in {context}")]
    NonFinite {
        /// The boundary that rejected the value.
        context: &'static str,
    },

    /// Dataset, config, or manifest validation failed (PRD FR-004).
    #[error("validation error: {0}")]
    Validation(String),

    /// A requested configuration or modality is not supported.
    #[error("unsupported: {0}")]
    Unsupported(String),

    /// Model weights could not be serialized to, or restored from, a
    /// checkpoint record (PRD NFR-011). Distinct from [`Error::Io`], which
    /// covers the file read/write itself.
    #[error("checkpoint error: {0}")]
    Checkpoint(String),

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

    /// (De)serialization failure for JSON manifests / artifacts.
    #[error("json error: {0}")]
    Json(#[from] serde_json::Error),

    /// A TOML document (e.g. an experiment config) could not be parsed into
    /// the target shape. Kept distinct from [`Error::Validation`], which is a
    /// *semantic* failure of a document that already parsed.
    #[error("toml error: {0}")]
    Toml(#[from] toml::de::Error),
}

impl Error {
    /// Shorthand for an [`Error::Validation`] from any displayable value.
    pub fn validation(msg: impl std::fmt::Display) -> Self {
        Error::Validation(msg.to_string())
    }

    /// Shorthand for an [`Error::Shape`] from expected/actual shapes.
    pub fn shape(expected: impl Into<Vec<usize>>, actual: impl Into<Vec<usize>>) -> Self {
        Error::Shape {
            expected: expected.into(),
            actual: actual.into(),
        }
    }
}

// Compile-time guarantee that `Error` stays a standard, thread-safe error:
// usable as `Box<dyn std::error::Error + Send + Sync>` and movable across
// threads. A future variant carrying a non-`Send` payload fails the build
// here rather than silently at a downstream `?`.
const _: fn() = || {
    fn assert_error_send_sync_static<T: std::error::Error + Send + Sync + 'static>() {}
    assert_error_send_sync_static::<Error>();
};