Skip to main content

qubit_datatype/converter/
data_converter.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//! # Reusable Data Conversion
11//!
12//! Provides `DataConverter`, a lightweight source-value wrapper used to convert
13//! common runtime values to supported Rust target types.
14//!
15
16use std::borrow::Cow;
17use std::collections::HashMap;
18use std::fmt;
19use std::time::Duration;
20
21use bigdecimal::BigDecimal;
22use chrono::{
23    DateTime,
24    NaiveDate,
25    NaiveDateTime,
26    NaiveTime,
27    Utc,
28};
29use num_bigint::BigInt;
30use num_traits::ToPrimitive;
31use url::Url;
32
33use super::data_conversion_error::DataConversionError;
34use super::data_conversion_options::DataConversionOptions;
35use super::data_conversion_result::DataConversionResult;
36use super::data_convert_to::DataConvertTo;
37use super::duration_unit::DurationUnit;
38use crate::datatype::DataType;
39
40/// A lightweight wrapper for converting common data values.
41///
42/// `DataConverter` borrows source values when possible and owns them only when
43/// created from owned inputs such as `String`, `BigInt`, or `serde_json::Value`.
44/// It does not depend on higher-level value containers.
45///
46/// # Examples
47///
48/// ```
49/// use std::time::Duration;
50///
51/// use qubit_datatype::converter::{
52///     DataConversionResult,
53///     DataConverter,
54/// };
55///
56/// fn read_timeout() -> DataConversionResult<Duration> {
57///     DataConverter::from("1500000000ns").to::<Duration>()
58/// }
59///
60/// let timeout = read_timeout().expect("duration text should convert");
61/// assert_eq!(timeout, Duration::new(1, 500_000_000));
62///
63/// let port: u16 = DataConverter::from("8080")
64///     .to()
65///     .expect("port text should convert to u16");
66/// assert_eq!(port, 8080);
67///
68/// let enabled: bool = DataConverter::from("true")
69///     .to()
70///     .expect("boolean text should convert to bool");
71/// assert!(enabled);
72/// ```
73#[derive(Debug, Clone, PartialEq)]
74pub enum DataConverter<'a> {
75    /// Empty source with a known data type.
76    Empty(DataType),
77    /// Boolean source.
78    Bool(bool),
79    /// Character source.
80    Char(char),
81    /// 8-bit signed integer source.
82    Int8(i8),
83    /// 16-bit signed integer source.
84    Int16(i16),
85    /// 32-bit signed integer source.
86    Int32(i32),
87    /// 64-bit signed integer source.
88    Int64(i64),
89    /// 128-bit signed integer source.
90    Int128(i128),
91    /// 8-bit unsigned integer source.
92    UInt8(u8),
93    /// 16-bit unsigned integer source.
94    UInt16(u16),
95    /// 32-bit unsigned integer source.
96    UInt32(u32),
97    /// 64-bit unsigned integer source.
98    UInt64(u64),
99    /// 128-bit unsigned integer source.
100    UInt128(u128),
101    /// Platform-dependent signed integer source.
102    IntSize(isize),
103    /// Platform-dependent unsigned integer source.
104    UIntSize(usize),
105    /// 32-bit floating point source.
106    Float32(f32),
107    /// 64-bit floating point source.
108    Float64(f64),
109    /// Big integer source.
110    BigInteger(Cow<'a, BigInt>),
111    /// Big decimal source.
112    BigDecimal(Cow<'a, BigDecimal>),
113    /// String source.
114    String(Cow<'a, str>),
115    /// Date source.
116    Date(NaiveDate),
117    /// Time source.
118    Time(NaiveTime),
119    /// Date-time source.
120    DateTime(NaiveDateTime),
121    /// UTC instant source.
122    Instant(DateTime<Utc>),
123    /// Duration source.
124    Duration(Duration),
125    /// URL source.
126    Url(Cow<'a, Url>),
127    /// String map source.
128    StringMap(Cow<'a, HashMap<String, String>>),
129    /// JSON value source.
130    Json(Cow<'a, serde_json::Value>),
131}
132
133impl DataConverter<'_> {
134    /// Converts this source value to the requested target type.
135    ///
136    /// # Type Parameters
137    ///
138    /// * `T` - Target type to convert to.
139    ///
140    /// # Returns
141    ///
142    /// Returns the converted value when the conversion is supported and the
143    /// source value is valid for the target type.
144    ///
145    /// # Errors
146    ///
147    /// Returns [`DataConversionError::NoValue`] for empty sources,
148    /// [`DataConversionError::ConversionFailed`] for unsupported source-target
149    /// pairs, or a detailed conversion error for invalid source content.
150    #[inline]
151    pub fn to<T>(&self) -> DataConversionResult<T>
152    where
153        Self: DataConvertTo<T>,
154    {
155        self.to_with(&DataConversionOptions::default())
156    }
157
158    /// Converts this source value to the requested target type using options.
159    ///
160    /// # Type Parameters
161    ///
162    /// * `T` - Target type to convert to.
163    ///
164    /// # Parameters
165    ///
166    /// * `options` - Conversion options used for string, boolean, and
167    ///   collection-related parsing.
168    ///
169    /// # Returns
170    ///
171    /// Returns the converted value when the conversion is supported and the
172    /// source value is valid for the target type.
173    ///
174    /// # Errors
175    ///
176    /// Returns [`DataConversionError::NoValue`] for empty or option-defined
177    /// missing sources, [`DataConversionError::ConversionFailed`] for
178    /// unsupported source-target pairs, or a detailed conversion error for
179    /// invalid source content.
180    #[inline]
181    pub fn to_with<T>(&self, options: &DataConversionOptions) -> DataConversionResult<T>
182    where
183        Self: DataConvertTo<T>,
184    {
185        <Self as DataConvertTo<T>>::convert(self, options)
186    }
187
188    /// Returns the data type represented by this source value.
189    ///
190    /// # Returns
191    ///
192    /// Returns the [`DataType`] corresponding to this converter's source
193    /// variant. Empty sources return their preserved data type.
194    #[inline]
195    pub const fn data_type(&self) -> DataType {
196        match self {
197            DataConverter::Empty(data_type) => *data_type,
198            DataConverter::Bool(_) => DataType::Bool,
199            DataConverter::Char(_) => DataType::Char,
200            DataConverter::Int8(_) => DataType::Int8,
201            DataConverter::Int16(_) => DataType::Int16,
202            DataConverter::Int32(_) => DataType::Int32,
203            DataConverter::Int64(_) => DataType::Int64,
204            DataConverter::Int128(_) => DataType::Int128,
205            DataConverter::UInt8(_) => DataType::UInt8,
206            DataConverter::UInt16(_) => DataType::UInt16,
207            DataConverter::UInt32(_) => DataType::UInt32,
208            DataConverter::UInt64(_) => DataType::UInt64,
209            DataConverter::UInt128(_) => DataType::UInt128,
210            DataConverter::IntSize(_) => DataType::IntSize,
211            DataConverter::UIntSize(_) => DataType::UIntSize,
212            DataConverter::Float32(_) => DataType::Float32,
213            DataConverter::Float64(_) => DataType::Float64,
214            DataConverter::BigInteger(_) => DataType::BigInteger,
215            DataConverter::BigDecimal(_) => DataType::BigDecimal,
216            DataConverter::String(_) => DataType::String,
217            DataConverter::Date(_) => DataType::Date,
218            DataConverter::Time(_) => DataType::Time,
219            DataConverter::DateTime(_) => DataType::DateTime,
220            DataConverter::Instant(_) => DataType::Instant,
221            DataConverter::Duration(_) => DataType::Duration,
222            DataConverter::Url(_) => DataType::Url,
223            DataConverter::StringMap(_) => DataType::StringMap,
224            DataConverter::Json(_) => DataType::Json,
225        }
226    }
227
228    /// Builds a conversion-failed error for this source value and target type.
229    fn unsupported(&self, target_type: DataType) -> DataConversionError {
230        DataConversionError::ConversionFailed {
231            from: self.data_type(),
232            to: target_type,
233        }
234    }
235}
236
237macro_rules! impl_from_copy {
238    ($source_type:ty, $variant:ident) => {
239        impl<'a> From<$source_type> for DataConverter<'a> {
240            #[inline]
241            fn from(value: $source_type) -> Self {
242                DataConverter::$variant(value)
243            }
244        }
245
246        impl<'a> From<&'a $source_type> for DataConverter<'a> {
247            #[inline]
248            fn from(value: &'a $source_type) -> Self {
249                DataConverter::$variant(*value)
250            }
251        }
252    };
253}
254
255macro_rules! impl_from_cow {
256    ($source_type:ty, $variant:ident) => {
257        impl<'a> From<$source_type> for DataConverter<'a> {
258            #[inline]
259            fn from(value: $source_type) -> Self {
260                DataConverter::$variant(Cow::Owned(value))
261            }
262        }
263
264        impl<'a> From<&'a $source_type> for DataConverter<'a> {
265            #[inline]
266            fn from(value: &'a $source_type) -> Self {
267                DataConverter::$variant(Cow::Borrowed(value))
268            }
269        }
270    };
271}
272
273impl_from_copy!(bool, Bool);
274impl_from_copy!(char, Char);
275impl_from_copy!(i8, Int8);
276impl_from_copy!(i16, Int16);
277impl_from_copy!(i32, Int32);
278impl_from_copy!(i64, Int64);
279impl_from_copy!(i128, Int128);
280impl_from_copy!(u8, UInt8);
281impl_from_copy!(u16, UInt16);
282impl_from_copy!(u32, UInt32);
283impl_from_copy!(u64, UInt64);
284impl_from_copy!(u128, UInt128);
285impl_from_copy!(isize, IntSize);
286impl_from_copy!(usize, UIntSize);
287impl_from_copy!(f32, Float32);
288impl_from_copy!(f64, Float64);
289impl_from_copy!(NaiveDate, Date);
290impl_from_copy!(NaiveTime, Time);
291impl_from_copy!(NaiveDateTime, DateTime);
292impl_from_copy!(DateTime<Utc>, Instant);
293impl_from_copy!(Duration, Duration);
294
295impl_from_cow!(BigInt, BigInteger);
296impl_from_cow!(BigDecimal, BigDecimal);
297impl_from_cow!(Url, Url);
298impl_from_cow!(HashMap<String, String>, StringMap);
299impl_from_cow!(serde_json::Value, Json);
300
301impl<'a> From<&'a str> for DataConverter<'a> {
302    /// Creates a converter from a borrowed string slice.
303    #[inline]
304    fn from(value: &'a str) -> Self {
305        DataConverter::String(Cow::Borrowed(value))
306    }
307}
308
309impl<'a> From<&'a String> for DataConverter<'a> {
310    /// Creates a converter from a borrowed [`String`].
311    #[inline]
312    fn from(value: &'a String) -> Self {
313        DataConverter::String(Cow::Borrowed(value.as_str()))
314    }
315}
316
317impl<'a> From<String> for DataConverter<'a> {
318    /// Creates a converter from an owned [`String`].
319    #[inline]
320    fn from(value: String) -> Self {
321        DataConverter::String(Cow::Owned(value))
322    }
323}
324
325/// Parses a string using the configured bool conversion rules.
326fn parse_bool_string(value: &str, options: &DataConversionOptions) -> DataConversionResult<bool> {
327    let value = options.string.normalize(value)?;
328    if let Some(parsed) = options.boolean.parse(&value) {
329        Ok(parsed)
330    } else {
331        Err(DataConversionError::ConversionError(format!(
332            "Cannot convert '{value}' to boolean"
333        )))
334    }
335}
336
337/// Parses a string using the configured duration conversion rules.
338///
339/// # Parameters
340///
341/// * `value` - Source string to parse.
342/// * `options` - Conversion options providing string normalization and the
343///   default unit for suffixless duration strings.
344///
345/// # Returns
346///
347/// The parsed [`Duration`].
348///
349/// # Errors
350///
351/// Returns [`DataConversionError::NoValue`] when string normalization treats
352/// the value as missing, or [`DataConversionError::ConversionError`] when the
353/// value is empty, has an invalid integer, uses an unsupported suffix, or
354/// overflows [`Duration`].
355fn parse_duration_string(
356    value: &str,
357    options: &DataConversionOptions,
358) -> DataConversionResult<Duration> {
359    let value = options.string.normalize(value)?;
360    let trimmed = value.trim();
361    if trimmed.is_empty() {
362        return Err(DataConversionError::ConversionError(
363            "Cannot convert empty string to Duration".to_string(),
364        ));
365    }
366    let (number, unit) = split_duration_number_and_unit(trimmed)?;
367    let value = number.parse::<u64>().map_err(|_| {
368        DataConversionError::ConversionError(format!(
369            "Cannot convert '{trimmed}' to Duration: invalid duration value"
370        ))
371    })?;
372    let unit = match unit {
373        Some(unit) => DurationUnit::from_suffix(unit).ok_or_else(|| {
374            DataConversionError::ConversionError(format!(
375                "Cannot convert '{trimmed}' to Duration: unsupported duration unit '{unit}'"
376            ))
377        })?,
378        None => options.duration.unit,
379    };
380    unit.duration_from_u64(value).map_err(|message| {
381        DataConversionError::ConversionError(format!(
382            "Cannot convert '{trimmed}' to Duration: {message}"
383        ))
384    })
385}
386
387/// Splits duration text into number and optional unit suffix.
388///
389/// # Parameters
390///
391/// * `text` - Non-empty duration text.
392///
393/// # Returns
394///
395/// The number text and optional unit suffix.
396///
397/// # Errors
398///
399/// Returns [`DataConversionError::ConversionError`] when the numeric value is
400/// missing.
401fn split_duration_number_and_unit(text: &str) -> DataConversionResult<(&str, Option<&str>)> {
402    let Some(split_at) = text.find(|ch: char| !ch.is_ascii_digit()) else {
403        return Ok((text, None));
404    };
405    let (number, unit) = text.split_at(split_at);
406    if number.is_empty() {
407        return Err(DataConversionError::ConversionError(
408            "Cannot convert duration string: duration value is missing".to_string(),
409        ));
410    }
411    Ok((number, Some(unit)))
412}
413
414/// Converts a signed integer to [`Duration`] using configured duration options.
415///
416/// # Parameters
417///
418/// * `value` - Signed integer value to convert.
419/// * `options` - Conversion options providing the source integer unit.
420///
421/// # Returns
422///
423/// The corresponding [`Duration`].
424///
425/// # Errors
426///
427/// Returns [`DataConversionError::ConversionError`] when the value is negative,
428/// larger than `u64::MAX`, or overflows while applying the configured unit.
429fn signed_integer_to_duration(
430    value: i128,
431    options: &DataConversionOptions,
432) -> DataConversionResult<Duration> {
433    if value < 0 {
434        return Err(DataConversionError::ConversionError(format!(
435            "Cannot convert {value} to Duration: duration value must be non-negative"
436        )));
437    }
438    let value = range_check(value, 0i128, u64::MAX as i128, "Duration")?;
439    options
440        .duration
441        .unit
442        .duration_from_u64(value as u64)
443        .map_err(|message| {
444            DataConversionError::ConversionError(format!(
445                "Cannot convert {value} to Duration: {message}"
446            ))
447        })
448}
449
450/// Converts an unsigned integer to [`Duration`] using configured duration options.
451///
452/// # Parameters
453///
454/// * `value` - Unsigned integer value to convert.
455/// * `options` - Conversion options providing the source integer unit.
456///
457/// # Returns
458///
459/// The corresponding [`Duration`].
460///
461/// # Errors
462///
463/// Returns [`DataConversionError::ConversionError`] when the value is larger
464/// than `u64::MAX` or overflows while applying the configured unit.
465fn unsigned_integer_to_duration(
466    value: u128,
467    options: &DataConversionOptions,
468) -> DataConversionResult<Duration> {
469    let value = range_check(value, 0u128, u64::MAX as u128, "Duration")?;
470    options
471        .duration
472        .unit
473        .duration_from_u64(value as u64)
474        .map_err(|message| {
475            DataConversionError::ConversionError(format!(
476                "Cannot convert {value} to Duration: {message}"
477            ))
478        })
479}
480
481/// Formats a [`Duration`] using configured duration options.
482///
483/// # Parameters
484///
485/// * `duration` - Duration to format.
486/// * `options` - Conversion options providing the target unit and suffix
487///   behavior.
488///
489/// # Returns
490///
491/// The formatted duration string. The numeric value is rounded half-up in the
492/// configured unit.
493fn format_duration(duration: Duration, options: &DataConversionOptions) -> String {
494    let unit = options.duration.unit;
495    let value = unit.rounded_units(duration);
496    if options.duration.append_unit_suffix {
497        format!("{}{}", value, unit.suffix())
498    } else {
499        value.to_string()
500    }
501}
502
503/// Checks that a value is inside a closed range.
504fn range_check<T>(value: T, min: T, max: T, target: &str) -> DataConversionResult<T>
505where
506    T: PartialOrd + fmt::Display + Copy,
507{
508    if value < min || value > max {
509        Err(DataConversionError::ConversionError(format!(
510            "Cannot convert value to {target}: value must be in range [{min}, {max}]: actual {value}"
511        )))
512    } else {
513        Ok(value)
514    }
515}
516
517/// Converts a finite floating point value to `i128`.
518fn checked_float_to_i128(value: f64, source: &str, target: &str) -> DataConversionResult<i128> {
519    if !value.is_finite() {
520        return Err(DataConversionError::ConversionError(format!(
521            "Cannot convert non-finite {source} value to {target}"
522        )));
523    }
524    value.to_i128().ok_or_else(|| {
525        DataConversionError::ConversionError(format!("{source} value out of {target} range"))
526    })
527}
528
529/// Converts a `BigInt` to `f32` and rejects infinite conversion results.
530fn checked_bigint_to_f32(value: &BigInt) -> DataConversionResult<f32> {
531    let converted = value.to_f32().unwrap_or(f32::INFINITY);
532    if converted.is_finite() {
533        Ok(converted)
534    } else {
535        Err(DataConversionError::ConversionError(
536            "BigInteger value out of f32 range".to_string(),
537        ))
538    }
539}
540
541/// Converts a `BigDecimal` to `f32` and rejects infinite conversion results.
542fn checked_bigdecimal_to_f32(value: &BigDecimal) -> DataConversionResult<f32> {
543    let converted = value.to_f32().unwrap_or(f32::INFINITY);
544    if converted.is_finite() {
545        Ok(converted)
546    } else {
547        Err(DataConversionError::ConversionError(
548            "BigDecimal value out of f32 range".to_string(),
549        ))
550    }
551}
552
553/// Converts a `u128` to `f32` and rejects values that overflow to infinity.
554fn checked_u128_to_f32(value: u128) -> DataConversionResult<f32> {
555    let converted = value as f32;
556    if converted.is_finite() {
557        Ok(converted)
558    } else {
559        Err(DataConversionError::ConversionError(
560            "u128 value out of f32 range".to_string(),
561        ))
562    }
563}
564
565/// Converts a `BigInt` to `f64` and rejects infinite conversion results.
566fn checked_bigint_to_f64(value: &BigInt) -> DataConversionResult<f64> {
567    let converted = value.to_f64().unwrap_or(f64::INFINITY);
568    if converted.is_finite() {
569        Ok(converted)
570    } else {
571        Err(DataConversionError::ConversionError(
572            "BigInteger value out of f64 range".to_string(),
573        ))
574    }
575}
576
577/// Converts a `BigDecimal` to `f64` and rejects infinite conversion results.
578fn checked_bigdecimal_to_f64(value: &BigDecimal) -> DataConversionResult<f64> {
579    let converted = value.to_f64().unwrap_or(f64::INFINITY);
580    if converted.is_finite() {
581        Ok(converted)
582    } else {
583        Err(DataConversionError::ConversionError(
584            "BigDecimal value out of f64 range".to_string(),
585        ))
586    }
587}
588
589/// Converts source values accepted by signed integer targets to `i128`.
590fn convert_to_signed_integer(
591    source: &DataConverter<'_>,
592    options: &DataConversionOptions,
593    target_type: DataType,
594    target: &str,
595) -> DataConversionResult<i128> {
596    match source {
597        DataConverter::Bool(value) => Ok(if *value { 1 } else { 0 }),
598        DataConverter::Char(value) => Ok(*value as u32 as i128),
599        DataConverter::Int8(value) => Ok(*value as i128),
600        DataConverter::Int16(value) => Ok(*value as i128),
601        DataConverter::Int32(value) => Ok(*value as i128),
602        DataConverter::Int64(value) => Ok(*value as i128),
603        DataConverter::Int128(value) => Ok(*value),
604        DataConverter::IntSize(value) => Ok(*value as i128),
605        DataConverter::UInt8(value) => Ok(*value as i128),
606        DataConverter::UInt16(value) => Ok(*value as i128),
607        DataConverter::UInt32(value) => Ok(*value as i128),
608        DataConverter::UInt64(value) => Ok(*value as i128),
609        DataConverter::UInt128(value) => {
610            let checked = range_check(*value, 0u128, i128::MAX as u128, target)?;
611            Ok(checked as i128)
612        }
613        DataConverter::UIntSize(value) => Ok(*value as i128),
614        DataConverter::Float32(value) => checked_float_to_i128(*value as f64, "f32", target),
615        DataConverter::Float64(value) => checked_float_to_i128(*value, "f64", target),
616        DataConverter::String(value) => options
617            .string
618            .normalize(value.as_ref())?
619            .parse::<i128>()
620            .map_err(|_| {
621                DataConversionError::ConversionError(format!(
622                    "Cannot convert '{}' to {target}",
623                    value.as_ref()
624                ))
625            }),
626        DataConverter::Duration(value) => {
627            let units = options.duration.unit.rounded_units(*value);
628            let checked = range_check(units, 0u128, i128::MAX as u128, target)?;
629            Ok(checked as i128)
630        }
631        DataConverter::BigInteger(value) => value.as_ref().to_i128().ok_or_else(|| {
632            DataConversionError::ConversionError(format!("BigInteger value out of {target} range"))
633        }),
634        DataConverter::BigDecimal(value) => value.as_ref().to_i128().ok_or_else(|| {
635            DataConversionError::ConversionError(format!(
636                "BigDecimal value cannot be converted to {target}"
637            ))
638        }),
639        DataConverter::Empty(_) => Err(DataConversionError::NoValue),
640        _ => Err(source.unsupported(target_type)),
641    }
642}
643
644/// Converts source values accepted by unsigned integer targets to `u128`.
645fn convert_to_unsigned_integer(
646    source: &DataConverter<'_>,
647    options: &DataConversionOptions,
648    target_type: DataType,
649    target: &str,
650) -> DataConversionResult<u128> {
651    match source {
652        DataConverter::Bool(value) => Ok(if *value { 1 } else { 0 }),
653        DataConverter::Char(value) => Ok((*value as u32).into()),
654        DataConverter::Int8(value) => {
655            let checked = range_check(*value, 0i8, i8::MAX, target)?;
656            Ok(checked as u128)
657        }
658        DataConverter::Int16(value) => {
659            let checked = range_check(*value, 0i16, i16::MAX, target)?;
660            Ok(checked as u128)
661        }
662        DataConverter::Int32(value) => {
663            let checked = range_check(*value, 0i32, i32::MAX, target)?;
664            Ok(checked as u128)
665        }
666        DataConverter::Int64(value) => {
667            let checked = range_check(*value, 0i64, i64::MAX, target)?;
668            Ok(checked as u128)
669        }
670        DataConverter::Int128(value) => {
671            let checked = range_check(*value, 0i128, i128::MAX, target)?;
672            Ok(checked as u128)
673        }
674        DataConverter::IntSize(value) => {
675            let checked = range_check(*value, 0isize, isize::MAX, target)?;
676            Ok(checked as u128)
677        }
678        DataConverter::UInt8(value) => Ok((*value).into()),
679        DataConverter::UInt16(value) => Ok((*value).into()),
680        DataConverter::UInt32(value) => Ok((*value).into()),
681        DataConverter::UInt64(value) => Ok((*value).into()),
682        DataConverter::UInt128(value) => Ok(*value),
683        DataConverter::UIntSize(value) => Ok(*value as u128),
684        DataConverter::String(value) => options
685            .string
686            .normalize(value.as_ref())?
687            .parse::<u128>()
688            .map_err(|_| {
689                DataConversionError::ConversionError(format!(
690                    "Cannot convert '{}' to {target}",
691                    value.as_ref()
692                ))
693            }),
694        DataConverter::Duration(value) => Ok(options.duration.unit.rounded_units(*value)),
695        DataConverter::Empty(_) => Err(DataConversionError::NoValue),
696        _ => Err(source.unsupported(target_type)),
697    }
698}
699
700impl DataConvertTo<String> for DataConverter<'_> {
701    /// Converts a supported source value to `String`.
702    fn convert(&self, options: &DataConversionOptions) -> DataConversionResult<String> {
703        match self {
704            DataConverter::String(value) => options.string.normalize(value.as_ref()),
705            DataConverter::Bool(value) => Ok(value.to_string()),
706            DataConverter::Char(value) => Ok(value.to_string()),
707            DataConverter::Int8(value) => Ok(value.to_string()),
708            DataConverter::Int16(value) => Ok(value.to_string()),
709            DataConverter::Int32(value) => Ok(value.to_string()),
710            DataConverter::Int64(value) => Ok(value.to_string()),
711            DataConverter::Int128(value) => Ok(value.to_string()),
712            DataConverter::UInt8(value) => Ok(value.to_string()),
713            DataConverter::UInt16(value) => Ok(value.to_string()),
714            DataConverter::UInt32(value) => Ok(value.to_string()),
715            DataConverter::UInt64(value) => Ok(value.to_string()),
716            DataConverter::UInt128(value) => Ok(value.to_string()),
717            DataConverter::IntSize(value) => Ok(value.to_string()),
718            DataConverter::UIntSize(value) => Ok(value.to_string()),
719            DataConverter::Float32(value) => Ok(value.to_string()),
720            DataConverter::Float64(value) => Ok(value.to_string()),
721            DataConverter::BigInteger(value) => Ok(value.to_string()),
722            DataConverter::BigDecimal(value) => Ok(value.to_string()),
723            DataConverter::Date(value) => Ok(value.to_string()),
724            DataConverter::Time(value) => Ok(value.to_string()),
725            DataConverter::DateTime(value) => Ok(value.to_string()),
726            DataConverter::Instant(value) => Ok(value.to_rfc3339()),
727            DataConverter::Duration(value) => Ok(format_duration(*value, options)),
728            DataConverter::Url(value) => Ok(value.to_string()),
729            DataConverter::StringMap(value) => match serde_json::to_string(value.as_ref()) {
730                Ok(value) => Ok(value),
731                Err(error) => Err(DataConversionError::JsonSerializationError(
732                    error.to_string(),
733                )),
734            },
735            DataConverter::Json(value) => match serde_json::to_string(value.as_ref()) {
736                Ok(value) => Ok(value),
737                Err(error) => Err(DataConversionError::JsonSerializationError(
738                    error.to_string(),
739                )),
740            },
741            DataConverter::Empty(_) => Err(DataConversionError::NoValue),
742        }
743    }
744}
745
746impl DataConvertTo<bool> for DataConverter<'_> {
747    /// Converts accepted source values to `bool`.
748    fn convert(&self, options: &DataConversionOptions) -> DataConversionResult<bool> {
749        match self {
750            DataConverter::Bool(value) => Ok(*value),
751            DataConverter::Int8(value) => Ok(*value != 0),
752            DataConverter::Int16(value) => Ok(*value != 0),
753            DataConverter::Int32(value) => Ok(*value != 0),
754            DataConverter::Int64(value) => Ok(*value != 0),
755            DataConverter::Int128(value) => Ok(*value != 0),
756            DataConverter::UInt8(value) => Ok(*value != 0),
757            DataConverter::UInt16(value) => Ok(*value != 0),
758            DataConverter::UInt32(value) => Ok(*value != 0),
759            DataConverter::UInt64(value) => Ok(*value != 0),
760            DataConverter::UInt128(value) => Ok(*value != 0),
761            DataConverter::String(value) => parse_bool_string(value.as_ref(), options),
762            DataConverter::Empty(_) => Err(DataConversionError::NoValue),
763            _ => Err(self.unsupported(DataType::Bool)),
764        }
765    }
766}
767
768impl DataConvertTo<char> for DataConverter<'_> {
769    /// Converts a character source to `char`.
770    fn convert(&self, _options: &DataConversionOptions) -> DataConversionResult<char> {
771        match self {
772            DataConverter::Char(value) => Ok(*value),
773            DataConverter::Empty(_) => Err(DataConversionError::NoValue),
774            _ => Err(self.unsupported(DataType::Char)),
775        }
776    }
777}
778
779macro_rules! impl_signed_integer_converter {
780    ($target_type:ty, $data_type:expr, $target_name:expr, $min:expr, $max:expr) => {
781        impl DataConvertTo<$target_type> for DataConverter<'_> {
782            #[inline]
783            fn convert(
784                &self,
785                options: &DataConversionOptions,
786            ) -> DataConversionResult<$target_type> {
787                let value = convert_to_signed_integer(self, options, $data_type, $target_name)?;
788                let checked = range_check(value, $min as i128, $max as i128, $target_name)?;
789                Ok(checked as $target_type)
790            }
791        }
792    };
793}
794
795impl_signed_integer_converter!(i8, DataType::Int8, "i8", i8::MIN, i8::MAX);
796impl_signed_integer_converter!(i16, DataType::Int16, "i16", i16::MIN, i16::MAX);
797impl_signed_integer_converter!(i32, DataType::Int32, "i32", i32::MIN, i32::MAX);
798impl_signed_integer_converter!(i64, DataType::Int64, "i64", i64::MIN, i64::MAX);
799impl_signed_integer_converter!(isize, DataType::IntSize, "isize", isize::MIN, isize::MAX);
800
801impl DataConvertTo<i128> for DataConverter<'_> {
802    /// Converts accepted source values to `i128`.
803    #[inline]
804    fn convert(&self, options: &DataConversionOptions) -> DataConversionResult<i128> {
805        convert_to_signed_integer(self, options, DataType::Int128, "i128")
806    }
807}
808
809macro_rules! impl_unsigned_integer_converter {
810    ($target_type:ty, $data_type:expr, $target_name:expr, $max:expr) => {
811        impl DataConvertTo<$target_type> for DataConverter<'_> {
812            #[inline]
813            fn convert(
814                &self,
815                options: &DataConversionOptions,
816            ) -> DataConversionResult<$target_type> {
817                let value = convert_to_unsigned_integer(self, options, $data_type, $target_name)?;
818                let checked = range_check(
819                    value,
820                    <$target_type>::MIN as u128,
821                    $max as u128,
822                    $target_name,
823                )?;
824                Ok(checked as $target_type)
825            }
826        }
827    };
828}
829
830impl_unsigned_integer_converter!(u8, DataType::UInt8, "u8", u8::MAX);
831impl_unsigned_integer_converter!(u16, DataType::UInt16, "u16", u16::MAX);
832impl_unsigned_integer_converter!(u32, DataType::UInt32, "u32", u32::MAX);
833impl_unsigned_integer_converter!(u64, DataType::UInt64, "u64", u64::MAX);
834impl_unsigned_integer_converter!(usize, DataType::UIntSize, "usize", usize::MAX);
835
836impl DataConvertTo<u128> for DataConverter<'_> {
837    /// Converts accepted source values to `u128`.
838    #[inline]
839    fn convert(&self, options: &DataConversionOptions) -> DataConversionResult<u128> {
840        convert_to_unsigned_integer(self, options, DataType::UInt128, "u128")
841    }
842}
843
844impl DataConvertTo<f32> for DataConverter<'_> {
845    /// Converts accepted source values to `f32`.
846    fn convert(&self, options: &DataConversionOptions) -> DataConversionResult<f32> {
847        match self {
848            DataConverter::Float32(value) => Ok(*value),
849            DataConverter::Float64(value) => {
850                if value.is_nan() || value.is_infinite() {
851                    Ok(*value as f32)
852                } else {
853                    let checked = range_check(*value, f32::MIN as f64, f32::MAX as f64, "f32")?;
854                    Ok(checked as f32)
855                }
856            }
857            DataConverter::Bool(value) => Ok(if *value { 1.0 } else { 0.0 }),
858            DataConverter::Char(value) => Ok(*value as u32 as f32),
859            DataConverter::Int8(value) => Ok(*value as f32),
860            DataConverter::Int16(value) => Ok(*value as f32),
861            DataConverter::Int32(value) => Ok(*value as f32),
862            DataConverter::Int64(value) => Ok(*value as f32),
863            DataConverter::Int128(value) => Ok(*value as f32),
864            DataConverter::IntSize(value) => Ok(*value as f32),
865            DataConverter::UInt8(value) => Ok(*value as f32),
866            DataConverter::UInt16(value) => Ok(*value as f32),
867            DataConverter::UInt32(value) => Ok(*value as f32),
868            DataConverter::UInt64(value) => Ok(*value as f32),
869            DataConverter::UInt128(value) => checked_u128_to_f32(*value),
870            DataConverter::UIntSize(value) => Ok(*value as f32),
871            DataConverter::String(value) => options
872                .string
873                .normalize(value.as_ref())?
874                .parse::<f32>()
875                .map_err(|_| {
876                    DataConversionError::ConversionError(format!(
877                        "Cannot convert '{}' to f32",
878                        value.as_ref()
879                    ))
880                }),
881            DataConverter::BigInteger(value) => checked_bigint_to_f32(value.as_ref()),
882            DataConverter::BigDecimal(value) => checked_bigdecimal_to_f32(value.as_ref()),
883            DataConverter::Empty(_) => Err(DataConversionError::NoValue),
884            _ => Err(self.unsupported(DataType::Float32)),
885        }
886    }
887}
888
889impl DataConvertTo<f64> for DataConverter<'_> {
890    /// Converts accepted source values to `f64`.
891    fn convert(&self, options: &DataConversionOptions) -> DataConversionResult<f64> {
892        match self {
893            DataConverter::Float64(value) => Ok(*value),
894            DataConverter::Float32(value) => Ok(*value as f64),
895            DataConverter::Bool(value) => Ok(if *value { 1.0 } else { 0.0 }),
896            DataConverter::Char(value) => Ok(*value as u32 as f64),
897            DataConverter::Int8(value) => Ok(*value as f64),
898            DataConverter::Int16(value) => Ok(*value as f64),
899            DataConverter::Int32(value) => Ok(*value as f64),
900            DataConverter::Int64(value) => Ok(*value as f64),
901            DataConverter::Int128(value) => Ok(*value as f64),
902            DataConverter::IntSize(value) => Ok(*value as f64),
903            DataConverter::UInt8(value) => Ok(*value as f64),
904            DataConverter::UInt16(value) => Ok(*value as f64),
905            DataConverter::UInt32(value) => Ok(*value as f64),
906            DataConverter::UInt64(value) => Ok(*value as f64),
907            DataConverter::UInt128(value) => Ok(*value as f64),
908            DataConverter::UIntSize(value) => Ok(*value as f64),
909            DataConverter::String(value) => options
910                .string
911                .normalize(value.as_ref())?
912                .parse::<f64>()
913                .map_err(|_| {
914                    DataConversionError::ConversionError(format!(
915                        "Cannot convert '{}' to f64",
916                        value.as_ref()
917                    ))
918                }),
919            DataConverter::BigInteger(value) => checked_bigint_to_f64(value.as_ref()),
920            DataConverter::BigDecimal(value) => checked_bigdecimal_to_f64(value.as_ref()),
921            DataConverter::Empty(_) => Err(DataConversionError::NoValue),
922            _ => Err(self.unsupported(DataType::Float64)),
923        }
924    }
925}
926
927macro_rules! impl_strict_copy_converter {
928    ($target_type:ty, $variant:ident, $data_type:expr) => {
929        impl DataConvertTo<$target_type> for DataConverter<'_> {
930            #[inline]
931            fn convert(
932                &self,
933                _options: &DataConversionOptions,
934            ) -> DataConversionResult<$target_type> {
935                match self {
936                    DataConverter::$variant(value) => Ok(*value),
937                    DataConverter::Empty(_) => Err(DataConversionError::NoValue),
938                    _ => Err(self.unsupported($data_type)),
939                }
940            }
941        }
942    };
943}
944
945macro_rules! impl_strict_cow_converter {
946    ($target_type:ty, $variant:ident, $data_type:expr) => {
947        impl DataConvertTo<$target_type> for DataConverter<'_> {
948            #[inline]
949            fn convert(
950                &self,
951                _options: &DataConversionOptions,
952            ) -> DataConversionResult<$target_type> {
953                match self {
954                    DataConverter::$variant(value) => Ok(value.as_ref().clone()),
955                    DataConverter::Empty(_) => Err(DataConversionError::NoValue),
956                    _ => Err(self.unsupported($data_type)),
957                }
958            }
959        }
960    };
961}
962
963impl_strict_copy_converter!(NaiveDate, Date, DataType::Date);
964impl_strict_copy_converter!(NaiveTime, Time, DataType::Time);
965impl_strict_copy_converter!(NaiveDateTime, DateTime, DataType::DateTime);
966impl_strict_copy_converter!(DateTime<Utc>, Instant, DataType::Instant);
967impl_strict_cow_converter!(BigInt, BigInteger, DataType::BigInteger);
968impl_strict_cow_converter!(BigDecimal, BigDecimal, DataType::BigDecimal);
969impl_strict_cow_converter!(HashMap<String, String>, StringMap, DataType::StringMap);
970
971impl DataConvertTo<Duration> for DataConverter<'_> {
972    /// Converts accepted source values to [`Duration`].
973    fn convert(&self, options: &DataConversionOptions) -> DataConversionResult<Duration> {
974        match self {
975            DataConverter::Duration(value) => Ok(*value),
976            DataConverter::Int8(value) => signed_integer_to_duration((*value).into(), options),
977            DataConverter::Int16(value) => signed_integer_to_duration((*value).into(), options),
978            DataConverter::Int32(value) => signed_integer_to_duration((*value).into(), options),
979            DataConverter::Int64(value) => signed_integer_to_duration((*value).into(), options),
980            DataConverter::Int128(value) => signed_integer_to_duration(*value, options),
981            DataConverter::IntSize(value) => signed_integer_to_duration(*value as i128, options),
982            DataConverter::UInt8(value) => unsigned_integer_to_duration((*value).into(), options),
983            DataConverter::UInt16(value) => unsigned_integer_to_duration((*value).into(), options),
984            DataConverter::UInt32(value) => unsigned_integer_to_duration((*value).into(), options),
985            DataConverter::UInt64(value) => unsigned_integer_to_duration((*value).into(), options),
986            DataConverter::UInt128(value) => unsigned_integer_to_duration(*value, options),
987            DataConverter::UIntSize(value) => unsigned_integer_to_duration(*value as u128, options),
988            DataConverter::BigInteger(value) => value.as_ref().to_u64().map_or_else(
989                || {
990                    Err(DataConversionError::ConversionError(
991                        "Cannot convert BigInteger to Duration: value must be a non-negative u64"
992                            .to_string(),
993                    ))
994                },
995                |value| {
996                    options
997                        .duration
998                        .unit
999                        .duration_from_u64(value)
1000                        .map_err(|message| {
1001                            DataConversionError::ConversionError(format!(
1002                                "Cannot convert BigInteger to Duration: {message}"
1003                            ))
1004                        })
1005                },
1006            ),
1007            DataConverter::String(value) => parse_duration_string(value.as_ref(), options),
1008            DataConverter::Empty(_) => Err(DataConversionError::NoValue),
1009            _ => Err(self.unsupported(DataType::Duration)),
1010        }
1011    }
1012}
1013
1014impl DataConvertTo<Url> for DataConverter<'_> {
1015    /// Converts accepted source values to [`Url`].
1016    fn convert(&self, options: &DataConversionOptions) -> DataConversionResult<Url> {
1017        match self {
1018            DataConverter::Url(value) => Ok(value.as_ref().clone()),
1019            DataConverter::String(value) => {
1020                let value = options.string.normalize(value.as_ref())?;
1021                Url::parse(&value).map_err(|error| {
1022                    DataConversionError::ConversionError(format!(
1023                        "Cannot convert '{value}' to Url: {error}"
1024                    ))
1025                })
1026            }
1027            DataConverter::Empty(_) => Err(DataConversionError::NoValue),
1028            _ => Err(self.unsupported(DataType::Url)),
1029        }
1030    }
1031}
1032
1033impl DataConvertTo<serde_json::Value> for DataConverter<'_> {
1034    /// Converts accepted source values to [`serde_json::Value`].
1035    fn convert(&self, options: &DataConversionOptions) -> DataConversionResult<serde_json::Value> {
1036        match self {
1037            DataConverter::Json(value) => Ok(value.as_ref().clone()),
1038            DataConverter::String(value) => {
1039                let value = options.string.normalize(value.as_ref())?;
1040                serde_json::from_str(&value).map_err(|error| {
1041                    DataConversionError::JsonDeserializationError(error.to_string())
1042                })
1043            }
1044            DataConverter::StringMap(value) => match serde_json::to_value(value.as_ref()) {
1045                Ok(value) => Ok(value),
1046                Err(error) => Err(DataConversionError::JsonSerializationError(
1047                    error.to_string(),
1048                )),
1049            },
1050            DataConverter::Empty(_) => Err(DataConversionError::NoValue),
1051            _ => Err(self.unsupported(DataType::Json)),
1052        }
1053    }
1054}