Skip to main content

qubit_value/multi_values/
multi_values_converters.rs

1/*******************************************************************************
2 *
3 *    Copyright (c) 2025 - 2026 Haixing Hu.
4 *
5 *    SPDX-License-Identifier: Apache-2.0
6 *
7 *    Licensed under the Apache License, Version 2.0.
8 *
9 ******************************************************************************/
10
11//! Internal conversion and interoperability implementations for `MultiValues`.
12//!
13//! This module keeps generic conversion logic (`to`, `to_list`, `to_value`,
14//! `merge`, etc.) and interoperability with [`Value`].
15
16use qubit_datatype::{
17    DataConversionOptions,
18    DataConvertTo,
19    DataConverter,
20    DataConverters,
21    DataListConversionError,
22    DataType,
23    ScalarStringDataConverters,
24};
25
26use crate::value_error::{
27    ValueError,
28    ValueResult,
29    map_data_conversion_error,
30};
31use crate::{
32    IntoValueDefault,
33    Value,
34};
35
36use super::multi_values::MultiValues;
37
38macro_rules! multi_values_to_value_match {
39    ($value:expr; $(($variant:ident, $type:ty, $data_type:expr)),+ $(,)?) => {
40        match $value {
41            MultiValues::Empty(data_type) => Value::Empty(*data_type),
42            $(
43                MultiValues::$variant(values) => values
44                    .first()
45                    .cloned()
46                    .map(Value::$variant)
47                    .unwrap_or(Value::Empty($data_type)),
48            )+
49        }
50    };
51}
52
53macro_rules! multi_values_merge_match {
54    ($left:expr, $right:expr; $(($variant:ident, $type:ty, $data_type:expr)),+ $(,)?) => {
55        match ($left, $right) {
56            $(
57                (MultiValues::$variant(values), MultiValues::$variant(other_values)) => {
58                    values.extend_from_slice(other_values)
59                }
60            )+
61            (slot @ MultiValues::Empty(_), other_values) => *slot = other_values.clone(),
62            _ => unreachable!(),
63        }
64    };
65}
66
67// ============================================================================
68// Inherent conversion APIs and `Value` interop
69// ============================================================================
70
71/// Maps a shared batch conversion error into `ValueError`.
72///
73/// # Parameters
74///
75/// * `error` - Error returned by `DataConverters`.
76///
77/// # Returns
78///
79/// Returns a `ValueError::ConversionError` whose message includes the failing
80/// source element index and the underlying conversion error.
81#[inline]
82fn map_data_list_conversion_error(error: DataListConversionError) -> ValueError {
83    let source = map_data_conversion_error(error.source);
84    ValueError::ConversionError(format!("Cannot convert value at index {}: {}", error.index, source))
85}
86
87/// Converts the first item from a batch converter using conversion options.
88///
89/// # Type Parameters
90///
91/// * `T` - Target type.
92/// * `I` - Iterator type wrapped by `DataConverters`.
93///
94/// # Parameters
95///
96/// * `values` - Batch converter containing source values.
97/// * `options` - Conversion options forwarded to `qubit_datatype`.
98///
99/// # Returns
100///
101/// Returns the converted first value.
102///
103/// # Errors
104///
105/// Returns `ValueError::NoValue` for empty sources or the mapped single-value
106/// conversion error for an invalid first source value.
107#[inline]
108fn convert_first_with<'a, T, I>(values: DataConverters<'a, I>, options: &DataConversionOptions) -> ValueResult<T>
109where
110    DataConverter<'a>: DataConvertTo<T>,
111    I: Iterator,
112    I::Item: Into<DataConverter<'a>>,
113{
114    values.to_first_with(options).map_err(map_data_conversion_error)
115}
116
117/// Converts every item from a batch converter using conversion options.
118///
119/// # Type Parameters
120///
121/// * `T` - Target element type.
122/// * `I` - Iterator type wrapped by `DataConverters`.
123///
124/// # Parameters
125///
126/// * `values` - Batch converter containing source values.
127/// * `options` - Conversion options forwarded to `qubit_datatype`.
128///
129/// # Returns
130///
131/// Returns converted values in the original order.
132///
133/// # Errors
134///
135/// Returns a mapped batch conversion error containing the failing source index.
136#[inline]
137fn convert_values_with<'a, T, I>(values: DataConverters<'a, I>, options: &DataConversionOptions) -> ValueResult<Vec<T>>
138where
139    DataConverter<'a>: DataConvertTo<T>,
140    I: Iterator,
141    I::Item: Into<DataConverter<'a>>,
142{
143    values.to_vec_with(options).map_err(map_data_list_conversion_error)
144}
145
146impl MultiValues {
147    /// Converts the first stored value to `T`.
148    ///
149    /// Unlike [`Self::get_first`], this method uses shared `DataConverter`
150    /// conversion rules instead of strict type matching. For example, a stored
151    /// `String("1")` can be converted to `bool`.
152    ///
153    /// # Type Parameters
154    ///
155    /// * `T` - Target type.
156    ///
157    /// # Returns
158    ///
159    /// The converted first value.
160    ///
161    /// # Errors
162    ///
163    /// Returns [`ValueError::NoValue`] when no value is stored, or a conversion
164    /// error when the first value cannot be converted to `T`.
165    #[inline]
166    pub fn to<T>(&self) -> ValueResult<T>
167    where
168        for<'a> DataConverter<'a>: DataConvertTo<T>,
169    {
170        self.to_with(&DataConversionOptions::default())
171    }
172
173    /// Converts the first stored value to `T`, or returns `default` when no
174    /// value is stored.
175    #[inline]
176    pub fn to_or<T>(&self, default: impl IntoValueDefault<T>) -> ValueResult<T>
177    where
178        for<'a> DataConverter<'a>: DataConvertTo<T>,
179    {
180        match self.to() {
181            Err(ValueError::NoValue) => Ok(default.into_value_default()),
182            result => result,
183        }
184    }
185
186    /// Converts the first stored value to `T` using conversion options.
187    ///
188    /// A `MultiValues::String` containing exactly one string is treated as a
189    /// scalar string source, so collection options can split it before taking
190    /// the first converted item. Multiple stored string values are treated as
191    /// an already-materialized list and are converted element by element.
192    ///
193    /// # Type Parameters
194    ///
195    /// * `T` - Target type.
196    ///
197    /// # Parameters
198    ///
199    /// * `options` - Conversion options forwarded to `qubit_datatype`.
200    ///
201    /// # Returns
202    ///
203    /// The converted first value.
204    ///
205    /// # Errors
206    ///
207    /// Returns [`ValueError::NoValue`] when no value is stored, or a conversion
208    /// error when the first value cannot be converted to `T`.
209    #[inline]
210    pub fn to_with<T>(&self, options: &DataConversionOptions) -> ValueResult<T>
211    where
212        for<'a> DataConverter<'a>: DataConvertTo<T>,
213    {
214        match self {
215            MultiValues::Empty(_) => Err(ValueError::NoValue),
216            MultiValues::Bool(v) => convert_first_with(DataConverters::from(v), options),
217            MultiValues::Char(v) => convert_first_with(DataConverters::from(v), options),
218            MultiValues::Int8(v) => convert_first_with(DataConverters::from(v), options),
219            MultiValues::Int16(v) => convert_first_with(DataConverters::from(v), options),
220            MultiValues::Int32(v) => convert_first_with(DataConverters::from(v), options),
221            MultiValues::Int64(v) => convert_first_with(DataConverters::from(v), options),
222            MultiValues::Int128(v) => convert_first_with(DataConverters::from(v), options),
223            MultiValues::UInt8(v) => convert_first_with(DataConverters::from(v), options),
224            MultiValues::UInt16(v) => convert_first_with(DataConverters::from(v), options),
225            MultiValues::UInt32(v) => convert_first_with(DataConverters::from(v), options),
226            MultiValues::UInt64(v) => convert_first_with(DataConverters::from(v), options),
227            MultiValues::UInt128(v) => convert_first_with(DataConverters::from(v), options),
228            MultiValues::IntSize(v) => convert_first_with(DataConverters::from(v), options),
229            MultiValues::UIntSize(v) => convert_first_with(DataConverters::from(v), options),
230            MultiValues::Float32(v) => convert_first_with(DataConverters::from(v), options),
231            MultiValues::Float64(v) => convert_first_with(DataConverters::from(v), options),
232            MultiValues::BigInteger(v) => convert_first_with(DataConverters::from(v), options),
233            MultiValues::BigDecimal(v) => convert_first_with(DataConverters::from(v), options),
234            MultiValues::String(v) if v.len() == 1 => ScalarStringDataConverters::from(v[0].as_str())
235                .to_first_with(options)
236                .map_err(map_data_conversion_error),
237            MultiValues::String(v) => convert_first_with(DataConverters::from(v), options),
238            MultiValues::Date(v) => convert_first_with(DataConverters::from(v), options),
239            MultiValues::Time(v) => convert_first_with(DataConverters::from(v), options),
240            MultiValues::DateTime(v) => convert_first_with(DataConverters::from(v), options),
241            MultiValues::Instant(v) => convert_first_with(DataConverters::from(v), options),
242            MultiValues::Duration(v) => convert_first_with(DataConverters::from(v), options),
243            MultiValues::Url(v) => convert_first_with(DataConverters::from(v), options),
244            MultiValues::StringMap(v) => convert_first_with(DataConverters::from(v), options),
245            MultiValues::Json(v) => convert_first_with(DataConverters::from(v), options),
246        }
247    }
248
249    /// Converts the first stored value to `T` using conversion options, or
250    /// returns `default` when no value is stored.
251    #[inline]
252    pub fn to_or_with<T>(&self, default: impl IntoValueDefault<T>, options: &DataConversionOptions) -> ValueResult<T>
253    where
254        for<'a> DataConverter<'a>: DataConvertTo<T>,
255    {
256        match self.to_with(options) {
257            Err(ValueError::NoValue) => Ok(default.into_value_default()),
258            result => result,
259        }
260    }
261
262    /// Converts all stored values to `T`.
263    ///
264    /// Unlike [`Self::get`], this method uses shared `DataConverter` conversion
265    /// rules for every element instead of strict type matching. Empty values
266    /// return an empty vector.
267    ///
268    /// # Type Parameters
269    ///
270    /// * `T` - Target element type.
271    ///
272    /// # Returns
273    ///
274    /// A vector containing all converted values in the original order.
275    ///
276    /// # Errors
277    ///
278    /// Returns the first conversion error encountered while converting an
279    /// element.
280    pub fn to_list<T>(&self) -> ValueResult<Vec<T>>
281    where
282        for<'a> DataConverter<'a>: DataConvertTo<T>,
283    {
284        self.to_list_with(&DataConversionOptions::default())
285    }
286
287    /// Converts all stored values to `T`, or returns `default` when the
288    /// converted list is empty.
289    #[inline]
290    pub fn to_list_or<T>(&self, default: impl IntoValueDefault<Vec<T>>) -> ValueResult<Vec<T>>
291    where
292        for<'a> DataConverter<'a>: DataConvertTo<T>,
293    {
294        let values = self.to_list()?;
295        if values.is_empty() {
296            Ok(default.into_value_default())
297        } else {
298            Ok(values)
299        }
300    }
301
302    /// Converts all stored values to `T` using conversion options.
303    ///
304    /// A `MultiValues::String` containing exactly one string is treated as a
305    /// scalar string source, so collection options can split it into items.
306    /// Multiple stored string values are treated as an already-materialized
307    /// list and are converted element by element.
308    ///
309    /// # Type Parameters
310    ///
311    /// * `T` - Target element type.
312    ///
313    /// # Parameters
314    ///
315    /// * `options` - Conversion options forwarded to `qubit_datatype`.
316    ///
317    /// # Returns
318    ///
319    /// A vector containing all converted values in the original order.
320    ///
321    /// # Errors
322    ///
323    /// Returns the first conversion error encountered while converting an
324    /// element.
325    pub fn to_list_with<T>(&self, options: &DataConversionOptions) -> ValueResult<Vec<T>>
326    where
327        for<'a> DataConverter<'a>: DataConvertTo<T>,
328    {
329        match self {
330            MultiValues::Empty(_) => Ok(Vec::new()),
331            MultiValues::Bool(v) => convert_values_with(DataConverters::from(v), options),
332            MultiValues::Char(v) => convert_values_with(DataConverters::from(v), options),
333            MultiValues::Int8(v) => convert_values_with(DataConverters::from(v), options),
334            MultiValues::Int16(v) => convert_values_with(DataConverters::from(v), options),
335            MultiValues::Int32(v) => convert_values_with(DataConverters::from(v), options),
336            MultiValues::Int64(v) => convert_values_with(DataConverters::from(v), options),
337            MultiValues::Int128(v) => convert_values_with(DataConverters::from(v), options),
338            MultiValues::UInt8(v) => convert_values_with(DataConverters::from(v), options),
339            MultiValues::UInt16(v) => convert_values_with(DataConverters::from(v), options),
340            MultiValues::UInt32(v) => convert_values_with(DataConverters::from(v), options),
341            MultiValues::UInt64(v) => convert_values_with(DataConverters::from(v), options),
342            MultiValues::UInt128(v) => convert_values_with(DataConverters::from(v), options),
343            MultiValues::IntSize(v) => convert_values_with(DataConverters::from(v), options),
344            MultiValues::UIntSize(v) => convert_values_with(DataConverters::from(v), options),
345            MultiValues::Float32(v) => convert_values_with(DataConverters::from(v), options),
346            MultiValues::Float64(v) => convert_values_with(DataConverters::from(v), options),
347            MultiValues::BigInteger(v) => convert_values_with(DataConverters::from(v), options),
348            MultiValues::BigDecimal(v) => convert_values_with(DataConverters::from(v), options),
349            MultiValues::String(v) if v.len() == 1 => ScalarStringDataConverters::from(v[0].as_str())
350                .to_vec_with(options)
351                .map_err(map_data_list_conversion_error),
352            MultiValues::String(v) => convert_values_with(DataConverters::from(v), options),
353            MultiValues::Date(v) => convert_values_with(DataConverters::from(v), options),
354            MultiValues::Time(v) => convert_values_with(DataConverters::from(v), options),
355            MultiValues::DateTime(v) => convert_values_with(DataConverters::from(v), options),
356            MultiValues::Instant(v) => convert_values_with(DataConverters::from(v), options),
357            MultiValues::Duration(v) => convert_values_with(DataConverters::from(v), options),
358            MultiValues::Url(v) => convert_values_with(DataConverters::from(v), options),
359            MultiValues::StringMap(v) => convert_values_with(DataConverters::from(v), options),
360            MultiValues::Json(v) => convert_values_with(DataConverters::from(v), options),
361        }
362    }
363
364    /// Converts all stored values to `T` using conversion options, or returns
365    /// `default` when the converted list is empty.
366    #[inline]
367    pub fn to_list_or_with<T>(
368        &self,
369        default: impl IntoValueDefault<Vec<T>>,
370        options: &DataConversionOptions,
371    ) -> ValueResult<Vec<T>>
372    where
373        for<'a> DataConverter<'a>: DataConvertTo<T>,
374    {
375        let values = self.to_list_with(options)?;
376        if values.is_empty() {
377            Ok(default.into_value_default())
378        } else {
379            Ok(values)
380        }
381    }
382
383    /// Convert to a single [`Value`] by taking the first element.
384    ///
385    /// If there is no element, returns `Value::Empty(self.data_type())`.
386    ///
387    /// # Returns
388    ///
389    /// Returns the first element wrapped as [`Value`], or an empty value
390    /// preserving the current data type.
391    pub fn to_value(&self) -> Value {
392        for_each_multi_value_type!(multi_values_to_value_match, self)
393    }
394
395    /// Merge another multiple values
396    ///
397    /// Append all values from another multiple values to the current multiple values
398    ///
399    /// # Parameters
400    ///
401    /// * `other` - The multiple values to merge
402    ///
403    /// # Returns
404    ///
405    /// Returns `Ok(())` after appending all values from `other`.
406    ///
407    /// # Errors
408    ///
409    /// Returns [`ValueError::TypeMismatch`] when `other` has a different data
410    /// type from this container.
411    ///
412    /// # Example
413    ///
414    /// ```rust
415    /// use qubit_value::MultiValues;
416    ///
417    /// let mut a = MultiValues::Int32(vec![1, 2]);
418    /// let b = MultiValues::Int32(vec![3, 4]);
419    /// a.merge(&b).unwrap();
420    /// assert_eq!(a.get_int32s().unwrap(), &[1, 2, 3, 4]);
421    /// ```
422    pub fn merge(&mut self, other: &MultiValues) -> ValueResult<()> {
423        if self.data_type() != other.data_type() {
424            return Err(ValueError::TypeMismatch {
425                expected: self.data_type(),
426                actual: other.data_type(),
427            });
428        }
429        if other.count() == 0 {
430            return Ok(());
431        }
432
433        for_each_multi_value_type!(multi_values_merge_match, self, other);
434
435        Ok(())
436    }
437}
438
439impl Default for MultiValues {
440    #[inline]
441    fn default() -> Self {
442        MultiValues::Empty(DataType::String)
443    }
444}
445
446impl From<Value> for MultiValues {
447    fn from(value: Value) -> Self {
448        match value {
449            Value::Empty(dt) => MultiValues::Empty(dt),
450            Value::Bool(v) => MultiValues::Bool(vec![v]),
451            Value::Char(v) => MultiValues::Char(vec![v]),
452            Value::Int8(v) => MultiValues::Int8(vec![v]),
453            Value::Int16(v) => MultiValues::Int16(vec![v]),
454            Value::Int32(v) => MultiValues::Int32(vec![v]),
455            Value::Int64(v) => MultiValues::Int64(vec![v]),
456            Value::Int128(v) => MultiValues::Int128(vec![v]),
457            Value::UInt8(v) => MultiValues::UInt8(vec![v]),
458            Value::UInt16(v) => MultiValues::UInt16(vec![v]),
459            Value::UInt32(v) => MultiValues::UInt32(vec![v]),
460            Value::UInt64(v) => MultiValues::UInt64(vec![v]),
461            Value::UInt128(v) => MultiValues::UInt128(vec![v]),
462            Value::Float32(v) => MultiValues::Float32(vec![v]),
463            Value::Float64(v) => MultiValues::Float64(vec![v]),
464            Value::String(v) => MultiValues::String(vec![v]),
465            Value::Date(v) => MultiValues::Date(vec![v]),
466            Value::Time(v) => MultiValues::Time(vec![v]),
467            Value::DateTime(v) => MultiValues::DateTime(vec![v]),
468            Value::Instant(v) => MultiValues::Instant(vec![v]),
469            Value::BigInteger(v) => MultiValues::BigInteger(vec![v]),
470            Value::BigDecimal(v) => MultiValues::BigDecimal(vec![v]),
471            Value::IntSize(v) => MultiValues::IntSize(vec![v]),
472            Value::UIntSize(v) => MultiValues::UIntSize(vec![v]),
473            Value::Duration(v) => MultiValues::Duration(vec![v]),
474            Value::Url(v) => MultiValues::Url(vec![v]),
475            Value::StringMap(v) => MultiValues::StringMap(vec![v]),
476            Value::Json(v) => MultiValues::Json(vec![v]),
477        }
478    }
479}