#[cfg(any(feature = "rust_decimal", feature = "bigdecimal", feature = "chrono", feature = "uuid"))]
use serde_json::Value;
#[cfg(any(feature = "rust_decimal", feature = "bigdecimal", feature = "chrono", feature = "uuid"))]
use crate::error::TushareError;
#[cfg(any(feature = "rust_decimal", feature = "bigdecimal", feature = "chrono", feature = "uuid"))]
use crate::traits::{FromTushareValue, FromOptionalTushareValue};
#[cfg(feature = "rust_decimal")]
mod rust_decimal_support {
use super::*;
use rust_decimal::Decimal;
impl FromTushareValue for Decimal {
fn from_tushare_value(value: &Value) -> Result<Self, TushareError> {
match value {
Value::String(s) => {
s.parse().map_err(|e| {
TushareError::ParseError(format!("Failed to parse decimal from string '{}': {}", s, e))
})
},
Value::Number(n) => {
if let Some(f) = n.as_f64() {
Decimal::try_from(f).map_err(|e| {
TushareError::ParseError(format!("Failed to convert number {} to decimal: {}", f, e))
})
} else {
Err(TushareError::ParseError(format!(
"Cannot convert number {:?} to decimal", n
)))
}
},
Value::Null => Err(TushareError::ParseError(
"Cannot convert null to decimal".to_string()
)),
_ => Err(TushareError::ParseError(format!(
"Cannot convert {:?} to decimal", value
))),
}
}
}
impl FromOptionalTushareValue for Decimal {
fn from_optional_tushare_value(value: &Value) -> Result<Option<Self>, TushareError> {
if value.is_null() {
Ok(None)
} else {
match value {
Value::String(s) if s.is_empty() => Ok(None),
_ => Decimal::from_tushare_value(value).map(Some)
}
}
}
}
}
#[cfg(feature = "bigdecimal")]
mod bigdecimal_support {
use super::*;
use bigdecimal::BigDecimal;
use std::str::FromStr;
impl FromTushareValue for BigDecimal {
fn from_tushare_value(value: &Value) -> Result<Self, TushareError> {
match value {
Value::String(s) => {
BigDecimal::from_str(s).map_err(|e| {
TushareError::ParseError(format!("Failed to parse BigDecimal from string '{}': {}", s, e))
})
},
Value::Number(n) => {
if let Some(f) = n.as_f64() {
BigDecimal::try_from(f).map_err(|e| {
TushareError::ParseError(format!("Failed to convert number {} to BigDecimal: {}", f, e))
})
} else {
Err(TushareError::ParseError(format!(
"Cannot convert number {:?} to BigDecimal", n
)))
}
},
Value::Null => Err(TushareError::ParseError(
"Cannot convert null to BigDecimal".to_string()
)),
_ => Err(TushareError::ParseError(format!(
"Cannot convert {:?} to BigDecimal", value
))),
}
}
}
impl FromOptionalTushareValue for BigDecimal {
fn from_optional_tushare_value(value: &Value) -> Result<Option<Self>, TushareError> {
if value.is_null() {
Ok(None)
} else {
match value {
Value::String(s) if s.is_empty() => Ok(None),
_ => BigDecimal::from_tushare_value(value).map(Some)
}
}
}
}
}
#[cfg(feature = "chrono")]
mod chrono_support {
use super::*;
use chrono::{DateTime, NaiveDate, NaiveDateTime, Utc};
impl FromTushareValue for NaiveDate {
fn from_tushare_value(value: &Value) -> Result<Self, TushareError> {
match value {
Value::String(s) => {
let formats = [
"%Y%m%d", "%Y-%m-%d", "%Y/%m/%d", "%d/%m/%Y", "%m/%d/%Y", "%d-%m-%Y", "%m-%d-%Y", "%d.%m.%Y", "%Y.%m.%d", "%Y年%m月%d日", "%Y-%m-%d %H:%M:%S", ];
for format in &formats {
if let Ok(date) = NaiveDate::parse_from_str(s, format) {
return Ok(date);
}
if format.contains("%H:%M:%S") {
if let Ok(datetime) = NaiveDateTime::parse_from_str(s, format) {
return Ok(datetime.date());
}
}
}
Err(TushareError::ParseError(format!(
"Failed to parse date from string '{}'. Supported formats: YYYYMMDD, YYYY-MM-DD, YYYY/MM/DD, DD/MM/YYYY, MM/DD/YYYY, DD-MM-YYYY, MM-DD-YYYY, DD.MM.YYYY, YYYY.MM.DD, YYYY年MM月DD日", s
)))
},
Value::Number(n) => {
if let Some(i) = n.as_i64() {
let date_str = i.to_string();
if date_str.len() == 8 {
NaiveDate::parse_from_str(&date_str, "%Y%m%d").map_err(|e| {
TushareError::ParseError(format!("Failed to parse date from number {}: {}", i, e))
})
} else {
Err(TushareError::ParseError(format!(
"Invalid date number format: {}. Expected YYYYMMDD", i
)))
}
} else {
Err(TushareError::ParseError(format!(
"Cannot convert number {:?} to date", n
)))
}
},
_ => Err(TushareError::ParseError(format!(
"Cannot convert {:?} to date", value
))),
}
}
}
impl FromOptionalTushareValue for NaiveDate {
fn from_optional_tushare_value(value: &Value) -> Result<Option<Self>, TushareError> {
if value.is_null() {
Ok(None)
} else {
match value {
Value::String(s) if s.is_empty() => Ok(None),
_ => NaiveDate::from_tushare_value(value).map(Some)
}
}
}
}
impl FromTushareValue for NaiveDateTime {
fn from_tushare_value(value: &Value) -> Result<Self, TushareError> {
match value {
Value::String(s) => {
if let Ok(dt) = NaiveDateTime::parse_from_str(s, "%Y%m%d %H:%M:%S") {
Ok(dt)
} else if let Ok(dt) = NaiveDateTime::parse_from_str(s, "%Y-%m-%d %H:%M:%S") {
Ok(dt)
} else if let Ok(dt) = NaiveDateTime::parse_from_str(s, "%Y/%m/%d %H:%M:%S") {
Ok(dt)
} else if let Ok(dt) = NaiveDateTime::parse_from_str(s, "%Y-%m-%dT%H:%M:%S") {
Ok(dt)
} else {
Err(TushareError::ParseError(format!(
"Failed to parse datetime from string '{}'. Expected formats: YYYYMMDD HH:MM:SS, YYYY-MM-DD HH:MM:SS, YYYY/MM/DD HH:MM:SS, or YYYY-MM-DDTHH:MM:SS", s
)))
}
},
_ => Err(TushareError::ParseError(format!(
"Cannot convert {:?} to datetime", value
))),
}
}
}
impl FromOptionalTushareValue for NaiveDateTime {
fn from_optional_tushare_value(value: &Value) -> Result<Option<Self>, TushareError> {
if value.is_null() {
Ok(None)
} else {
match value {
Value::String(s) if s.is_empty() => Ok(None),
_ => NaiveDateTime::from_tushare_value(value).map(Some)
}
}
}
}
impl FromTushareValue for DateTime<Utc> {
fn from_tushare_value(value: &Value) -> Result<Self, TushareError> {
match value {
Value::String(s) => {
if let Ok(dt) = DateTime::parse_from_rfc3339(s) {
Ok(dt.with_timezone(&Utc))
} else if let Ok(naive_dt) = NaiveDateTime::from_tushare_value(value) {
Ok(DateTime::from_naive_utc_and_offset(naive_dt, Utc))
} else {
Err(TushareError::ParseError(format!(
"Failed to parse UTC datetime from string '{}'", s
)))
}
},
_ => Err(TushareError::ParseError(format!(
"Cannot convert {:?} to UTC datetime", value
))),
}
}
}
impl FromOptionalTushareValue for DateTime<Utc> {
fn from_optional_tushare_value(value: &Value) -> Result<Option<Self>, TushareError> {
if value.is_null() {
Ok(None)
} else {
match value {
Value::String(s) if s.is_empty() => Ok(None),
_ => DateTime::<Utc>::from_tushare_value(value).map(Some)
}
}
}
}
}
#[cfg(feature = "uuid")]
mod uuid_support {
use super::*;
use uuid::Uuid;
impl FromTushareValue for Uuid {
fn from_tushare_value(value: &Value) -> Result<Self, TushareError> {
match value {
Value::String(s) => {
Uuid::parse_str(s).map_err(|e| {
TushareError::ParseError(format!("Failed to parse UUID from string '{}': {}", s, e))
})
},
_ => Err(TushareError::ParseError(format!(
"Cannot convert {:?} to UUID", value
))),
}
}
}
impl FromOptionalTushareValue for Uuid {
fn from_optional_tushare_value(value: &Value) -> Result<Option<Self>, TushareError> {
if value.is_null() {
Ok(None)
} else {
match value {
Value::String(s) if s.is_empty() => Ok(None),
_ => Uuid::from_tushare_value(value).map(Some)
}
}
}
}
}