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!(
85        "Cannot convert value at index {}: {}",
86        error.index, source
87    ))
88}
89
90/// Converts the first item from a batch converter using conversion options.
91///
92/// # Type Parameters
93///
94/// * `T` - Target type.
95/// * `I` - Iterator type wrapped by `DataConverters`.
96///
97/// # Parameters
98///
99/// * `values` - Batch converter containing source values.
100/// * `options` - Conversion options forwarded to `qubit_datatype`.
101///
102/// # Returns
103///
104/// Returns the converted first value.
105///
106/// # Errors
107///
108/// Returns `ValueError::NoValue` for empty sources or the mapped single-value
109/// conversion error for an invalid first source value.
110#[inline]
111fn convert_first_with<'a, T, I>(
112    values: DataConverters<'a, I>,
113    options: &DataConversionOptions,
114) -> ValueResult<T>
115where
116    DataConverter<'a>: DataConvertTo<T>,
117    I: Iterator,
118    I::Item: Into<DataConverter<'a>>,
119{
120    values
121        .to_first_with(options)
122        .map_err(map_data_conversion_error)
123}
124
125/// Converts every item from a batch converter using conversion options.
126///
127/// # Type Parameters
128///
129/// * `T` - Target element type.
130/// * `I` - Iterator type wrapped by `DataConverters`.
131///
132/// # Parameters
133///
134/// * `values` - Batch converter containing source values.
135/// * `options` - Conversion options forwarded to `qubit_datatype`.
136///
137/// # Returns
138///
139/// Returns converted values in the original order.
140///
141/// # Errors
142///
143/// Returns a mapped batch conversion error containing the failing source index.
144#[inline]
145fn convert_values_with<'a, T, I>(
146    values: DataConverters<'a, I>,
147    options: &DataConversionOptions,
148) -> ValueResult<Vec<T>>
149where
150    DataConverter<'a>: DataConvertTo<T>,
151    I: Iterator,
152    I::Item: Into<DataConverter<'a>>,
153{
154    values
155        .to_vec_with(options)
156        .map_err(map_data_list_conversion_error)
157}
158
159impl MultiValues {
160    /// Converts the first stored value to `T`.
161    ///
162    /// Unlike [`Self::get_first`], this method uses shared `DataConverter`
163    /// conversion rules instead of strict type matching. For example, a stored
164    /// `String("1")` can be converted to `bool`.
165    ///
166    /// # Type Parameters
167    ///
168    /// * `T` - Target type.
169    ///
170    /// # Returns
171    ///
172    /// The converted first value.
173    ///
174    /// # Errors
175    ///
176    /// Returns [`ValueError::NoValue`] when no value is stored, or a conversion
177    /// error when the first value cannot be converted to `T`.
178    #[inline]
179    pub fn to<T>(&self) -> ValueResult<T>
180    where
181        for<'a> DataConverter<'a>: DataConvertTo<T>,
182    {
183        self.to_with(&DataConversionOptions::default())
184    }
185
186    /// Converts the first stored value to `T`, or returns `default` when no
187    /// value is stored.
188    #[inline]
189    pub fn to_or<T>(&self, default: impl IntoValueDefault<T>) -> ValueResult<T>
190    where
191        for<'a> DataConverter<'a>: DataConvertTo<T>,
192    {
193        match self.to() {
194            Err(ValueError::NoValue) => Ok(default.into_value_default()),
195            result => result,
196        }
197    }
198
199    /// Converts the first stored value to `T` using conversion options.
200    ///
201    /// A `MultiValues::String` containing exactly one string is treated as a
202    /// scalar string source, so collection options can split it before taking
203    /// the first converted item. Multiple stored string values are treated as
204    /// an already-materialized list and are converted element by element.
205    ///
206    /// # Type Parameters
207    ///
208    /// * `T` - Target type.
209    ///
210    /// # Parameters
211    ///
212    /// * `options` - Conversion options forwarded to `qubit_datatype`.
213    ///
214    /// # Returns
215    ///
216    /// The converted first value.
217    ///
218    /// # Errors
219    ///
220    /// Returns [`ValueError::NoValue`] when no value is stored, or a conversion
221    /// error when the first value cannot be converted to `T`.
222    #[inline]
223    pub fn to_with<T>(&self, options: &DataConversionOptions) -> ValueResult<T>
224    where
225        for<'a> DataConverter<'a>: DataConvertTo<T>,
226    {
227        match self {
228            MultiValues::Empty(_) => Err(ValueError::NoValue),
229            MultiValues::Bool(v) => convert_first_with(DataConverters::from(v), options),
230            MultiValues::Char(v) => convert_first_with(DataConverters::from(v), options),
231            MultiValues::Int8(v) => convert_first_with(DataConverters::from(v), options),
232            MultiValues::Int16(v) => convert_first_with(DataConverters::from(v), options),
233            MultiValues::Int32(v) => convert_first_with(DataConverters::from(v), options),
234            MultiValues::Int64(v) => convert_first_with(DataConverters::from(v), options),
235            MultiValues::Int128(v) => convert_first_with(DataConverters::from(v), options),
236            MultiValues::UInt8(v) => convert_first_with(DataConverters::from(v), options),
237            MultiValues::UInt16(v) => convert_first_with(DataConverters::from(v), options),
238            MultiValues::UInt32(v) => convert_first_with(DataConverters::from(v), options),
239            MultiValues::UInt64(v) => convert_first_with(DataConverters::from(v), options),
240            MultiValues::UInt128(v) => convert_first_with(DataConverters::from(v), options),
241            MultiValues::IntSize(v) => convert_first_with(DataConverters::from(v), options),
242            MultiValues::UIntSize(v) => convert_first_with(DataConverters::from(v), options),
243            MultiValues::Float32(v) => convert_first_with(DataConverters::from(v), options),
244            MultiValues::Float64(v) => convert_first_with(DataConverters::from(v), options),
245            MultiValues::BigInteger(v) => convert_first_with(DataConverters::from(v), options),
246            MultiValues::BigDecimal(v) => convert_first_with(DataConverters::from(v), options),
247            MultiValues::String(v) if v.len() == 1 => {
248                ScalarStringDataConverters::from(v[0].as_str())
249                    .to_first_with(options)
250                    .map_err(map_data_conversion_error)
251            }
252            MultiValues::String(v) => convert_first_with(DataConverters::from(v), options),
253            MultiValues::Date(v) => convert_first_with(DataConverters::from(v), options),
254            MultiValues::Time(v) => convert_first_with(DataConverters::from(v), options),
255            MultiValues::DateTime(v) => convert_first_with(DataConverters::from(v), options),
256            MultiValues::Instant(v) => convert_first_with(DataConverters::from(v), options),
257            MultiValues::Duration(v) => convert_first_with(DataConverters::from(v), options),
258            MultiValues::Url(v) => convert_first_with(DataConverters::from(v), options),
259            MultiValues::StringMap(v) => convert_first_with(DataConverters::from(v), options),
260            MultiValues::Json(v) => convert_first_with(DataConverters::from(v), options),
261        }
262    }
263
264    /// Converts the first stored value to `T` using conversion options, or
265    /// returns `default` when no value is stored.
266    #[inline]
267    pub fn to_or_with<T>(
268        &self,
269        default: impl IntoValueDefault<T>,
270        options: &DataConversionOptions,
271    ) -> ValueResult<T>
272    where
273        for<'a> DataConverter<'a>: DataConvertTo<T>,
274    {
275        match self.to_with(options) {
276            Err(ValueError::NoValue) => Ok(default.into_value_default()),
277            result => result,
278        }
279    }
280
281    /// Converts all stored values to `T`.
282    ///
283    /// Unlike [`Self::get`], this method uses shared `DataConverter` conversion
284    /// rules for every element instead of strict type matching. Empty values
285    /// return an empty vector.
286    ///
287    /// # Type Parameters
288    ///
289    /// * `T` - Target element type.
290    ///
291    /// # Returns
292    ///
293    /// A vector containing all converted values in the original order.
294    ///
295    /// # Errors
296    ///
297    /// Returns the first conversion error encountered while converting an
298    /// element.
299    pub fn to_list<T>(&self) -> ValueResult<Vec<T>>
300    where
301        for<'a> DataConverter<'a>: DataConvertTo<T>,
302    {
303        self.to_list_with(&DataConversionOptions::default())
304    }
305
306    /// Converts all stored values to `T`, or returns `default` when the
307    /// converted list is empty.
308    #[inline]
309    pub fn to_list_or<T>(&self, default: impl IntoValueDefault<Vec<T>>) -> ValueResult<Vec<T>>
310    where
311        for<'a> DataConverter<'a>: DataConvertTo<T>,
312    {
313        let values = self.to_list()?;
314        if values.is_empty() {
315            Ok(default.into_value_default())
316        } else {
317            Ok(values)
318        }
319    }
320
321    /// Converts all stored values to `T` using conversion options.
322    ///
323    /// A `MultiValues::String` containing exactly one string is treated as a
324    /// scalar string source, so collection options can split it into items.
325    /// Multiple stored string values are treated as an already-materialized
326    /// list and are converted element by element.
327    ///
328    /// # Type Parameters
329    ///
330    /// * `T` - Target element type.
331    ///
332    /// # Parameters
333    ///
334    /// * `options` - Conversion options forwarded to `qubit_datatype`.
335    ///
336    /// # Returns
337    ///
338    /// A vector containing all converted values in the original order.
339    ///
340    /// # Errors
341    ///
342    /// Returns the first conversion error encountered while converting an
343    /// element.
344    pub fn to_list_with<T>(&self, options: &DataConversionOptions) -> ValueResult<Vec<T>>
345    where
346        for<'a> DataConverter<'a>: DataConvertTo<T>,
347    {
348        match self {
349            MultiValues::Empty(_) => Ok(Vec::new()),
350            MultiValues::Bool(v) => convert_values_with(DataConverters::from(v), options),
351            MultiValues::Char(v) => convert_values_with(DataConverters::from(v), options),
352            MultiValues::Int8(v) => convert_values_with(DataConverters::from(v), options),
353            MultiValues::Int16(v) => convert_values_with(DataConverters::from(v), options),
354            MultiValues::Int32(v) => convert_values_with(DataConverters::from(v), options),
355            MultiValues::Int64(v) => convert_values_with(DataConverters::from(v), options),
356            MultiValues::Int128(v) => convert_values_with(DataConverters::from(v), options),
357            MultiValues::UInt8(v) => convert_values_with(DataConverters::from(v), options),
358            MultiValues::UInt16(v) => convert_values_with(DataConverters::from(v), options),
359            MultiValues::UInt32(v) => convert_values_with(DataConverters::from(v), options),
360            MultiValues::UInt64(v) => convert_values_with(DataConverters::from(v), options),
361            MultiValues::UInt128(v) => convert_values_with(DataConverters::from(v), options),
362            MultiValues::IntSize(v) => convert_values_with(DataConverters::from(v), options),
363            MultiValues::UIntSize(v) => convert_values_with(DataConverters::from(v), options),
364            MultiValues::Float32(v) => convert_values_with(DataConverters::from(v), options),
365            MultiValues::Float64(v) => convert_values_with(DataConverters::from(v), options),
366            MultiValues::BigInteger(v) => convert_values_with(DataConverters::from(v), options),
367            MultiValues::BigDecimal(v) => convert_values_with(DataConverters::from(v), options),
368            MultiValues::String(v) if v.len() == 1 => {
369                ScalarStringDataConverters::from(v[0].as_str())
370                    .to_vec_with(options)
371                    .map_err(map_data_list_conversion_error)
372            }
373            MultiValues::String(v) => convert_values_with(DataConverters::from(v), options),
374            MultiValues::Date(v) => convert_values_with(DataConverters::from(v), options),
375            MultiValues::Time(v) => convert_values_with(DataConverters::from(v), options),
376            MultiValues::DateTime(v) => convert_values_with(DataConverters::from(v), options),
377            MultiValues::Instant(v) => convert_values_with(DataConverters::from(v), options),
378            MultiValues::Duration(v) => convert_values_with(DataConverters::from(v), options),
379            MultiValues::Url(v) => convert_values_with(DataConverters::from(v), options),
380            MultiValues::StringMap(v) => convert_values_with(DataConverters::from(v), options),
381            MultiValues::Json(v) => convert_values_with(DataConverters::from(v), options),
382        }
383    }
384
385    /// Converts all stored values to `T` using conversion options, or returns
386    /// `default` when the converted list is empty.
387    #[inline]
388    pub fn to_list_or_with<T>(
389        &self,
390        default: impl IntoValueDefault<Vec<T>>,
391        options: &DataConversionOptions,
392    ) -> ValueResult<Vec<T>>
393    where
394        for<'a> DataConverter<'a>: DataConvertTo<T>,
395    {
396        let values = self.to_list_with(options)?;
397        if values.is_empty() {
398            Ok(default.into_value_default())
399        } else {
400            Ok(values)
401        }
402    }
403
404    /// Convert to a single [`Value`] by taking the first element.
405    ///
406    /// If there is no element, returns `Value::Empty(self.data_type())`.
407    ///
408    /// # Returns
409    ///
410    /// Returns the first element wrapped as [`Value`], or an empty value
411    /// preserving the current data type.
412    pub fn to_value(&self) -> Value {
413        for_each_multi_value_type!(multi_values_to_value_match, self)
414    }
415
416    /// Merge another multiple values
417    ///
418    /// Append all values from another multiple values to the current multiple values
419    ///
420    /// # Parameters
421    ///
422    /// * `other` - The multiple values to merge
423    ///
424    /// # Returns
425    ///
426    /// Returns `Ok(())` after appending all values from `other`.
427    ///
428    /// # Errors
429    ///
430    /// Returns [`ValueError::TypeMismatch`] when `other` has a different data
431    /// type from this container.
432    ///
433    /// # Example
434    ///
435    /// ```rust
436    /// use qubit_value::MultiValues;
437    ///
438    /// let mut a = MultiValues::Int32(vec![1, 2]);
439    /// let b = MultiValues::Int32(vec![3, 4]);
440    /// a.merge(&b).unwrap();
441    /// assert_eq!(a.get_int32s().unwrap(), &[1, 2, 3, 4]);
442    /// ```
443    pub fn merge(&mut self, other: &MultiValues) -> ValueResult<()> {
444        if self.data_type() != other.data_type() {
445            return Err(ValueError::TypeMismatch {
446                expected: self.data_type(),
447                actual: other.data_type(),
448            });
449        }
450        if other.count() == 0 {
451            return Ok(());
452        }
453
454        for_each_multi_value_type!(multi_values_merge_match, self, other);
455
456        Ok(())
457    }
458}
459
460impl Default for MultiValues {
461    #[inline]
462    fn default() -> Self {
463        MultiValues::Empty(DataType::String)
464    }
465}
466
467impl From<Value> for MultiValues {
468    fn from(value: Value) -> Self {
469        match value {
470            Value::Empty(dt) => MultiValues::Empty(dt),
471            Value::Bool(v) => MultiValues::Bool(vec![v]),
472            Value::Char(v) => MultiValues::Char(vec![v]),
473            Value::Int8(v) => MultiValues::Int8(vec![v]),
474            Value::Int16(v) => MultiValues::Int16(vec![v]),
475            Value::Int32(v) => MultiValues::Int32(vec![v]),
476            Value::Int64(v) => MultiValues::Int64(vec![v]),
477            Value::Int128(v) => MultiValues::Int128(vec![v]),
478            Value::UInt8(v) => MultiValues::UInt8(vec![v]),
479            Value::UInt16(v) => MultiValues::UInt16(vec![v]),
480            Value::UInt32(v) => MultiValues::UInt32(vec![v]),
481            Value::UInt64(v) => MultiValues::UInt64(vec![v]),
482            Value::UInt128(v) => MultiValues::UInt128(vec![v]),
483            Value::Float32(v) => MultiValues::Float32(vec![v]),
484            Value::Float64(v) => MultiValues::Float64(vec![v]),
485            Value::String(v) => MultiValues::String(vec![v]),
486            Value::Date(v) => MultiValues::Date(vec![v]),
487            Value::Time(v) => MultiValues::Time(vec![v]),
488            Value::DateTime(v) => MultiValues::DateTime(vec![v]),
489            Value::Instant(v) => MultiValues::Instant(vec![v]),
490            Value::BigInteger(v) => MultiValues::BigInteger(vec![v]),
491            Value::BigDecimal(v) => MultiValues::BigDecimal(vec![v]),
492            Value::IntSize(v) => MultiValues::IntSize(vec![v]),
493            Value::UIntSize(v) => MultiValues::UIntSize(vec![v]),
494            Value::Duration(v) => MultiValues::Duration(vec![v]),
495            Value::Url(v) => MultiValues::Url(vec![v]),
496            Value::StringMap(v) => MultiValues::StringMap(vec![v]),
497            Value::Json(v) => MultiValues::Json(vec![v]),
498        }
499    }
500}