use {
super::{Convert, Value, ValueError, ValueType},
crate::{Error, Result},
chrono::{NaiveDate, NaiveDateTime, NaiveTime, ParseError},
std::convert::TryInto,
thousands::Separable,
};
pub trait Cast<Output> {
fn cast(self) -> Result<Output>;
}
pub trait CastWithRules<Output> {
fn cast_with_rule(self, rule: Self) -> Result<Output>;
}
fn failed_cast(value: &Value, value_type: ValueType) -> Error {
Error::Value(ValueError::FailedCast(value.clone(), value_type))
}
fn unimplemented_cast(value: &Value, value_type: ValueType) -> Error {
Error::Value(ValueError::FailedCast(value.clone().into(), value_type))
}
impl Cast<bool> for Value {
fn cast(self) -> Result<bool> {
Ok(match self {
Value::Bool(value) => value,
Value::I64(value) => match value {
1 => true,
0 => false,
_ => return Err(failed_cast(&self, ValueType::Bool)),
},
Value::F64(value) => {
if value.eq(&1.0) {
true
} else if value.eq(&0.0) {
false
} else {
return Err(failed_cast(&self, ValueType::Bool));
}
}
Value::Str(value) => match value.to_lowercase().as_str() {
"true" => true,
"false" => false,
_ => return Err(failed_cast(&Value::Str(value), ValueType::Bool)),
},
Value::Null => return Err(failed_cast(&self, ValueType::Bool)),
_ => return Err(unimplemented_cast(&self, ValueType::Bool)),
})
}
}
impl Cast<u64> for Value {
fn cast(self) -> Result<u64> {
Ok(match self {
Value::Bool(value) => {
if value {
1
} else {
0
}
}
Value::U64(value) => value,
Value::I64(value) => value
.try_into()
.map_err(|_| failed_cast(&self, ValueType::U64))?,
Value::F64(value) => (value.trunc() as i64)
.try_into()
.map_err(|_| failed_cast(&self, ValueType::U64))?,
Value::Str(value) => lexical::parse(&value)
.map_err(|_| failed_cast(&Value::Str(value), ValueType::U64))?,
Value::Null => return Err(failed_cast(&self, ValueType::U64)),
_ => return Err(unimplemented_cast(&self, ValueType::U64)),
})
}
}
impl Cast<i64> for Value {
fn cast(self) -> Result<i64> {
Ok(match self {
Value::Bool(value) => {
if value {
1
} else {
0
}
}
Value::U64(value) => value
.try_into()
.map_err(|_| failed_cast(&self, ValueType::I64))?,
Value::I64(value) => value,
Value::F64(value) => value.trunc() as i64,
Value::Str(value) => lexical::parse(&value)
.map_err(|_| failed_cast(&Value::Str(value), ValueType::I64))?,
Value::Null => return Err(failed_cast(&self, ValueType::I64)),
_ => return Err(unimplemented_cast(&self, ValueType::I64)),
})
}
}
impl Cast<f64> for Value {
fn cast(self) -> Result<f64> {
Ok(match self {
Value::Bool(value) => {
if value {
1.0
} else {
0.0
}
}
Value::U64(value) => (value as f64).trunc(),
Value::I64(value) => (value as f64).trunc(),
Value::F64(value) => value,
Value::Str(value) => fast_float::parse(&value)
.map_err(|_| failed_cast(&Value::Str(value), ValueType::F64))?,
Value::Null => return Err(failed_cast(&self, ValueType::F64)),
_ => return Err(unimplemented_cast(&self, ValueType::F64)),
})
}
}
impl Cast<String> for Value {
fn cast(self) -> Result<String> {
Ok(match self {
Value::Bool(value) => (if value { "true" } else { "false" }).to_string(),
Value::U64(value) => lexical::to_string(value),
Value::I64(value) => lexical::to_string(value),
Value::F64(value) => lexical::to_string(value),
Value::Str(value) => value,
Value::Timestamp(value) => NaiveDateTime::from_timestamp(value, 0).to_string(),
Value::Null => String::from("NULL"),
_ => return Err(unimplemented_cast(&self, ValueType::Str)),
})
}
}
impl Cast<usize> for Value {
fn cast(self) -> Result<usize> {
let int: u64 = self.cast()?;
int.try_into()
.map_err(|_| ValueError::ImpossibleCast.into())
}
}
impl CastWithRules<bool> for Value {
fn cast_with_rule(self, rule: Self) -> Result<bool> {
match rule {
Value::I64(000) | Value::Bool(true) => self.cast(),
_ => Err(ValueError::InvalidConversionRule.into()),
}
}
}
impl CastWithRules<i64> for Value {
fn cast_with_rule(self, rule: Self) -> Result<i64> {
match rule {
Value::I64(000) | Value::Bool(true) => self.cast(),
_ => Err(ValueError::InvalidConversionRule.into()),
}
}
}
impl CastWithRules<f64> for Value {
fn cast_with_rule(self, rule: Self) -> Result<f64> {
match rule {
Value::I64(000) | Value::Bool(true) => self.cast(),
_ => Err(ValueError::InvalidConversionRule.into()),
}
}
}
impl CastWithRules<String> for Value {
fn cast_with_rule(self, rule: Self) -> Result<String> {
match rule {
Value::I64(000) | Value::Bool(true) => self.cast(),
Value::Str(specified) if specified == *"DATETIME" => {
Ok(NaiveDateTime::from_timestamp(self.convert()?, 0)
.format("%F %T")
.to_string())
}
Value::Str(specified) if specified == *"MONEY" => {
let value: f64 = self.convert()?;
let value = (value * 100.0).round() / 100.0;
let value = value.separate_with_commas();
Ok(format!("${}", value))
}
Value::Str(specified) if specified == *"SEPARATED" => {
let value: f64 = self.convert()?;
let value = (value * 100.0).round() / 100.0;
let value = value.separate_with_commas();
Ok(value)
}
Value::Str(format) if matches!(self, Value::I64(..)) => {
Ok(NaiveDateTime::from_timestamp(self.convert()?, 0)
.format(&format)
.to_string())
}
_ => Err(ValueError::InvalidConversionRule.into()),
}
}
}
fn parse_error_into(error: ParseError) -> Error {
ValueError::DateTimeParseError(format!("{:?}", error)).into()
}
impl Cast<NaiveDateTime> for Value {
fn cast(self) -> Result<NaiveDateTime> {
let timestamp: i64 = self.cast()?;
NaiveDateTime::from_timestamp_opt(timestamp, 0)
.ok_or_else(|| ValueError::ImpossibleCast.into())
}
}
#[allow(clippy::zero_prefixed_literal)]
impl CastWithRules<NaiveDateTime> for Value {
fn cast_with_rule(self, rule: Self) -> Result<NaiveDateTime> {
fn for_format_datetime(string: Value, format: &str) -> Result<NaiveDateTime> {
let string: String = string.cast()?;
let string: &str = string.as_str();
NaiveDateTime::parse_from_str(string, format).map_err(parse_error_into)
}
fn for_format_date(string: Value, format: &str) -> Result<NaiveDateTime> {
let string: String = string.cast()?;
let string: &str = string.as_str();
Ok(NaiveDate::parse_from_str(string, format)
.map_err(parse_error_into)?
.and_hms(0, 0, 0))
}
fn for_format_time(string: Value, format: &str) -> Result<NaiveDateTime> {
let string: String = string.cast()?;
let string: &str = string.as_str();
Ok(NaiveDateTime::from_timestamp(0, 0)
.date()
.and_time(NaiveTime::parse_from_str(string, format).map_err(parse_error_into)?))
}
fn try_rules(try_value: &Value, rules: &[i64]) -> Result<NaiveDateTime> {
rules
.iter()
.find_map(|try_rule| try_value.clone().cast_with_rule((*try_rule).into()).ok())
.ok_or_else(|| ValueError::ParseError(try_value.clone(), "TIMESTAMP").into())
}
const TRY_RULES_TIMESTAMP: [i64; 1] = [000];
const TRY_RULES_DATETIME: [i64; 9] = [010, 011, 020, 021, 030, 031, 060, 062, 063];
const TRY_RULES_DATE: [i64; 6] = [022, 033, 032, 061, 064, 040]; const TRY_RULES_TIME: [i64; 2] = [100, 101];
match rule {
Value::Null => try_rules(&self, &TRY_RULES_TIMESTAMP)
.or_else(|_| try_rules(&self, &TRY_RULES_DATETIME))
.or_else(|_| try_rules(&self, &TRY_RULES_DATE))
.or_else(|_| try_rules(&self, &TRY_RULES_TIME)),
Value::Bool(true) => try_rules(&self, &TRY_RULES_TIMESTAMP),
Value::Str(custom) => match custom.as_str() {
"TIMESTAMP" => try_rules(&self, &TRY_RULES_TIMESTAMP),
"DATETIME" => try_rules(&self, &TRY_RULES_DATETIME),
"DATE" => try_rules(&self, &TRY_RULES_DATE),
"TIME" => try_rules(&self, &TRY_RULES_TIME),
custom_format => for_format_datetime(self.clone(), custom_format)
.or_else(|_| for_format_date(self.clone(), custom_format))
.or_else(|_| for_format_time(self, custom_format)),
},
Value::I64(000) => {
self.cast()
}
Value::I64(020) => for_format_datetime(self, "%F %T"),
Value::I64(021) => for_format_datetime(self, "%F %R"),
Value::I64(022) => for_format_date(self, "%F"),
Value::I64(030) => for_format_datetime(self, "%F %r"),
Value::I64(031) => for_format_datetime(self, "%I:%M %p"),
Value::I64(032) => for_format_date(self, "%v"),
Value::I64(033) => for_format_date(self, "%e-%b-%y"),
Value::I64(040) => for_format_date(self, "%Y%m%d"),
Value::I64(060) => for_format_datetime(self, "%d/%m/%Y %H:%M"),
Value::I64(061) => for_format_date(self, "%d/%m/%Y"),
Value::I64(062) => for_format_datetime(self, "%d/%m/%Y %H:%M:%S"),
Value::I64(063) => for_format_datetime(self, "%d%m%Y %H:%M:%S"),
Value::I64(064) => for_format_date(self, "%d%m%Y"),
Value::I64(100) => for_format_time(self, "%T"),
Value::I64(101) => for_format_time(self, "%R"),
_ => Err(ValueError::InvalidConversionRule.into()),
}
}
}