Skip to main content

qubit_value/
value_missing.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//! Structured reasons why a value read produced no concrete item.
9
10use std::fmt;
11
12use qubit_datatype::DataType;
13
14/// Describes the typed state that produced a missing-value error.
15#[must_use]
16#[non_exhaustive]
17#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
18pub enum ValueMissing {
19    /// A scalar was unset with a declared data type.
20    UnsetScalar {
21        /// Type retained by the unset scalar storage.
22        data_type: DataType,
23    },
24    /// A collection was unset with a declared element type.
25    UnsetCollection {
26        /// Element type retained by the unset collection storage.
27        data_type: DataType,
28    },
29    /// A concrete collection contains no item for a first-item read.
30    EmptyCollection {
31        /// Element type of the concrete empty collection.
32        data_type: DataType,
33    },
34    /// A conversion requested one item from an empty collection.
35    ///
36    /// The source collection has no item and therefore no source value type
37    /// can be recovered from the shared conversion error. `to` records the
38    /// requested target type instead of overloading `EmptyCollection`.
39    EmptyCollectionConversion {
40        /// Requested target data type.
41        to: DataType,
42    },
43    /// A conversion policy treated a concrete scalar as missing.
44    Conversion {
45        /// Declared source data type.
46        from: DataType,
47        /// Requested target data type.
48        to: DataType,
49    },
50    /// A collection item conversion produced no value.
51    CollectionItem {
52        /// Original zero-based source position.
53        source_index: usize,
54        /// Declared source data type.
55        from: DataType,
56        /// Requested target data type.
57        to: DataType,
58    },
59}
60
61impl ValueMissing {
62    /// Returns the source or declared data type associated with the error.
63    ///
64    /// Returns `None` for [`Self::EmptyCollectionConversion`] because no source
65    /// item exists for that conversion.
66    #[inline(always)]
67    pub const fn source_type(self) -> Option<DataType> {
68        match self {
69            Self::UnsetScalar { data_type }
70            | Self::UnsetCollection { data_type }
71            | Self::EmptyCollection { data_type } => Some(data_type),
72            Self::EmptyCollectionConversion { .. } => None,
73            Self::Conversion { from, .. }
74            | Self::CollectionItem { from, .. } => Some(from),
75        }
76    }
77
78    /// Returns the requested target type for conversion failures.
79    #[must_use]
80    #[inline(always)]
81    pub const fn target_type(self) -> Option<DataType> {
82        match self {
83            Self::Conversion { to, .. }
84            | Self::CollectionItem { to, .. }
85            | Self::EmptyCollectionConversion { to } => Some(to),
86            Self::UnsetScalar { .. }
87            | Self::UnsetCollection { .. }
88            | Self::EmptyCollection { .. } => None,
89        }
90    }
91
92    /// Returns the source index for a missing collection item.
93    #[must_use]
94    #[inline(always)]
95    pub const fn source_index(self) -> Option<usize> {
96        match self {
97            Self::CollectionItem { source_index, .. } => Some(source_index),
98            Self::UnsetScalar { .. }
99            | Self::UnsetCollection { .. }
100            | Self::EmptyCollection { .. }
101            | Self::EmptyCollectionConversion { .. }
102            | Self::Conversion { .. } => None,
103        }
104    }
105
106    /// Reports whether storage itself is unset.
107    #[must_use]
108    #[inline(always)]
109    pub const fn is_unset(self) -> bool {
110        matches!(
111            self,
112            Self::UnsetScalar { .. } | Self::UnsetCollection { .. }
113        )
114    }
115
116    /// Reports whether a concrete collection is empty.
117    #[must_use]
118    #[inline(always)]
119    pub const fn is_empty_collection(self) -> bool {
120        matches!(
121            self,
122            Self::EmptyCollection { .. }
123                | Self::EmptyCollectionConversion { .. }
124        )
125    }
126
127    /// Reports whether the missing value came from a conversion.
128    #[must_use]
129    #[inline(always)]
130    pub const fn is_conversion(self) -> bool {
131        matches!(
132            self,
133            Self::Conversion { .. }
134                | Self::CollectionItem { .. }
135                | Self::EmptyCollectionConversion { .. }
136        )
137    }
138
139    /// Reports whether conversion APIs may use a caller-provided fallback.
140    #[cfg(feature = "converter")]
141    #[must_use]
142    #[inline(always)]
143    pub(crate) const fn is_defaultable_for_conversion(self) -> bool {
144        self.is_unset() || matches!(self, Self::Conversion { .. })
145    }
146}
147
148impl fmt::Display for ValueMissing {
149    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
150        match self {
151            Self::UnsetScalar { data_type } => {
152                write!(formatter, "unset scalar with declared type {data_type}")
153            }
154            Self::UnsetCollection { data_type } => {
155                write!(
156                    formatter,
157                    "unset collection with declared type {data_type}"
158                )
159            }
160            Self::EmptyCollection { data_type } => {
161                write!(
162                    formatter,
163                    "empty collection with element type {data_type}"
164                )
165            }
166            Self::Conversion { from, to } => {
167                write!(
168                    formatter,
169                    "conversion from {from} to {to} produced no value"
170                )
171            }
172            Self::CollectionItem {
173                source_index,
174                from,
175                to,
176            } => write!(
177                formatter,
178                "collection item at index {source_index} conversion from {from} to {to} produced no value"
179            ),
180            Self::EmptyCollectionConversion { to } => write!(
181                formatter,
182                "empty collection conversion to {to} produced no value"
183            ),
184        }
185    }
186}