use std::borrow::Cow;
use std::collections::HashMap;
use std::time::Duration;
#[cfg(feature = "big-decimal")]
use bigdecimal::BigDecimal;
#[cfg(feature = "chrono")]
use chrono::DateTime;
#[cfg(feature = "chrono")]
use chrono::NaiveDate;
#[cfg(feature = "chrono")]
use chrono::NaiveDateTime;
#[cfg(feature = "chrono")]
use chrono::NaiveTime;
#[cfg(feature = "chrono")]
use chrono::Utc;
#[cfg(feature = "big-integer")]
use num_bigint::BigInt;
#[cfg(feature = "json")]
use serde_json::Value;
#[cfg(feature = "url")]
use url::Url;
#[cfg(feature = "json")]
use self::structured::account_json_structure;
use self::structured::account_string_map_structure;
use super::admitted_conversion::AdmittedConversion;
use super::conversion_context::ConversionContext;
use super::conversion_session::ConversionSession;
use super::data_conversion_target::DataConversionTarget;
use super::error::DataConversionError;
use super::error::InvalidValueReason;
use super::options::ConversionLimits;
use super::options::ConversionPolicy;
use crate::datatype::DataType;
use crate::datatype::for_each_data_type_mapping;
mod boolean;
mod duration;
mod internal;
mod numeric;
mod source;
mod string_source;
mod structured;
mod text;
macro_rules! data_converter_data_type_match {
($value:expr; $( $(#[$meta:meta])* ($variant:ident, $source:ty, $strategy:ident) ),+ $(,)?) => {
match $value {
Self::Unset(data_type) => *data_type,
$(
$(#[$meta])*
Self::$variant(_) => DataType::$variant,
)+
}
};
}
#[must_use]
#[derive(Debug, Clone, PartialEq)]
#[non_exhaustive]
pub enum DataConverter<'a> {
Unset(
DataType,
),
Bool(
bool,
),
Char(
char,
),
Int8(
i8,
),
Int16(
i16,
),
Int32(
i32,
),
Int64(
i64,
),
Int128(
i128,
),
UInt8(
u8,
),
UInt16(
u16,
),
UInt32(
u32,
),
UInt64(
u64,
),
UInt128(
u128,
),
Float32(
f32,
),
Float64(
f64,
),
#[cfg(feature = "big-integer")]
BigInteger(
Cow<'a, BigInt>,
),
#[cfg(feature = "big-decimal")]
BigDecimal(
Cow<'a, BigDecimal>,
),
String(
Cow<'a, str>,
),
#[cfg(feature = "chrono")]
Date(
NaiveDate,
),
#[cfg(feature = "chrono")]
Time(
NaiveTime,
),
#[cfg(feature = "chrono")]
DateTime(
NaiveDateTime,
),
#[cfg(feature = "chrono")]
Instant(
DateTime<Utc>,
),
Duration(
Duration,
),
#[cfg(feature = "url")]
Url(
Cow<'a, Url>,
),
StringMap(
Cow<'a, HashMap<String, String>>,
),
#[cfg(feature = "json")]
Json(
Cow<'a, Value>,
),
}
impl<'source> DataConverter<'source> {
#[inline(always)]
pub fn to<T>(&self) -> Result<T, DataConversionError>
where
T: DataConversionTarget,
{
self.to_with(ConversionPolicy::default_ref(), ConversionLimits::default_ref())
}
#[inline(always)]
pub fn to_with<T>(&self, policy: &ConversionPolicy, limits: &ConversionLimits) -> Result<T, DataConversionError>
where
T: DataConversionTarget,
{
let mut session = ConversionSession::new(policy, limits);
self.to_in(&mut session)
}
#[inline(always)]
pub fn to_in<T>(&self, session: &mut ConversionSession<'_>) -> Result<T, DataConversionError>
where
T: DataConversionTarget,
{
session
.consume_item()
.map_err(|limit| DataConversionError::limit_exceeded(self.data_type(), T::DATA_TYPE, limit))?;
self.charge_input_for_target(T::DATA_TYPE, session)?;
let context = ConversionContext::new(session, self.data_type(), T::DATA_TYPE);
T::convert(AdmittedConversion::new(self.borrowed(), context))
}
#[inline(always)]
pub fn into_target<T>(self) -> Result<T, DataConversionError>
where
T: DataConversionTarget,
{
self.into_target_with(ConversionPolicy::default_ref(), ConversionLimits::default_ref())
}
#[inline(always)]
pub fn into_target_with<T>(
self,
policy: &ConversionPolicy,
limits: &ConversionLimits,
) -> Result<T, DataConversionError>
where
T: DataConversionTarget,
{
let mut session = ConversionSession::new(policy, limits);
self.into_target_in(&mut session)
}
#[inline(always)]
pub fn into_target_in<T>(self, session: &mut ConversionSession<'_>) -> Result<T, DataConversionError>
where
T: DataConversionTarget,
{
let from = self.data_type();
session
.consume_item()
.map_err(|limit| DataConversionError::limit_exceeded(from, T::DATA_TYPE, limit))?;
self.charge_input_for_target(T::DATA_TYPE, session)?;
let context = ConversionContext::new(session, from, T::DATA_TYPE);
T::convert(AdmittedConversion::new(self, context))
}
fn charge_input_for_target(
&self,
target: DataType,
session: &mut ConversionSession<'_>,
) -> Result<(), DataConversionError> {
match self {
Self::String(value) => {
session
.consume_input_bytes_usize(value.len())
.map_err(|limit| DataConversionError::measured_limit(self.data_type(), target, limit))?;
}
#[cfg(feature = "json")]
Self::Json(value) if target == DataType::Json => {
account_json_structure(value, self.data_type(), target, session)?;
}
Self::StringMap(value) if target == DataType::StringMap => {
account_string_map_structure(value, 1, session)
.map_err(|limit| DataConversionError::measured_limit(self.data_type(), target, limit))?;
}
#[cfg(feature = "json")]
Self::StringMap(value) if target == DataType::Json => {
account_string_map_structure(value, 1, session)
.map_err(|limit| DataConversionError::measured_limit(self.data_type(), target, limit))?;
}
_ => {}
}
Ok(())
}
#[inline(always)]
fn borrowed<'borrowed>(&'borrowed self) -> DataConverter<'borrowed>
where
'source: 'borrowed,
{
match self {
Self::Unset(value) => Self::Unset(*value),
Self::Bool(value) => Self::Bool(*value),
Self::Char(value) => Self::Char(*value),
Self::Int8(value) => Self::Int8(*value),
Self::Int16(value) => Self::Int16(*value),
Self::Int32(value) => Self::Int32(*value),
Self::Int64(value) => Self::Int64(*value),
Self::Int128(value) => Self::Int128(*value),
Self::UInt8(value) => Self::UInt8(*value),
Self::UInt16(value) => Self::UInt16(*value),
Self::UInt32(value) => Self::UInt32(*value),
Self::UInt64(value) => Self::UInt64(*value),
Self::UInt128(value) => Self::UInt128(*value),
Self::Float32(value) => Self::Float32(*value),
Self::Float64(value) => Self::Float64(*value),
Self::String(value) => DataConverter::String(Cow::Borrowed(value.as_ref())),
#[cfg(feature = "big-integer")]
Self::BigInteger(value) => DataConverter::BigInteger(Cow::Borrowed(value.as_ref())),
#[cfg(feature = "big-decimal")]
Self::BigDecimal(value) => DataConverter::BigDecimal(Cow::Borrowed(value.as_ref())),
#[cfg(feature = "chrono")]
Self::Date(value) => Self::Date(*value),
#[cfg(feature = "chrono")]
Self::Time(value) => Self::Time(*value),
#[cfg(feature = "chrono")]
Self::DateTime(value) => Self::DateTime(*value),
#[cfg(feature = "chrono")]
Self::Instant(value) => Self::Instant(*value),
Self::Duration(value) => Self::Duration(*value),
#[cfg(feature = "url")]
Self::Url(value) => DataConverter::Url(Cow::Borrowed(value.as_ref())),
Self::StringMap(value) => DataConverter::StringMap(Cow::Borrowed(value.as_ref())),
#[cfg(feature = "json")]
Self::Json(value) => DataConverter::Json(Cow::Borrowed(value.as_ref())),
}
}
#[must_use = "the source data type is returned"]
#[inline(always)]
pub const fn data_type(&self) -> DataType {
for_each_data_type_mapping!(data_converter_data_type_match, self)
}
#[inline(always)]
fn missing(&self, to: DataType) -> DataConversionError {
DataConversionError::missing(self.data_type(), to)
}
#[inline(always)]
fn unsupported(&self, to: DataType) -> DataConversionError {
DataConversionError::unsupported(self.data_type(), to)
}
#[inline(always)]
fn invalid(&self, to: DataType, reason: InvalidValueReason) -> DataConversionError {
DataConversionError::invalid(self.data_type(), to, reason)
}
}