use crate::Engine;
use crate::arena::{DataValue, coerce_to_number_cfg};
use bumpalo::Bump;
use std::fmt::Display;
#[inline]
pub(super) fn write_into_arena<'a>(arena: &'a Bump, value: impl Display) -> &'a DataValue<'a> {
arena.alloc(DataValue::String(write_str_into_arena(arena, value)))
}
#[inline]
pub(super) fn write_str_into_arena(arena: &Bump, value: impl Display) -> &str {
use std::fmt::Write;
let mut buf = bumpalo::collections::String::new_in(arena);
write!(&mut buf, "{}", value).expect("bumpalo String write is infallible");
buf.into_bump_str()
}
#[inline]
fn extract_dt_dur(
av: &DataValue<'_>,
) -> (
Option<datavalue::DataDateTime>,
Option<datavalue::DataDuration>,
) {
use super::{extract_datetime, extract_duration};
let dt = extract_datetime(av);
let dur = if dt.is_none() {
extract_duration(av)
} else {
None
};
(dt, dur)
}
#[derive(Clone, Copy)]
pub(crate) enum Temporal {
DateTime(datavalue::DataDateTime),
Duration(datavalue::DataDuration),
}
impl Temporal {
#[inline]
pub(crate) fn of(av: &DataValue<'_>) -> Option<Self> {
match extract_dt_dur(av) {
(Some(dt), _) => Some(Temporal::DateTime(dt)),
(None, Some(dur)) => Some(Temporal::Duration(dur)),
(None, None) => None,
}
}
#[inline]
pub(crate) fn add(self, other: Self) -> Option<Self> {
match (self, other) {
(Temporal::DateTime(dt), Temporal::Duration(dur))
| (Temporal::Duration(dur), Temporal::DateTime(dt)) => {
Some(Temporal::DateTime(dt.add_duration(&dur)))
}
(Temporal::Duration(d1), Temporal::Duration(d2)) => {
Some(Temporal::Duration(d1.add(&d2)))
}
(Temporal::DateTime(_), Temporal::DateTime(_)) => None,
}
}
#[inline]
pub(crate) fn sub(self, other: Self) -> Option<Self> {
match (self, other) {
(Temporal::DateTime(d1), Temporal::DateTime(d2)) => {
Some(Temporal::Duration(d1.diff(&d2)))
}
(Temporal::DateTime(dt), Temporal::Duration(dur)) => {
Some(Temporal::DateTime(dt.sub_duration(&dur)))
}
(Temporal::Duration(d1), Temporal::Duration(d2)) => {
Some(Temporal::Duration(d1.sub(&d2)))
}
(Temporal::Duration(_), Temporal::DateTime(_)) => None,
}
}
#[inline]
pub(crate) fn into_value<'a>(self, arena: &'a Bump) -> &'a DataValue<'a> {
match self {
Temporal::DateTime(dt) => write_into_arena(arena, dt),
Temporal::Duration(dur) => write_into_arena(arena, dur),
}
}
}
#[inline]
pub(crate) fn datetime_subtract<'a>(
a_av: &'a DataValue<'a>,
b_av: &'a DataValue<'a>,
arena: &'a Bump,
) -> Option<&'a DataValue<'a>> {
let a = Temporal::of(a_av)?;
let b = Temporal::of(b_av)?;
Some(a.sub(b)?.into_value(arena))
}
#[inline]
pub(crate) fn datetime_add<'a>(
a_av: &'a DataValue<'a>,
b_av: &'a DataValue<'a>,
arena: &'a Bump,
) -> Option<&'a DataValue<'a>> {
let a = Temporal::of(a_av)?;
let b = Temporal::of(b_av)?;
Some(a.add(b)?.into_value(arena))
}
#[inline]
pub(crate) fn datetime_multiply<'a>(
a_av: &'a DataValue<'a>,
b_av: &'a DataValue<'a>,
engine: &Engine,
arena: &'a Bump,
) -> Option<&'a DataValue<'a>> {
let (_, a_dur) = extract_dt_dur(a_av);
let (_, b_dur) = extract_dt_dur(b_av);
if let (Some(dur), None) = (&a_dur, &b_dur)
&& let Some(factor) = coerce_to_number_cfg(b_av, engine)
{
return Some(write_into_arena(arena, dur.multiply(factor)));
}
if let (None, Some(dur)) = (&a_dur, &b_dur)
&& let Some(factor) = coerce_to_number_cfg(a_av, engine)
{
return Some(write_into_arena(arena, dur.multiply(factor)));
}
None
}
#[inline]
pub(crate) fn datetime_divide<'a>(
a_av: &'a DataValue<'a>,
b_av: &'a DataValue<'a>,
ctx: &mut crate::arena::ContextStack<'_>,
engine: &Engine,
arena: &'a Bump,
) -> Option<crate::Result<&'a DataValue<'a>>> {
let (_, a_dur) = extract_dt_dur(a_av);
let a_dur = a_dur?;
let divisor = coerce_to_number_cfg(b_av, engine)?;
if divisor == 0.0 {
return Some(Err(crate::operators::nan_error(
crate::operators::NanForm::Thrown,
ctx,
)));
}
Some(Ok(write_into_arena(arena, a_dur.divide(divisor))))
}