use chrono::Utc;
use serde_json::{Value, json};
use crate::datetime::{
DataDateTime, DataDuration, extract_datetime, is_datetime_object, is_duration_object,
};
use crate::{CompiledNode, ContextStack, DataLogic, Error, Result};
#[inline]
pub fn evaluate_datetime(
args: &[CompiledNode],
context: &mut ContextStack,
engine: &DataLogic,
) -> Result<Value> {
if args.is_empty() {
return Err(Error::InvalidArguments(
"datetime requires an argument".to_string(),
));
}
let value = engine.evaluate_node(&args[0], context)?;
if is_datetime_object(&value) {
return Ok(value);
}
if let Value::String(s) = &value
&& DataDateTime::parse(s).is_some()
{
return Ok(value);
}
Err(Error::InvalidArguments(
"Invalid datetime format".to_string(),
))
}
#[inline]
pub fn evaluate_timestamp(
args: &[CompiledNode],
context: &mut ContextStack,
engine: &DataLogic,
) -> Result<Value> {
if args.is_empty() {
return Err(Error::InvalidArguments(
"timestamp requires an argument".to_string(),
));
}
let value = engine.evaluate_node(&args[0], context)?;
if is_duration_object(&value) {
return Ok(value);
}
if let Value::String(s) = &value
&& let Some(duration) = DataDuration::parse(s)
{
return Ok(Value::String(duration.to_string()));
}
Err(Error::InvalidArguments(
"Invalid duration format".to_string(),
))
}
#[inline]
pub fn evaluate_parse_date(
args: &[CompiledNode],
context: &mut ContextStack,
engine: &DataLogic,
) -> Result<Value> {
if args.len() < 2 {
return Err(Error::InvalidArguments(
"parse_date requires date string and format".to_string(),
));
}
let date_str = engine.evaluate_node(&args[0], context)?;
let format_str = engine.evaluate_node(&args[1], context)?;
if let (Value::String(date), Value::String(format)) = (date_str, format_str) {
let chrono_format = format
.replace("yyyy", "%Y")
.replace("MM", "%m")
.replace("dd", "%d")
.replace("HH", "%H")
.replace("mm", "%M")
.replace("ss", "%S");
if let Some(dt) = DataDateTime::parse_with_format(&date, &chrono_format) {
return Ok(Value::String(dt.to_iso_string()));
}
}
Err(Error::InvalidArguments("Failed to parse date".to_string()))
}
#[inline]
pub fn evaluate_format_date(
args: &[CompiledNode],
context: &mut ContextStack,
engine: &DataLogic,
) -> Result<Value> {
if args.len() < 2 {
return Err(Error::InvalidArguments(
"format_date requires datetime and format".to_string(),
));
}
let datetime_val = engine.evaluate_node(&args[0], context)?;
let format_str = engine.evaluate_node(&args[1], context)?;
let dt = if is_datetime_object(&datetime_val) {
extract_datetime(&datetime_val)
} else if let Value::String(s) = &datetime_val {
DataDateTime::parse(s)
} else {
None
};
if let (Some(datetime), Value::String(format)) = (dt, format_str) {
let chrono_format = if format == "z" {
format
} else {
format
.replace("yyyy", "%Y")
.replace("MM", "%m")
.replace("dd", "%d")
.replace("HH", "%H")
.replace("mm", "%M")
.replace("ss", "%S")
};
return Ok(Value::String(datetime.format(&chrono_format)));
}
Err(Error::InvalidArguments("Failed to format date".to_string()))
}
#[inline]
pub fn evaluate_date_diff(
args: &[CompiledNode],
context: &mut ContextStack,
engine: &DataLogic,
) -> Result<Value> {
if args.len() < 3 {
return Err(Error::InvalidArguments(
"date_diff requires two dates and a unit".to_string(),
));
}
let date1_val = engine.evaluate_node(&args[0], context)?;
let date2_val = engine.evaluate_node(&args[1], context)?;
let unit = engine.evaluate_node(&args[2], context)?;
let dt1 = if is_datetime_object(&date1_val) {
extract_datetime(&date1_val)
} else if let Value::String(s) = &date1_val {
DataDateTime::parse(s)
} else {
None
};
let dt2 = if is_datetime_object(&date2_val) {
extract_datetime(&date2_val)
} else if let Value::String(s) = &date2_val {
DataDateTime::parse(s)
} else {
None
};
if let (Some(datetime1), Some(datetime2), Value::String(unit_str)) = (dt1, dt2, unit) {
let diff = datetime1.diff_in_unit(&datetime2, &unit_str);
return Ok(json!(diff as i64));
}
Err(Error::InvalidArguments(
"Failed to calculate date difference".to_string(),
))
}
#[inline]
pub fn evaluate_now(
_args: &[CompiledNode],
_context: &mut ContextStack,
_engine: &DataLogic,
) -> Result<Value> {
let now = Utc::now();
let data_dt = DataDateTime {
dt: now,
original_offset: Some(0), };
Ok(Value::String(data_dt.to_iso_string()))
}