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>;