use alloc::boxed::Box;
use alloc::string::String;
use alloc::sync::Arc;
use mf2_runtime::{Arg, CustomValue, Date, DateTime, Time};
#[derive(Clone)]
pub enum Text {
Static(&'static str),
Shared(Arc<str>),
}
impl core::fmt::Debug for Text {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
crate::debug::quoted(f, self.as_str())
}
}
impl Text {
#[must_use]
pub fn as_str(&self) -> &str {
match self {
Text::Static(s) => s,
Text::Shared(s) => s,
}
}
}
impl From<&'static str> for Text {
fn from(s: &'static str) -> Text {
Text::Static(s)
}
}
impl From<String> for Text {
fn from(s: String) -> Text {
Text::Shared(Arc::from(s))
}
}
impl From<Arc<str>> for Text {
fn from(s: Arc<str>) -> Text {
Text::Shared(s)
}
}
pub trait ArgSource: Send + Sync {
fn arg_value(&self) -> ArgValue;
}
#[derive(Clone)]
pub struct DateTimeValue {
date: Date,
time: Time,
offset: Option<i32>,
zone: Option<Text>,
calendar: Option<Text>,
}
impl DateTimeValue {
#[must_use]
pub fn instant(epoch_ms: i64) -> Option<DateTimeValue> {
DateTime::from_epoch_ms(epoch_ms).map(DateTimeValue::from)
}
#[must_use]
pub fn floating(date: Date, time: Time) -> DateTimeValue {
DateTimeValue {
date,
time,
offset: None,
zone: None,
calendar: None,
}
}
#[must_use]
pub fn wall_time(date: Date, time: Time, zone: impl Into<Text>) -> DateTimeValue {
DateTimeValue::floating(date, time).with_zone(zone)
}
#[must_use]
pub fn with_zone(mut self, zone: impl Into<Text>) -> DateTimeValue {
self.zone = Some(zone.into());
self
}
#[must_use]
pub fn with_calendar(mut self, calendar: impl Into<Text>) -> DateTimeValue {
self.calendar = Some(calendar.into());
self
}
pub(crate) fn borrow(&self) -> DateTime<'_> {
let mut dt = DateTime::floating(self.date, self.time);
if let Some(seconds) = self.offset
&& let Some(with) = dt.with_offset(seconds)
{
dt = with;
}
if let Some(zone) = &self.zone {
dt = dt.in_zone(zone.as_str());
}
if let Some(calendar) = &self.calendar {
dt.options.calendar = Some(calendar.as_str());
}
dt
}
}
impl core::fmt::Debug for DateTimeValue {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
use crate::debug::{fixed, int};
f.write_str("DateTimeValue(")?;
int(f, i64::from(self.date.year()))?;
f.write_str("-")?;
fixed(f, u64::from(self.date.month()), 2)?;
f.write_str("-")?;
fixed(f, u64::from(self.date.day()), 2)?;
f.write_str("T")?;
fixed(f, u64::from(self.time.hour()), 2)?;
f.write_str(":")?;
fixed(f, u64::from(self.time.minute()), 2)?;
f.write_str(":")?;
fixed(f, u64::from(self.time.second()), 2)?;
f.write_str(".")?;
fixed(f, u64::from(self.time.millisecond()), 3)?;
if let Some(offset) = self.offset {
f.write_str(" offset ")?;
int(f, i64::from(offset))?;
}
if let Some(zone) = &self.zone {
f.write_str(" ")?;
f.write_str(zone.as_str())?;
}
if let Some(calendar) = &self.calendar {
f.write_str(" u-ca=")?;
f.write_str(calendar.as_str())?;
}
f.write_str(")")
}
}
impl From<DateTime<'_>> for DateTimeValue {
fn from(d: DateTime<'_>) -> DateTimeValue {
DateTimeValue {
date: d.date,
time: d.time,
offset: d.offset,
zone: d.zone.map(|z| Text::Shared(Arc::from(z))),
calendar: d.options.calendar.map(|c| Text::Shared(Arc::from(c))),
}
}
}
#[derive(Clone)]
#[non_exhaustive]
pub enum ArgValue {
Str(Text),
Int(i64),
Float(f64),
Decimal(Text),
DateTime(Arc<DateTimeValue>),
Custom(Arc<dyn CustomValue + Send + Sync>),
Source(Arc<dyn ArgSource>),
Unset,
}
impl ArgValue {
#[must_use]
#[doc(hidden)]
pub const fn str_static(s: &'static str) -> ArgValue {
ArgValue::Str(Text::Static(s))
}
#[must_use]
pub fn decimal(text: impl Into<Text>) -> ArgValue {
ArgValue::Decimal(text.into())
}
#[must_use]
pub fn custom<C: CustomValue + Send + Sync + 'static>(value: C) -> ArgValue {
ArgValue::Custom(Arc::new(value))
}
#[must_use]
pub fn source<S: ArgSource + 'static>(source: S) -> ArgValue {
ArgValue::Source(Arc::new(source))
}
pub(crate) fn resolve(&self) -> ArgValue {
let mut value = self.clone();
for _ in 0..4 {
let ArgValue::Source(source) = &value else {
return value;
};
value = source.arg_value();
}
ArgValue::Unset
}
}
impl core::fmt::Debug for ArgValue {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
use crate::debug::{float, int, quoted};
match self {
ArgValue::Str(t) => {
f.write_str("Str(")?;
quoted(f, t.as_str())?;
f.write_str(")")
}
ArgValue::Int(n) => {
f.write_str("Int(")?;
int(f, *n)?;
f.write_str(")")
}
ArgValue::Float(x) => {
f.write_str("Float(")?;
float(f, *x)?;
f.write_str(")")
}
ArgValue::Decimal(t) => {
f.write_str("Decimal(")?;
quoted(f, t.as_str())?;
f.write_str(")")
}
ArgValue::DateTime(d) => {
f.write_str("DateTime(")?;
core::fmt::Debug::fmt(&**d, f)?;
f.write_str(")")
}
ArgValue::Custom(_) => f.write_str("Custom(..)"),
ArgValue::Source(_) => f.write_str("Source(..)"),
ArgValue::Unset => f.write_str("Unset"),
}
}
}
impl From<Text> for ArgValue {
fn from(t: Text) -> ArgValue {
ArgValue::Str(t)
}
}
impl From<&str> for ArgValue {
fn from(s: &str) -> ArgValue {
ArgValue::Str(Text::Shared(Arc::from(s)))
}
}
impl From<String> for ArgValue {
fn from(s: String) -> ArgValue {
ArgValue::Str(Text::Shared(Arc::from(s)))
}
}
impl From<&String> for ArgValue {
fn from(s: &String) -> ArgValue {
ArgValue::Str(Text::Shared(Arc::from(s.as_str())))
}
}
impl From<Arc<str>> for ArgValue {
fn from(s: Arc<str>) -> ArgValue {
ArgValue::Str(Text::Shared(s))
}
}
impl From<char> for ArgValue {
fn from(c: char) -> ArgValue {
let mut buf = [0u8; 4];
ArgValue::Str(Text::Shared(Arc::from(&*c.encode_utf8(&mut buf))))
}
}
macro_rules! int_arg {
($($t:ty),*) => {$(
impl From<$t> for ArgValue {
#[inline]
fn from(n: $t) -> ArgValue {
ArgValue::Int(i64::from(n))
}
}
)*};
}
int_arg!(i8, i16, i32, i64, u8, u16, u32);
impl From<usize> for ArgValue {
#[inline]
fn from(n: usize) -> ArgValue {
ArgValue::Int(i64::try_from(n).unwrap_or(i64::MAX))
}
}
impl From<f64> for ArgValue {
#[inline]
fn from(x: f64) -> ArgValue {
ArgValue::Float(x)
}
}
impl From<f32> for ArgValue {
#[inline]
fn from(x: f32) -> ArgValue {
ArgValue::Float(f64::from(x))
}
}
impl From<DateTimeValue> for ArgValue {
fn from(d: DateTimeValue) -> ArgValue {
ArgValue::DateTime(Arc::new(d))
}
}
impl From<DateTime<'_>> for ArgValue {
fn from(d: DateTime<'_>) -> ArgValue {
ArgValue::DateTime(Arc::new(DateTimeValue::from(d)))
}
}
impl<C: CustomValue + Send + Sync + 'static> From<Arc<C>> for ArgValue {
fn from(c: Arc<C>) -> ArgValue {
ArgValue::Custom(c)
}
}
#[derive(Clone)]
pub struct ArgList(Repr);
pub(crate) const INLINE: usize = 4;
#[derive(Clone)]
enum Repr {
Inline(u8, [ArgValue; INLINE]),
Spill(Box<[ArgValue]>),
}
impl ArgList {
pub(crate) const EMPTY: ArgList = ArgList(Repr::Inline(
0,
[
ArgValue::Unset,
ArgValue::Unset,
ArgValue::Unset,
ArgValue::Unset,
],
));
pub(crate) const fn inline(n: u8, values: [ArgValue; INLINE]) -> ArgList {
ArgList(Repr::Inline(n, values))
}
pub(crate) fn spill(values: Box<[ArgValue]>) -> ArgList {
ArgList(Repr::Spill(values))
}
#[must_use]
pub fn as_slice(&self) -> &[ArgValue] {
match &self.0 {
Repr::Inline(n, values) => values.get(..usize::from(*n)).unwrap_or(&[]),
Repr::Spill(values) => values,
}
}
}
impl core::fmt::Debug for ArgList {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.write_str("[")?;
for (i, value) in self.as_slice().iter().enumerate() {
if i > 0 {
f.write_str(", ")?;
}
core::fmt::Debug::fmt(value, f)?;
}
f.write_str("]")
}
}
pub(crate) fn to_arg<'a>(value: &'a ArgValue, date: Option<&'a DateTime<'a>>) -> Arg<'a> {
match value {
ArgValue::Str(t) => Arg::Str(t.as_str()),
ArgValue::Int(n) => Arg::Int(*n),
ArgValue::Float(x) => Arg::Float(*x),
ArgValue::Decimal(t) => Arg::Decimal(t.as_str()),
ArgValue::DateTime(_) => match date {
Some(d) => Arg::DateTime(d),
None => Arg::Unset,
},
ArgValue::Custom(c) => Arg::Custom(&**c),
ArgValue::Source(_) | ArgValue::Unset => Arg::Unset,
}
}