Skip to main content

kizzasi_tokenizer/
error.rs

1//! Error types for kizzasi-tokenizer
2//!
3//! Provides comprehensive error types with detailed context for debugging
4//! and user-friendly error messages.
5
6use thiserror::Error;
7
8/// Result type alias for tokenizer operations
9pub type TokenizerResult<T> = Result<T, TokenizerError>;
10
11/// Errors that can occur in tokenizer operations
12#[derive(Error, Debug)]
13pub enum TokenizerError {
14    /// Configuration parameter is invalid
15    ///
16    /// This error includes detailed context about what parameter was invalid
17    /// and why, to help users fix their configuration.
18    #[error("Invalid configuration: {0}")]
19    InvalidConfig(String),
20
21    /// Array dimensions don't match expected values
22    ///
23    /// Provides both the expected and actual dimensions to help diagnose
24    /// shape mismatches in multi-dimensional arrays.
25    #[error("Dimension mismatch in {context}: expected {expected}, got {got}")]
26    DimensionMismatch {
27        expected: usize,
28        got: usize,
29        context: String,
30    },
31
32    /// Signal encoding operation failed
33    ///
34    /// Wraps detailed information about why encoding failed, such as
35    /// invalid input ranges, NaN values, or algorithmic failures.
36    #[error("Encoding failed in {operation}: {reason}")]
37    EncodingError { operation: String, reason: String },
38
39    /// Signal decoding operation failed
40    ///
41    /// Includes context about which decoding step failed and why.
42    #[error("Decoding failed in {operation}: {reason}")]
43    DecodingError { operation: String, reason: String },
44
45    /// Codebook has not been initialized before use
46    ///
47    /// This typically happens when trying to use a VQ-VAE tokenizer
48    /// before training or loading a pretrained codebook.
49    #[error("Codebook not initialized: {hint}")]
50    CodebookNotInitialized { hint: String },
51
52    /// Value is outside the valid range
53    ///
54    /// Provides the offending value and the valid range for debugging.
55    #[error("Value out of range in {context}: {value} not in [{min}, {max}]")]
56    ValueOutOfRange {
57        value: f32,
58        min: f32,
59        max: f32,
60        context: String,
61    },
62
63    /// Error from kizzasi-core crate (not available on wasm32)
64    #[cfg(not(target_arch = "wasm32"))]
65    #[error("Core error: {0}")]
66    CoreError(#[from] kizzasi_core::CoreError),
67
68    /// Internal error that should not happen under normal circumstances
69    ///
70    /// If you encounter this error, it likely indicates a bug in the library.
71    /// Please report it with the full error message and context.
72    #[error("Internal error (please report this): {0}")]
73    InternalError(String),
74
75    /// Invalid input data provided to a function
76    ///
77    /// This error provides context about what was invalid in the input,
78    /// such as empty arrays, NaN values, or incorrect types.
79    #[error("Invalid input in {operation}: {reason}")]
80    InvalidInput { operation: String, reason: String },
81
82    /// Serialization or deserialization failed
83    #[error("Serialization error: {0}")]
84    SerializationError(String),
85
86    /// File I/O operation failed
87    #[error("I/O error: {0}")]
88    IoError(#[from] std::io::Error),
89
90    /// Training operation failed
91    #[error("Training error at epoch {epoch}: {reason}")]
92    TrainingError { epoch: usize, reason: String },
93
94    /// Numerical computation resulted in invalid values (NaN, Inf)
95    #[error("Numerical error in {operation}: {reason}")]
96    NumericalError { operation: String, reason: String },
97
98    /// Resource limit exceeded
99    #[error("Resource limit exceeded: {resource} - {details}")]
100    ResourceLimitExceeded { resource: String, details: String },
101
102    /// Feature not yet implemented
103    #[error("Feature not implemented: {0}")]
104    NotImplemented(String),
105}
106
107impl TokenizerError {
108    /// Create a dimension mismatch error with context
109    pub fn dim_mismatch(expected: usize, got: usize, context: impl Into<String>) -> Self {
110        Self::DimensionMismatch {
111            expected,
112            got,
113            context: context.into(),
114        }
115    }
116
117    /// Create an encoding error with context
118    pub fn encoding(operation: impl Into<String>, reason: impl Into<String>) -> Self {
119        Self::EncodingError {
120            operation: operation.into(),
121            reason: reason.into(),
122        }
123    }
124
125    /// Create a decoding error with context
126    pub fn decoding(operation: impl Into<String>, reason: impl Into<String>) -> Self {
127        Self::DecodingError {
128            operation: operation.into(),
129            reason: reason.into(),
130        }
131    }
132
133    /// Create a value out of range error with context
134    pub fn out_of_range(value: f32, min: f32, max: f32, context: impl Into<String>) -> Self {
135        Self::ValueOutOfRange {
136            value,
137            min,
138            max,
139            context: context.into(),
140        }
141    }
142
143    /// Create an invalid input error with context
144    pub fn invalid_input(operation: impl Into<String>, reason: impl Into<String>) -> Self {
145        Self::InvalidInput {
146            operation: operation.into(),
147            reason: reason.into(),
148        }
149    }
150
151    /// Create a numerical error with context
152    pub fn numerical(operation: impl Into<String>, reason: impl Into<String>) -> Self {
153        Self::NumericalError {
154            operation: operation.into(),
155            reason: reason.into(),
156        }
157    }
158
159    /// Create a training error with context
160    pub fn training(epoch: usize, reason: impl Into<String>) -> Self {
161        Self::TrainingError {
162            epoch,
163            reason: reason.into(),
164        }
165    }
166}
167
168#[cfg(test)]
169mod tests {
170    use super::*;
171
172    #[test]
173    fn test_error_messages() {
174        let err = TokenizerError::dim_mismatch(10, 20, "input validation");
175        assert!(err.to_string().contains("10"));
176        assert!(err.to_string().contains("20"));
177        assert!(err.to_string().contains("input validation"));
178
179        let err2 = TokenizerError::encoding("VQ-VAE", "codebook lookup failed");
180        assert!(err2.to_string().contains("VQ-VAE"));
181        assert!(err2.to_string().contains("codebook lookup failed"));
182    }
183
184    #[test]
185    fn test_helper_constructors() {
186        let err = TokenizerError::out_of_range(1.5, 0.0, 1.0, "quantization");
187        assert!(matches!(err, TokenizerError::ValueOutOfRange { .. }));
188
189        let err2 = TokenizerError::invalid_input("encode", "empty array");
190        assert!(matches!(err2, TokenizerError::InvalidInput { .. }));
191    }
192}