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`, etc.)
14//! while dispatch traits are implemented in dedicated `multi_values_*` modules.
15
16use qubit_datatype::{
17    DataConversionError,
18    DataConversionOptions,
19    DataConvertTo,
20    DataConverter,
21    DataConverters,
22    DataListConversionError,
23    DataType,
24    ScalarStringDataConverters,
25};
26
27use crate::Value;
28use crate::value_error::{
29    ValueError,
30    ValueResult,
31};
32
33use super::multi_values::MultiValues;
34
35// ============================================================================
36// Inherent conversion APIs and `Value` interop
37// ============================================================================
38
39/// Maps a shared single-value conversion error into `ValueError`.
40///
41/// # Parameters
42///
43/// * `error` - Error returned by `DataConverter`.
44///
45/// # Returns
46///
47/// Returns the corresponding `ValueError` variant.
48fn map_data_conversion_error(error: DataConversionError) -> ValueError {
49    match error {
50        DataConversionError::NoValue => ValueError::NoValue,
51        DataConversionError::ConversionFailed { from, to } => {
52            ValueError::ConversionFailed { from, to }
53        }
54        DataConversionError::ConversionError(message) => ValueError::ConversionError(message),
55        DataConversionError::JsonSerializationError(message) => {
56            ValueError::JsonSerializationError(message)
57        }
58        DataConversionError::JsonDeserializationError(message) => {
59            ValueError::JsonDeserializationError(message)
60        }
61    }
62}
63
64/// Maps a shared batch conversion error into `ValueError`.
65///
66/// # Parameters
67///
68/// * `error` - Error returned by `DataConverters`.
69///
70/// # Returns
71///
72/// Returns a `ValueError::ConversionError` whose message includes the failing
73/// source element index and the underlying conversion error.
74#[inline]
75fn map_data_list_conversion_error(error: DataListConversionError) -> ValueError {
76    let source = map_data_conversion_error(error.source);
77    ValueError::ConversionError(format!(
78        "Cannot convert value at index {}: {}",
79        error.index, source
80    ))
81}
82
83/// Converts the first item from a batch converter using conversion options.
84///
85/// # Type Parameters
86///
87/// * `T` - Target type.
88/// * `I` - Iterator type wrapped by `DataConverters`.
89///
90/// # Parameters
91///
92/// * `values` - Batch converter containing source values.
93/// * `options` - Conversion options forwarded to `qubit_datatype`.
94///
95/// # Returns
96///
97/// Returns the converted first value.
98///
99/// # Errors
100///
101/// Returns `ValueError::NoValue` for empty sources or the mapped single-value
102/// conversion error for an invalid first source value.
103#[inline]
104fn convert_first_with<'a, T, I>(
105    values: DataConverters<'a, I>,
106    options: &DataConversionOptions,
107) -> ValueResult<T>
108where
109    DataConverter<'a>: DataConvertTo<T>,
110    I: Iterator,
111    I::Item: Into<DataConverter<'a>>,
112{
113    values
114        .to_first_with(options)
115        .map_err(map_data_conversion_error)
116}
117
118/// Converts every item from a batch converter using conversion options.
119///
120/// # Type Parameters
121///
122/// * `T` - Target element type.
123/// * `I` - Iterator type wrapped by `DataConverters`.
124///
125/// # Parameters
126///
127/// * `values` - Batch converter containing source values.
128/// * `options` - Conversion options forwarded to `qubit_datatype`.
129///
130/// # Returns
131///
132/// Returns converted values in the original order.
133///
134/// # Errors
135///
136/// Returns a mapped batch conversion error containing the failing source index.
137#[inline]
138fn convert_values_with<'a, T, I>(
139    values: DataConverters<'a, I>,
140    options: &DataConversionOptions,
141) -> ValueResult<Vec<T>>
142where
143    DataConverter<'a>: DataConvertTo<T>,
144    I: Iterator,
145    I::Item: Into<DataConverter<'a>>,
146{
147    values
148        .to_vec_with(options)
149        .map_err(map_data_list_conversion_error)
150}
151
152impl MultiValues {
153    /// Converts the first stored value to `T`.
154    ///
155    /// Unlike [`Self::get_first`], this method uses shared `DataConverter`
156    /// conversion rules instead of strict type matching. For example, a stored
157    /// `String("1")` can be converted to `bool`.
158    ///
159    /// # Type Parameters
160    ///
161    /// * `T` - Target type.
162    ///
163    /// # Returns
164    ///
165    /// The converted first value.
166    ///
167    /// # Errors
168    ///
169    /// Returns [`ValueError::NoValue`] when no value is stored, or a conversion
170    /// error when the first value cannot be converted to `T`.
171    #[inline]
172    pub fn to<T>(&self) -> ValueResult<T>
173    where
174        for<'a> DataConverter<'a>: DataConvertTo<T>,
175    {
176        self.to_with(&DataConversionOptions::default())
177    }
178
179    /// Converts the first stored value to `T` using conversion options.
180    ///
181    /// A `MultiValues::String` containing exactly one string is treated as a
182    /// scalar string source, so collection options can split it before taking
183    /// the first converted item. Multiple stored string values are treated as
184    /// an already-materialized list and are converted element by element.
185    ///
186    /// # Type Parameters
187    ///
188    /// * `T` - Target type.
189    ///
190    /// # Parameters
191    ///
192    /// * `options` - Conversion options forwarded to `qubit_datatype`.
193    ///
194    /// # Returns
195    ///
196    /// The converted first value.
197    ///
198    /// # Errors
199    ///
200    /// Returns [`ValueError::NoValue`] when no value is stored, or a conversion
201    /// error when the first value cannot be converted to `T`.
202    #[inline]
203    pub fn to_with<T>(&self, options: &DataConversionOptions) -> ValueResult<T>
204    where
205        for<'a> DataConverter<'a>: DataConvertTo<T>,
206    {
207        match self {
208            MultiValues::Empty(_) => Err(ValueError::NoValue),
209            MultiValues::Bool(v) => convert_first_with(DataConverters::from(v), options),
210            MultiValues::Char(v) => convert_first_with(DataConverters::from(v), options),
211            MultiValues::Int8(v) => convert_first_with(DataConverters::from(v), options),
212            MultiValues::Int16(v) => convert_first_with(DataConverters::from(v), options),
213            MultiValues::Int32(v) => convert_first_with(DataConverters::from(v), options),
214            MultiValues::Int64(v) => convert_first_with(DataConverters::from(v), options),
215            MultiValues::Int128(v) => convert_first_with(DataConverters::from(v), options),
216            MultiValues::UInt8(v) => convert_first_with(DataConverters::from(v), options),
217            MultiValues::UInt16(v) => convert_first_with(DataConverters::from(v), options),
218            MultiValues::UInt32(v) => convert_first_with(DataConverters::from(v), options),
219            MultiValues::UInt64(v) => convert_first_with(DataConverters::from(v), options),
220            MultiValues::UInt128(v) => convert_first_with(DataConverters::from(v), options),
221            MultiValues::IntSize(v) => convert_first_with(DataConverters::from(v), options),
222            MultiValues::UIntSize(v) => convert_first_with(DataConverters::from(v), options),
223            MultiValues::Float32(v) => convert_first_with(DataConverters::from(v), options),
224            MultiValues::Float64(v) => convert_first_with(DataConverters::from(v), options),
225            MultiValues::BigInteger(v) => convert_first_with(DataConverters::from(v), options),
226            MultiValues::BigDecimal(v) => convert_first_with(DataConverters::from(v), options),
227            MultiValues::String(v) if v.len() == 1 => {
228                ScalarStringDataConverters::from(v[0].as_str())
229                    .to_first_with(options)
230                    .map_err(map_data_conversion_error)
231            }
232            MultiValues::String(v) => convert_first_with(DataConverters::from(v), options),
233            MultiValues::Date(v) => convert_first_with(DataConverters::from(v), options),
234            MultiValues::Time(v) => convert_first_with(DataConverters::from(v), options),
235            MultiValues::DateTime(v) => convert_first_with(DataConverters::from(v), options),
236            MultiValues::Instant(v) => convert_first_with(DataConverters::from(v), options),
237            MultiValues::Duration(v) => convert_first_with(DataConverters::from(v), options),
238            MultiValues::Url(v) => convert_first_with(DataConverters::from(v), options),
239            MultiValues::StringMap(v) => convert_first_with(DataConverters::from(v), options),
240            MultiValues::Json(v) => convert_first_with(DataConverters::from(v), options),
241        }
242    }
243
244    /// Converts all stored values to `T`.
245    ///
246    /// Unlike [`Self::get`], this method uses shared `DataConverter` conversion
247    /// rules for every element instead of strict type matching. Empty values
248    /// return an empty vector.
249    ///
250    /// # Type Parameters
251    ///
252    /// * `T` - Target element type.
253    ///
254    /// # Returns
255    ///
256    /// A vector containing all converted values in the original order.
257    ///
258    /// # Errors
259    ///
260    /// Returns the first conversion error encountered while converting an
261    /// element.
262    pub fn to_list<T>(&self) -> ValueResult<Vec<T>>
263    where
264        for<'a> DataConverter<'a>: DataConvertTo<T>,
265    {
266        self.to_list_with(&DataConversionOptions::default())
267    }
268
269    /// Converts all stored values to `T` using conversion options.
270    ///
271    /// A `MultiValues::String` containing exactly one string is treated as a
272    /// scalar string source, so collection options can split it into items.
273    /// Multiple stored string values are treated as an already-materialized
274    /// list and are converted element by element.
275    ///
276    /// # Type Parameters
277    ///
278    /// * `T` - Target element type.
279    ///
280    /// # Parameters
281    ///
282    /// * `options` - Conversion options forwarded to `qubit_datatype`.
283    ///
284    /// # Returns
285    ///
286    /// A vector containing all converted values in the original order.
287    ///
288    /// # Errors
289    ///
290    /// Returns the first conversion error encountered while converting an
291    /// element.
292    pub fn to_list_with<T>(&self, options: &DataConversionOptions) -> ValueResult<Vec<T>>
293    where
294        for<'a> DataConverter<'a>: DataConvertTo<T>,
295    {
296        match self {
297            MultiValues::Empty(_) => Ok(Vec::new()),
298            MultiValues::Bool(v) => convert_values_with(DataConverters::from(v), options),
299            MultiValues::Char(v) => convert_values_with(DataConverters::from(v), options),
300            MultiValues::Int8(v) => convert_values_with(DataConverters::from(v), options),
301            MultiValues::Int16(v) => convert_values_with(DataConverters::from(v), options),
302            MultiValues::Int32(v) => convert_values_with(DataConverters::from(v), options),
303            MultiValues::Int64(v) => convert_values_with(DataConverters::from(v), options),
304            MultiValues::Int128(v) => convert_values_with(DataConverters::from(v), options),
305            MultiValues::UInt8(v) => convert_values_with(DataConverters::from(v), options),
306            MultiValues::UInt16(v) => convert_values_with(DataConverters::from(v), options),
307            MultiValues::UInt32(v) => convert_values_with(DataConverters::from(v), options),
308            MultiValues::UInt64(v) => convert_values_with(DataConverters::from(v), options),
309            MultiValues::UInt128(v) => convert_values_with(DataConverters::from(v), options),
310            MultiValues::IntSize(v) => convert_values_with(DataConverters::from(v), options),
311            MultiValues::UIntSize(v) => convert_values_with(DataConverters::from(v), options),
312            MultiValues::Float32(v) => convert_values_with(DataConverters::from(v), options),
313            MultiValues::Float64(v) => convert_values_with(DataConverters::from(v), options),
314            MultiValues::BigInteger(v) => convert_values_with(DataConverters::from(v), options),
315            MultiValues::BigDecimal(v) => convert_values_with(DataConverters::from(v), options),
316            MultiValues::String(v) if v.len() == 1 => {
317                ScalarStringDataConverters::from(v[0].as_str())
318                    .to_vec_with(options)
319                    .map_err(map_data_list_conversion_error)
320            }
321            MultiValues::String(v) => convert_values_with(DataConverters::from(v), options),
322            MultiValues::Date(v) => convert_values_with(DataConverters::from(v), options),
323            MultiValues::Time(v) => convert_values_with(DataConverters::from(v), options),
324            MultiValues::DateTime(v) => convert_values_with(DataConverters::from(v), options),
325            MultiValues::Instant(v) => convert_values_with(DataConverters::from(v), options),
326            MultiValues::Duration(v) => convert_values_with(DataConverters::from(v), options),
327            MultiValues::Url(v) => convert_values_with(DataConverters::from(v), options),
328            MultiValues::StringMap(v) => convert_values_with(DataConverters::from(v), options),
329            MultiValues::Json(v) => convert_values_with(DataConverters::from(v), options),
330        }
331    }
332
333    /// Convert to a single [`Value`] by taking the first element.
334    ///
335    /// If there is no element, returns `Value::Empty(self.data_type())`.
336    ///
337    /// # Returns
338    ///
339    /// Returns the first element wrapped as [`Value`], or an empty value
340    /// preserving the current data type.
341    pub fn to_value(&self) -> Value {
342        match self {
343            MultiValues::Empty(dt) => Value::Empty(*dt),
344            MultiValues::Bool(v) => v
345                .first()
346                .copied()
347                .map(Value::Bool)
348                .unwrap_or(Value::Empty(DataType::Bool)),
349            MultiValues::Char(v) => v
350                .first()
351                .copied()
352                .map(Value::Char)
353                .unwrap_or(Value::Empty(DataType::Char)),
354            MultiValues::Int8(v) => v
355                .first()
356                .copied()
357                .map(Value::Int8)
358                .unwrap_or(Value::Empty(DataType::Int8)),
359            MultiValues::Int16(v) => v
360                .first()
361                .copied()
362                .map(Value::Int16)
363                .unwrap_or(Value::Empty(DataType::Int16)),
364            MultiValues::Int32(v) => v
365                .first()
366                .copied()
367                .map(Value::Int32)
368                .unwrap_or(Value::Empty(DataType::Int32)),
369            MultiValues::Int64(v) => v
370                .first()
371                .copied()
372                .map(Value::Int64)
373                .unwrap_or(Value::Empty(DataType::Int64)),
374            MultiValues::Int128(v) => v
375                .first()
376                .copied()
377                .map(Value::Int128)
378                .unwrap_or(Value::Empty(DataType::Int128)),
379            MultiValues::UInt8(v) => v
380                .first()
381                .copied()
382                .map(Value::UInt8)
383                .unwrap_or(Value::Empty(DataType::UInt8)),
384            MultiValues::UInt16(v) => v
385                .first()
386                .copied()
387                .map(Value::UInt16)
388                .unwrap_or(Value::Empty(DataType::UInt16)),
389            MultiValues::UInt32(v) => v
390                .first()
391                .copied()
392                .map(Value::UInt32)
393                .unwrap_or(Value::Empty(DataType::UInt32)),
394            MultiValues::UInt64(v) => v
395                .first()
396                .copied()
397                .map(Value::UInt64)
398                .unwrap_or(Value::Empty(DataType::UInt64)),
399            MultiValues::UInt128(v) => v
400                .first()
401                .copied()
402                .map(Value::UInt128)
403                .unwrap_or(Value::Empty(DataType::UInt128)),
404            MultiValues::IntSize(v) => v
405                .first()
406                .copied()
407                .map(Value::IntSize)
408                .unwrap_or(Value::Empty(DataType::IntSize)),
409            MultiValues::UIntSize(v) => v
410                .first()
411                .copied()
412                .map(Value::UIntSize)
413                .unwrap_or(Value::Empty(DataType::UIntSize)),
414            MultiValues::Float32(v) => v
415                .first()
416                .copied()
417                .map(Value::Float32)
418                .unwrap_or(Value::Empty(DataType::Float32)),
419            MultiValues::Float64(v) => v
420                .first()
421                .copied()
422                .map(Value::Float64)
423                .unwrap_or(Value::Empty(DataType::Float64)),
424            MultiValues::BigInteger(v) => v
425                .first()
426                .cloned()
427                .map(Value::BigInteger)
428                .unwrap_or(Value::Empty(DataType::BigInteger)),
429            MultiValues::BigDecimal(v) => v
430                .first()
431                .cloned()
432                .map(Value::BigDecimal)
433                .unwrap_or(Value::Empty(DataType::BigDecimal)),
434            MultiValues::String(v) => v
435                .first()
436                .cloned()
437                .map(Value::String)
438                .unwrap_or(Value::Empty(DataType::String)),
439            MultiValues::Date(v) => v
440                .first()
441                .copied()
442                .map(Value::Date)
443                .unwrap_or(Value::Empty(DataType::Date)),
444            MultiValues::Time(v) => v
445                .first()
446                .copied()
447                .map(Value::Time)
448                .unwrap_or(Value::Empty(DataType::Time)),
449            MultiValues::DateTime(v) => v
450                .first()
451                .copied()
452                .map(Value::DateTime)
453                .unwrap_or(Value::Empty(DataType::DateTime)),
454            MultiValues::Instant(v) => v
455                .first()
456                .copied()
457                .map(Value::Instant)
458                .unwrap_or(Value::Empty(DataType::Instant)),
459            MultiValues::Duration(v) => v
460                .first()
461                .copied()
462                .map(Value::Duration)
463                .unwrap_or(Value::Empty(DataType::Duration)),
464            MultiValues::Url(v) => v
465                .first()
466                .cloned()
467                .map(Value::Url)
468                .unwrap_or(Value::Empty(DataType::Url)),
469            MultiValues::StringMap(v) => v
470                .first()
471                .cloned()
472                .map(Value::StringMap)
473                .unwrap_or(Value::Empty(DataType::StringMap)),
474            MultiValues::Json(v) => v
475                .first()
476                .cloned()
477                .map(Value::Json)
478                .unwrap_or(Value::Empty(DataType::Json)),
479        }
480    }
481
482    /// Merge another multiple values
483    ///
484    /// Append all values from another multiple values to the current multiple values
485    ///
486    /// # Parameters
487    ///
488    /// * `other` - The multiple values to merge
489    ///
490    /// # Returns
491    ///
492    /// If types match, returns `Ok(())`; otherwise returns an error
493    ///
494    /// # Example
495    ///
496    /// ```rust
497    /// use qubit_value::MultiValues;
498    ///
499    /// let mut a = MultiValues::Int32(vec![1, 2]);
500    /// let b = MultiValues::Int32(vec![3, 4]);
501    /// a.merge(&b).unwrap();
502    /// assert_eq!(a.get_int32s().unwrap(), &[1, 2, 3, 4]);
503    /// ```
504    pub fn merge(&mut self, other: &MultiValues) -> ValueResult<()> {
505        if self.data_type() != other.data_type() {
506            return Err(ValueError::TypeMismatch {
507                expected: self.data_type(),
508                actual: other.data_type(),
509            });
510        }
511        if other.count() == 0 {
512            return Ok(());
513        }
514
515        match (self, other) {
516            (MultiValues::Bool(v), MultiValues::Bool(o)) => v.extend_from_slice(o),
517            (MultiValues::Char(v), MultiValues::Char(o)) => v.extend_from_slice(o),
518            (MultiValues::Int8(v), MultiValues::Int8(o)) => v.extend_from_slice(o),
519            (MultiValues::Int16(v), MultiValues::Int16(o)) => v.extend_from_slice(o),
520            (MultiValues::Int32(v), MultiValues::Int32(o)) => v.extend_from_slice(o),
521            (MultiValues::Int64(v), MultiValues::Int64(o)) => v.extend_from_slice(o),
522            (MultiValues::Int128(v), MultiValues::Int128(o)) => v.extend_from_slice(o),
523            (MultiValues::UInt8(v), MultiValues::UInt8(o)) => v.extend_from_slice(o),
524            (MultiValues::UInt16(v), MultiValues::UInt16(o)) => v.extend_from_slice(o),
525            (MultiValues::UInt32(v), MultiValues::UInt32(o)) => v.extend_from_slice(o),
526            (MultiValues::UInt64(v), MultiValues::UInt64(o)) => v.extend_from_slice(o),
527            (MultiValues::UInt128(v), MultiValues::UInt128(o)) => v.extend_from_slice(o),
528            (MultiValues::Float32(v), MultiValues::Float32(o)) => v.extend_from_slice(o),
529            (MultiValues::Float64(v), MultiValues::Float64(o)) => v.extend_from_slice(o),
530            (MultiValues::String(v), MultiValues::String(o)) => v.extend_from_slice(o),
531            (MultiValues::Date(v), MultiValues::Date(o)) => v.extend_from_slice(o),
532            (MultiValues::Time(v), MultiValues::Time(o)) => v.extend_from_slice(o),
533            (MultiValues::DateTime(v), MultiValues::DateTime(o)) => v.extend_from_slice(o),
534            (MultiValues::Instant(v), MultiValues::Instant(o)) => v.extend_from_slice(o),
535            (MultiValues::BigInteger(v), MultiValues::BigInteger(o)) => v.extend_from_slice(o),
536            (MultiValues::BigDecimal(v), MultiValues::BigDecimal(o)) => v.extend_from_slice(o),
537            (MultiValues::IntSize(v), MultiValues::IntSize(o)) => v.extend_from_slice(o),
538            (MultiValues::UIntSize(v), MultiValues::UIntSize(o)) => v.extend_from_slice(o),
539            (MultiValues::Duration(v), MultiValues::Duration(o)) => v.extend_from_slice(o),
540            (MultiValues::Url(v), MultiValues::Url(o)) => v.extend_from_slice(o),
541            (MultiValues::StringMap(v), MultiValues::StringMap(o)) => v.extend(o.iter().cloned()),
542            (MultiValues::Json(v), MultiValues::Json(o)) => v.extend(o.iter().cloned()),
543            (slot @ MultiValues::Empty(_), other_values) => *slot = other_values.clone(),
544            _ => unreachable!(),
545        }
546
547        Ok(())
548    }
549}
550
551impl Default for MultiValues {
552    #[inline]
553    fn default() -> Self {
554        MultiValues::Empty(DataType::String)
555    }
556}
557
558impl From<Value> for MultiValues {
559    fn from(value: Value) -> Self {
560        match value {
561            Value::Empty(dt) => MultiValues::Empty(dt),
562            Value::Bool(v) => MultiValues::Bool(vec![v]),
563            Value::Char(v) => MultiValues::Char(vec![v]),
564            Value::Int8(v) => MultiValues::Int8(vec![v]),
565            Value::Int16(v) => MultiValues::Int16(vec![v]),
566            Value::Int32(v) => MultiValues::Int32(vec![v]),
567            Value::Int64(v) => MultiValues::Int64(vec![v]),
568            Value::Int128(v) => MultiValues::Int128(vec![v]),
569            Value::UInt8(v) => MultiValues::UInt8(vec![v]),
570            Value::UInt16(v) => MultiValues::UInt16(vec![v]),
571            Value::UInt32(v) => MultiValues::UInt32(vec![v]),
572            Value::UInt64(v) => MultiValues::UInt64(vec![v]),
573            Value::UInt128(v) => MultiValues::UInt128(vec![v]),
574            Value::Float32(v) => MultiValues::Float32(vec![v]),
575            Value::Float64(v) => MultiValues::Float64(vec![v]),
576            Value::String(v) => MultiValues::String(vec![v]),
577            Value::Date(v) => MultiValues::Date(vec![v]),
578            Value::Time(v) => MultiValues::Time(vec![v]),
579            Value::DateTime(v) => MultiValues::DateTime(vec![v]),
580            Value::Instant(v) => MultiValues::Instant(vec![v]),
581            Value::BigInteger(v) => MultiValues::BigInteger(vec![v]),
582            Value::BigDecimal(v) => MultiValues::BigDecimal(vec![v]),
583            Value::IntSize(v) => MultiValues::IntSize(vec![v]),
584            Value::UIntSize(v) => MultiValues::UIntSize(vec![v]),
585            Value::Duration(v) => MultiValues::Duration(vec![v]),
586            Value::Url(v) => MultiValues::Url(vec![v]),
587            Value::StringMap(v) => MultiValues::StringMap(vec![v]),
588            Value::Json(v) => MultiValues::Json(vec![v]),
589        }
590    }
591}