#[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::{FromOptionalTushareValue, FromTushareValue};
#[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),
}
}
}
}
}