use alloc::boxed::Box;
use alloc::string::String;
use core::any::Any;
use crate::datetime::DateTime;
use crate::host::Host;
use crate::number::{Measure, Number};
use crate::parts::FallbackSource;
#[derive(Clone, Copy)]
#[non_exhaustive]
pub enum Arg<'a> {
Str(&'a str),
Int(i64),
Float(f64),
Decimal(&'a str),
Custom(&'a dyn CustomValue),
Unset,
DateTime(&'a DateTime<'a>),
}
impl<'a> From<&'a DateTime<'a>> for Arg<'a> {
#[inline]
fn from(d: &'a DateTime<'a>) -> Self {
Arg::DateTime(d)
}
}
impl<'a> From<&'a str> for Arg<'a> {
#[inline]
fn from(s: &'a str) -> Self {
Arg::Str(s)
}
}
impl<'a> From<&'a String> for Arg<'a> {
#[inline]
fn from(s: &'a String) -> Self {
Arg::Str(s)
}
}
macro_rules! int_arg {
($($t:ty),*) => {$(
impl From<$t> for Arg<'_> {
#[inline]
fn from(n: $t) -> Self {
Arg::Int(i64::from(n))
}
}
)*};
}
int_arg!(i8, i16, i32, i64, u8, u16, u32);
impl From<f64> for Arg<'_> {
#[inline]
fn from(x: f64) -> Self {
Arg::Float(x)
}
}
impl From<f32> for Arg<'_> {
#[inline]
fn from(x: f32) -> Self {
Arg::Float(f64::from(x))
}
}
pub trait CustomValue {
fn as_str(&self) -> Option<&str> {
None
}
fn as_number(&self) -> Option<Number> {
None
}
fn as_any(&self) -> Option<&dyn Any> {
None
}
fn as_date_time(&self) -> Option<DateTime<'_>> {
None
}
fn as_measure(&self) -> Option<Measure<'_>> {
None
}
}
#[non_exhaustive]
pub enum Value<'a> {
Str(&'a str),
Int(i64),
Float(f64),
Decimal(&'a str),
Number(Number),
Custom(&'a dyn CustomValue),
Boxed(Box<dyn Any>),
Fallback(FallbackSource<'a>),
DateTime(DateTime<'a>),
Measure(Measure<'a>),
}
impl<'a> Value<'a> {
pub fn from_arg(arg: Arg<'a>) -> Option<Value<'a>> {
Some(match arg {
Arg::Str(s) => Value::Str(s),
Arg::Int(n) => Value::Int(n),
Arg::Float(x) => Value::Float(x),
Arg::Decimal(s) => Value::Decimal(s),
Arg::Custom(c) => Value::Custom(c),
Arg::DateTime(d) => Value::DateTime(*d),
Arg::Unset => return None,
})
}
pub fn as_str(&self) -> Option<&str> {
match self {
Value::Str(s) | Value::Decimal(s) => Some(s),
Value::Custom(c) => c.as_str(),
_ => None,
}
}
pub fn to_number(&self, host: &dyn Host) -> Option<Number> {
match self {
Value::Str(s) | Value::Decimal(s) => Number::parse(s),
Value::Int(n) => Some(Number::from_i64(*n)),
Value::Float(x) => Number::from_f64(*x, host),
Value::Number(n) => Some(n.bare()),
Value::Measure(m) => Some(m.number.bare()),
Value::Custom(c) => c
.as_number()
.or_else(|| c.as_measure().map(|m| m.number.bare())),
Value::Boxed(_) | Value::Fallback(_) | Value::DateTime(_) => None,
}
}
pub fn digit_size(&self) -> Option<u8> {
crate::number::digit_size(self)
}
pub fn downcast_ref<T: Any>(&self) -> Option<&T> {
match self {
Value::Boxed(b) => b.downcast_ref(),
Value::Custom(c) => c.as_any()?.downcast_ref(),
_ => None,
}
}
pub(crate) fn try_copy(&self) -> Option<Value<'a>> {
Some(match self {
Value::Str(s) => Value::Str(s),
Value::Int(n) => Value::Int(*n),
Value::Float(x) => Value::Float(*x),
Value::Decimal(s) => Value::Decimal(s),
Value::Number(n) => Value::Number(n.clone()),
Value::Custom(c) => Value::Custom(*c),
Value::Fallback(f) => Value::Fallback(*f),
Value::Boxed(_) | Value::DateTime(_) | Value::Measure(_) => return None,
})
}
}