Skip to main content

qubit_value/
value_error.rs

1// =============================================================================
2//    Copyright (c) 2025 - 2026 Haixing Hu.
3//
4//    SPDX-License-Identifier: Apache-2.0
5//
6//    Licensed under the Apache License, Version 2.0.
7// =============================================================================
8//! # Value Processing Error Types
9//!
10//! Defines various errors that may occur during value processing.
11
12use qubit_datatype::DataType;
13#[cfg(feature = "converter")]
14use qubit_datatype::{
15    DataConversionError,
16    DataListConversionError,
17};
18use thiserror::Error;
19
20use crate::ValueMissing;
21
22/// Value processing error type
23///
24/// Defines various error conditions that may occur during value operations.
25/// Downstream matches must include a wildcard arm because this enum is
26/// non-exhaustive and may gain new error variants.
27///
28/// # Features
29///
30/// - Type mismatch error
31/// - Structured missing-value errors
32/// - Structured single-value conversion errors when `converter` is enabled
33/// - Structured list conversion errors, including the failing item index, when
34///   `converter` is enabled
35///
36/// # Examples
37///
38/// ```rust
39/// use qubit_datatype::DataType;
40/// use qubit_value::{ValueError, ValueMissing};
41///
42/// let error = ValueError::Missing(ValueMissing::UnsetScalar {
43///     data_type: DataType::String,
44/// });
45/// assert_eq!(error.to_string(), "Missing value: unset scalar with declared type string");
46/// ```
47#[non_exhaustive]
48#[derive(Debug, Clone, Error, PartialEq, Eq)]
49pub enum ValueError {
50    /// No concrete item is available from typed runtime storage or conversion.
51    #[error("Missing value: {0}")]
52    Missing(
53        /// Structured typed storage state that caused the missing value.
54        ValueMissing,
55    ),
56
57    /// Type mismatch
58    #[error("Type mismatch: expected {expected}, actual {actual}")]
59    TypeMismatch {
60        /// Expected data type
61        expected: DataType,
62        /// Actual data type
63        actual: DataType,
64    },
65
66    /// Error returned by the shared single-value conversion layer.
67    #[cfg(feature = "converter")]
68    #[error("Conversion error: {0}")]
69    Conversion(
70        /// Structured conversion failure from `qubit-datatype`.
71        #[source]
72        DataConversionError,
73    ),
74
75    /// Error returned by the shared list conversion layer.
76    #[cfg(feature = "converter")]
77    #[error("List conversion error: {0}")]
78    ListConversion(
79        /// Structured list conversion failure, including the source index.
80        #[source]
81        DataListConversionError,
82    ),
83}
84
85impl ValueError {
86    /// Reports whether this error describes a missing value.
87    #[must_use]
88    #[inline(always)]
89    pub const fn is_missing(&self) -> bool {
90        matches!(self, Self::Missing(_))
91    }
92
93    /// Returns the structured missing-value reason, when present.
94    #[must_use]
95    #[inline(always)]
96    pub const fn missing(&self) -> Option<&ValueMissing> {
97        match self {
98            Self::Missing(missing) => Some(missing),
99            Self::TypeMismatch { .. } => None,
100            #[cfg(feature = "converter")]
101            Self::Conversion(_) | Self::ListConversion(_) => None,
102        }
103    }
104}
105
106#[cfg(feature = "converter")]
107impl From<DataConversionError> for ValueError {
108    fn from(error: DataConversionError) -> Self {
109        if error.is_missing()
110            && let Some(from) = error.from_type()
111        {
112            return Self::Missing(ValueMissing::Conversion {
113                from,
114                to: error.to_type(),
115            });
116        }
117        if error.kind()
118            == qubit_datatype::DataConversionErrorKind::EmptyCollection
119        {
120            return Self::Missing(ValueMissing::EmptyCollectionConversion {
121                to: error.to_type(),
122            });
123        }
124        Self::Conversion(error)
125    }
126}
127
128#[cfg(feature = "converter")]
129impl From<DataListConversionError> for ValueError {
130    fn from(error: DataListConversionError) -> Self {
131        let (source_index, source) = error.into_parts();
132        if source.is_missing()
133            && let Some(from) = source.from_type()
134        {
135            return Self::Missing(ValueMissing::CollectionItem {
136                source_index,
137                from,
138                to: source.to_type(),
139            });
140        }
141        if source.kind()
142            == qubit_datatype::DataConversionErrorKind::EmptyCollection
143        {
144            return Self::Missing(ValueMissing::EmptyCollectionConversion {
145                to: source.to_type(),
146            });
147        }
148        Self::ListConversion(DataListConversionError::new(source_index, source))
149    }
150}
151
152/// Value processing result type
153pub type ValueResult<T> = Result<T, ValueError>;