Skip to main content

qubit_value/multi_values/
multi_values_getter.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
9//! `TryFrom<&MultiValues>` implementations for strict typed reads.
10
11use qubit_datatype::DataType;
12
13use super::MultiValuesRepr;
14use super::multi_values::MultiValues;
15use crate::ValueMissing;
16use crate::value_error::ValueError;
17use crate::value_error::ValueResult;
18
19/// Implements strict borrowed collection reads from the shared value table.
20macro_rules! impl_multi_values_try_from_table {
21    (
22        ;
23        $(
24            (
25                [$($cfg:meta),*],
26                $variant:ident,
27                $type:ty,
28                $data_type:expr,
29                $materialization:ident,
30                $json_class:ident,
31                $number_projection:ident,
32                $value_doc:literal,
33                $multi_doc:literal
34                $(, $_wire:tt)*
35            )
36        ),+ $(,)?
37    ) => {
38        $(
39            $(#[$cfg])*
40            $(#[cfg_attr(docsrs, doc($cfg))])*
41            impl TryFrom<&MultiValues> for $type {
42                /// Reports a missing value or an exact runtime type mismatch.
43                type Error = ValueError;
44
45                /// Copies or clones the first element without converting its runtime type.
46                ///
47                /// # Parameters
48                ///
49                /// - `values`: The collection to read without modifying its storage.
50                ///
51                /// # Returns
52                ///
53                /// An owned copy or clone of the first element.
54                ///
55                /// # Errors
56                ///
57                /// Returns a missing-value error for an empty or matching unset collection,
58                /// or a type-mismatch error when the runtime element type differs.
59                #[inline(always)]
60                fn try_from(values: &MultiValues) -> ValueResult<$type> {
61                    match &values.repr {
62                        MultiValuesRepr::$variant(values) => values
63                            .first()
64                            .map(|value| materialize_stored!($materialization, value))
65                            .ok_or(ValueError::Missing(ValueMissing::empty_collection($data_type, $data_type))),
66                        MultiValuesRepr::Unset(actual) if *actual == $data_type => {
67                            Err(ValueError::Missing(ValueMissing::unset_collection(*actual, *actual)))
68                        }
69                        _ => Err(ValueError::TypeMismatch {
70                            expected: $data_type,
71                            actual: values.data_type(),
72                        }),
73                    }
74                }
75            }
76
77            $(#[$cfg])*
78            $(#[cfg_attr(docsrs, doc($cfg))])*
79            impl TryFrom<&MultiValues> for Vec<$type> {
80                /// Reports a missing value or an exact runtime type mismatch.
81                type Error = ValueError;
82
83                /// Copies or clones all elements while preserving their order.
84                ///
85                /// # Parameters
86                ///
87                /// - `values`: The collection to read without modifying its storage.
88                ///
89                /// # Returns
90                ///
91                /// An owned vector, including an empty vector for matching empty storage.
92                ///
93                /// # Errors
94                ///
95                /// Returns a missing-value error for a matching unset collection,
96                /// or a type-mismatch error when the runtime element type differs.
97                #[inline(always)]
98                fn try_from(values: &MultiValues) -> ValueResult<Vec<$type>> {
99                    match &values.repr {
100                        MultiValuesRepr::$variant(values) => Ok(values
101                            .iter()
102                            .map(|value| materialize_stored!($materialization, value))
103                            .collect()),
104                        MultiValuesRepr::Unset(actual) if *actual == $data_type => {
105                            Err(ValueError::Missing(ValueMissing::unset_collection(*actual, *actual)))
106                        }
107                        _ => Err(ValueError::TypeMismatch {
108                            expected: $data_type,
109                            actual: values.data_type(),
110                        }),
111                    }
112                }
113            }
114        )+
115    };
116}
117
118for_each_value_type!(impl_multi_values_try_from_table);
119
120/// Implements borrowed and consuming collection reads from the shared table.
121macro_rules! impl_multi_values_borrowed_and_owned_table {
122    (
123        ;
124        $(
125            (
126                [$($cfg:meta),*],
127                $variant:ident,
128                $type:ty,
129                $data_type:expr,
130                $materialization:ident,
131                $json_class:ident,
132                $number_projection:ident,
133                $value_doc:literal,
134                $multi_doc:literal
135                $(, $_wire:tt)*
136            )
137        ),+ $(,)?
138    ) => {
139        $(
140            $(#[$cfg])*
141            $(#[cfg_attr(docsrs, doc($cfg))])*
142            impl<'a> TryFrom<&'a MultiValues> for &'a $type {
143                /// Reports a missing value or an exact runtime type mismatch.
144                type Error = ValueError;
145
146                /// Borrows the first element without allocating or converting its runtime type.
147                ///
148                /// # Parameters
149                ///
150                /// - `values`: The collection whose storage is borrowed for the returned lifetime.
151                ///
152                /// # Returns
153                ///
154                /// A reference to the first element, valid for the input borrow.
155                ///
156                /// # Errors
157                ///
158                /// Returns a missing-value error for an empty or matching unset collection,
159                /// or a type-mismatch error when the runtime element type differs.
160                #[inline(always)]
161                fn try_from(values: &'a MultiValues) -> ValueResult<Self> {
162                    match &values.repr {
163                        MultiValuesRepr::$variant(values) => values.first().ok_or(
164                            ValueError::Missing(ValueMissing::empty_collection($data_type, $data_type)),
165                        ),
166                        MultiValuesRepr::Unset(actual) if *actual == $data_type => {
167                            Err(ValueError::Missing(ValueMissing::unset_collection(*actual, *actual)))
168                        }
169                        _ => Err(ValueError::TypeMismatch {
170                            expected: $data_type,
171                            actual: values.data_type(),
172                        }),
173                    }
174                }
175            }
176
177            $(#[$cfg])*
178            $(#[cfg_attr(docsrs, doc($cfg))])*
179            impl<'a> TryFrom<&'a MultiValues> for &'a [$type] {
180                /// Reports a missing value or an exact runtime type mismatch.
181                type Error = ValueError;
182
183                /// Borrows the complete element slice without allocating.
184                ///
185                /// # Parameters
186                ///
187                /// - `values`: The collection whose storage is borrowed for the returned lifetime.
188                ///
189                /// # Returns
190                ///
191                /// The stored slice, possibly empty, valid for the input borrow.
192                ///
193                /// # Errors
194                ///
195                /// Returns a missing-value error for a matching unset collection,
196                /// or a type-mismatch error when the runtime element type differs.
197                #[inline(always)]
198                fn try_from(values: &'a MultiValues) -> ValueResult<Self> {
199                    match &values.repr {
200                        MultiValuesRepr::$variant(values) => Ok(values.as_slice()),
201                        MultiValuesRepr::Unset(actual) if *actual == $data_type => {
202                            Err(ValueError::Missing(ValueMissing::unset_collection(*actual, *actual)))
203                        }
204                        _ => Err(ValueError::TypeMismatch {
205                            expected: $data_type,
206                            actual: values.data_type(),
207                        }),
208                    }
209                }
210            }
211
212            $(#[$cfg])*
213            $(#[cfg_attr(docsrs, doc($cfg))])*
214            impl TryFrom<MultiValues> for Vec<$type> {
215                /// Reports a missing value or an exact runtime type mismatch.
216                type Error = ValueError;
217
218                /// Moves the stored vector out of the collection without cloning its elements.
219                ///
220                /// # Parameters
221                ///
222                /// - `values`: The collection consumed by this conversion.
223                ///
224                /// # Returns
225                ///
226                /// The owned vector in its original order, possibly empty.
227                ///
228                /// # Errors
229                ///
230                /// Returns a missing-value error for a matching unset collection,
231                /// or a type-mismatch error when the runtime element type differs.
232                #[inline(always)]
233                fn try_from(values: MultiValues) -> ValueResult<Self> {
234                    match values.repr {
235                        MultiValuesRepr::$variant(values) => Ok(values),
236                        MultiValuesRepr::Unset(actual) if actual == $data_type => {
237                            Err(ValueError::Missing(ValueMissing::unset_collection(actual, actual)))
238                        }
239                        other => {
240                            let actual = MultiValues { repr: other }.data_type();
241                            Err(ValueError::TypeMismatch { expected: $data_type, actual })
242                        }
243                    }
244                }
245            }
246        )+
247    };
248}
249
250for_each_value_type!(impl_multi_values_borrowed_and_owned_table);
251
252impl<'a> TryFrom<&'a MultiValues> for &'a str {
253    /// Reports a missing string or an exact runtime type mismatch.
254    type Error = ValueError;
255
256    /// Borrows the first stored string as a slice without allocating.
257    ///
258    /// # Parameters
259    ///
260    /// - `values`: The collection borrowed for the returned slice's lifetime.
261    ///
262    /// # Returns
263    ///
264    /// The first string slice, valid for the input borrow.
265    ///
266    /// # Errors
267    ///
268    /// Returns a missing-value error for an empty or matching unset collection,
269    /// or a type-mismatch error when the runtime element type is not string.
270    #[inline(always)]
271    fn try_from(values: &'a MultiValues) -> ValueResult<Self> {
272        match &values.repr {
273            MultiValuesRepr::String(values) => {
274                values
275                    .first()
276                    .map(String::as_str)
277                    .ok_or(ValueError::Missing(ValueMissing::empty_collection(
278                        DataType::String,
279                        DataType::String,
280                    )))
281            }
282            MultiValuesRepr::Unset(actual) if *actual == DataType::String => Err(
283                ValueError::Missing(ValueMissing::unset_collection(*actual, *actual)),
284            ),
285            _ => Err(ValueError::TypeMismatch {
286                expected: DataType::String,
287                actual: values.data_type(),
288            }),
289        }
290    }
291}