use mf2_runtime::{
DateFields, DateLength, DateStyle, DateTime, DateTimeOptions, Dir, ErrorSink, FnContext,
FormatError, Function, Options, Sink, SubPartSink, TimePrecision, Value,
};
use crate::literal::parse_literal;
use crate::options::{self, Kind, Own};
use crate::plan::{Backend, Plan};
use crate::zone;
#[derive(Clone, Copy, Debug)]
pub struct DateTimeFunction<B = crate::DefaultBackend> {
kind: Kind,
backend: B,
}
impl<B> DateTimeFunction<B> {
pub const fn datetime(backend: B) -> Self {
DateTimeFunction {
kind: Kind::DateTime,
backend,
}
}
pub const fn date(backend: B) -> Self {
DateTimeFunction {
kind: Kind::Date,
backend,
}
}
pub const fn time(backend: B) -> Self {
DateTimeFunction {
kind: Kind::Time,
backend,
}
}
pub const fn backend(&self) -> &B {
&self.backend
}
const fn kind(&self) -> Kind {
self.kind
}
}
pub fn operand<'a>(v: &Value<'a>) -> Option<DateTime<'a>> {
match v {
Value::DateTime(d) => Some(*d),
Value::Str(s) => parse_literal(s),
Value::Custom(c) => c
.as_date_time()
.or_else(|| c.as_str().and_then(parse_literal)),
_ => None,
}
}
fn parts<'a>(kind: Kind, own: &Own<'a>) -> DateTimeOptions<'a> {
let date = DateStyle {
fields: own.fields.unwrap_or(DateFields::YearMonthDay),
length: own.length.unwrap_or(DateLength::Medium),
};
let time = own.precision.unwrap_or(TimePrecision::Minute);
let mut o = DateTimeOptions::default();
match kind {
Kind::DateTime => {
o.date = Some(date);
o.time = Some(time);
o.time_zone_style = own.zone_style;
}
Kind::Date => o.date = Some(date),
Kind::Time => {
o.time = Some(time);
o.time_zone_style = own.zone_style;
}
}
o
}
pub fn literal_options<'s>(
function: &str,
literal: &dyn Fn(&str) -> Option<&'s str>,
) -> Option<DateTimeOptions<'s>> {
let kind = options::kind(function)?;
let own = options::read_literals(kind, literal);
let mut o = parts(kind, &own);
o.hour12 = own.hour12;
o.calendar = own.calendar;
Some(o)
}
fn resolve<'a>(
kind: Kind,
cx: &FnContext<'_>,
operand: Option<&Value<'a>>,
own: &Own<'a>,
errs: &mut dyn ErrorSink,
) -> Option<DateTime<'a>> {
let Some(mut d) = operand.and_then(self::operand) else {
errs.error(FormatError::BadOperand);
return None;
};
let inherited = d.options;
let mut o = parts(kind, own);
o.hour12 = own.hour12.or(inherited.hour12);
o.calendar = own.calendar.or(inherited.calendar);
match zone::place(cx, &mut d, own.time_zone.or(inherited.time_zone), errs) {
Ok(z) => o.time_zone = z,
Err(e) => {
errs.error(e);
return None;
}
}
d.options = o;
Some(d)
}
impl<B> DateTimeFunction<B> {
fn value<'v>(value: &Value<'v>) -> Result<DateTime<'v>, FormatError> {
match value {
Value::DateTime(d) => Ok(*d),
_ => Err(FormatError::MessageFunctionError),
}
}
}
impl<B: Backend> Function for DateTimeFunction<B> {
fn resolve<'a>(
&self,
cx: &FnContext<'_>,
operand: Option<&Value<'a>>,
options: &Options<'_, 'a>,
errs: &mut dyn ErrorSink,
) -> Option<Value<'a>> {
let kind = self.kind();
let own = options::read(kind, *options, errs);
resolve(kind, cx, operand, &own, errs).map(Value::DateTime)
}
fn formattable(&self, cx: &FnContext<'_>, value: &Value<'_>) -> Result<(), FormatError> {
let d = Self::value(value)?;
self.backend.supports(cx, &Plan::new(cx, &d))
}
fn format(&self, cx: &FnContext<'_>, value: &Value<'_>, out: &mut dyn Sink) {
if let Ok(d) = Self::value(value) {
self.backend.format(cx, &Plan::new(cx, &d), out);
}
}
fn format_parts(&self, cx: &FnContext<'_>, value: &Value<'_>, out: &mut dyn SubPartSink) {
if let Ok(d) = Self::value(value) {
self.backend.format_parts(cx, &Plan::new(cx, &d), out);
}
}
fn part_kind(&self) -> &'static str {
"datetime"
}
fn dir(&self, cx: &FnContext<'_>, value: &Value<'_>) -> Dir {
match Self::value(value) {
Ok(d) => self.backend.dir(cx, &Plan::new(cx, &d)),
Err(_) => Dir::Auto,
}
}
}