typed_arrow/
error.rs

1//! Error types for typed-arrow.
2
3use arrow_schema::DataType;
4use thiserror::Error;
5
6/// Error type for schema validation failures.
7#[derive(Debug, Clone, Error)]
8pub enum SchemaError {
9    /// Type mismatch between expected and actual schema
10    #[error("schema type mismatch: expected {expected}, got {actual}")]
11    TypeMismatch {
12        /// Expected Arrow DataType
13        expected: DataType,
14        /// Actual Arrow DataType
15        actual: DataType,
16    },
17    /// Missing required field
18    #[error("missing required field: {field_name}")]
19    MissingField {
20        /// Name of the missing field
21        field_name: String,
22    },
23    /// Invalid schema configuration
24    #[error("invalid schema: {message}")]
25    InvalidSchema {
26        /// Error message
27        message: String,
28    },
29}
30
31impl SchemaError {
32    /// Create a type mismatch error
33    pub fn type_mismatch(expected: DataType, actual: DataType) -> Self {
34        Self::TypeMismatch { expected, actual }
35    }
36
37    /// Create a missing field error
38    pub fn missing_field(field_name: impl Into<String>) -> Self {
39        Self::MissingField {
40            field_name: field_name.into(),
41        }
42    }
43
44    /// Create an invalid schema error
45    pub fn invalid(message: impl Into<String>) -> Self {
46        Self::InvalidSchema {
47            message: message.into(),
48        }
49    }
50}
51
52/// Error type for view access failures when reading from Arrow arrays.
53#[cfg(feature = "views")]
54#[derive(Debug, Clone, Error)]
55pub enum ViewAccessError {
56    /// Index out of bounds
57    #[error("index {index} out of bounds (len {len}){}", field_name.map(|n| format!(" for field '{n}'")).unwrap_or_default())]
58    OutOfBounds {
59        /// The invalid index
60        index: usize,
61        /// The array length
62        len: usize,
63        /// Optional field name for context
64        field_name: Option<&'static str>,
65    },
66    /// Unexpected null value
67    #[error("unexpected null at index {index}{}", field_name.map(|n| format!(" for field '{n}'")).unwrap_or_default())]
68    UnexpectedNull {
69        /// The index where null was found
70        index: usize,
71        /// Optional field name for context
72        field_name: Option<&'static str>,
73    },
74    /// Type mismatch during array downcast
75    #[error("type mismatch: expected {expected}, got {actual}{}", field_name.map(|n| format!(" for field '{n}'")).unwrap_or_default())]
76    TypeMismatch {
77        /// Expected Arrow DataType
78        expected: DataType,
79        /// Actual Arrow DataType
80        actual: DataType,
81        /// Optional field name for context
82        field_name: Option<&'static str>,
83    },
84}
85
86/// Allows generic code to uniformly handle both infallible and fallible view-to-owned conversions.
87///
88/// When converting views to owned types, primitives and `String` never fail (`TryFrom<Primitive,
89/// Error = Infallible>`), while nested structs can fail (`TryFrom<StructView, Error =
90/// ViewAccessError>`). This blanket conversion allows generic code like `List<T>` or `Map<K, V>` to
91/// use a single implementation with `E: Into<ViewAccessError>` bounds that works for both cases.
92///
93/// The empty match is safe because `Infallible` is an uninhabited type that can never be
94/// constructed.
95#[cfg(feature = "views")]
96impl From<core::convert::Infallible> for ViewAccessError {
97    fn from(x: core::convert::Infallible) -> Self {
98        match x {}
99    }
100}