1use mf2_runtime::{
5 DateFields, DateLength, DateStyle, DateTime, DateTimeOptions, Dir, ErrorSink, FnContext,
6 FormatError, Function, Options, Sink, SubPartSink, TimePrecision, Value,
7};
8
9use crate::literal::parse_literal;
10use crate::options::{self, Kind, Own};
11use crate::plan::{Backend, Plan};
12use crate::zone;
13
14#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
16enum Role {
17 Function(Kind),
19 Unannotated,
22}
23
24#[derive(Clone, Copy, Debug)]
29pub struct DateTimeFunction<B = crate::DefaultBackend> {
30 role: Role,
31 backend: B,
32}
33
34impl<B> DateTimeFunction<B> {
35 pub const fn datetime(backend: B) -> Self {
37 DateTimeFunction {
38 role: Role::Function(Kind::DateTime),
39 backend,
40 }
41 }
42
43 pub const fn date(backend: B) -> Self {
45 DateTimeFunction {
46 role: Role::Function(Kind::Date),
47 backend,
48 }
49 }
50
51 pub const fn time(backend: B) -> Self {
53 DateTimeFunction {
54 role: Role::Function(Kind::Time),
55 backend,
56 }
57 }
58
59 pub const fn unannotated(backend: B) -> Self {
65 DateTimeFunction {
66 role: Role::Unannotated,
67 backend,
68 }
69 }
70
71 pub const fn backend(&self) -> &B {
73 &self.backend
74 }
75
76 const fn kind(&self) -> Kind {
77 match self.role {
78 Role::Function(k) => k,
79 Role::Unannotated => Kind::DateTime,
80 }
81 }
82}
83
84pub fn operand<'a>(v: &Value<'a>) -> Option<DateTime<'a>> {
91 match v {
92 Value::DateTime(d) => Some(*d),
93 Value::Str(s) => parse_literal(s),
94 Value::Custom(c) => c
95 .as_date_time()
96 .or_else(|| c.as_str().and_then(parse_literal)),
97 _ => None,
98 }
99}
100
101fn parts<'a>(kind: Kind, own: &Own<'a>) -> DateTimeOptions<'a> {
105 let date = DateStyle {
106 fields: own.fields.unwrap_or(DateFields::YearMonthDay),
107 length: own.length.unwrap_or(DateLength::Medium),
108 };
109 let time = own.precision.unwrap_or(TimePrecision::Minute);
110 let mut o = DateTimeOptions::default();
111 match kind {
112 Kind::DateTime => {
113 o.date = Some(date);
114 o.time = Some(time);
115 o.time_zone_style = own.zone_style;
116 }
117 Kind::Date => o.date = Some(date),
118 Kind::Time => {
119 o.time = Some(time);
120 o.time_zone_style = own.zone_style;
121 }
122 }
123 o
124}
125
126pub fn literal_options<'s>(
138 function: Option<&str>,
139 literal: &dyn Fn(&str) -> Option<&'s str>,
140) -> Option<DateTimeOptions<'s>> {
141 let kind = match function {
142 None => Kind::DateTime,
143 Some(name) => options::kind(name)?,
144 };
145 let own = match function {
146 None => Own::default(),
147 Some(_) => options::read_literals(kind, literal),
148 };
149 let mut o = parts(kind, &own);
150 o.hour12 = own.hour12;
151 o.calendar = own.calendar;
152 Some(o)
153}
154
155fn resolve<'a>(
160 kind: Kind,
161 cx: &FnContext<'_>,
162 operand: Option<&Value<'a>>,
163 own: &Own<'a>,
164 errs: &mut dyn ErrorSink,
165) -> Option<DateTime<'a>> {
166 let Some(mut d) = operand.and_then(self::operand) else {
167 errs.error(FormatError::BadOperand);
168 return None;
169 };
170 let inherited = d.options;
173 let mut o = parts(kind, own);
174 o.hour12 = own.hour12.or(inherited.hour12);
175 o.calendar = own.calendar.or(inherited.calendar);
176 match zone::place(cx, &mut d, own.time_zone.or(inherited.time_zone), errs) {
177 Ok(z) => o.time_zone = z,
178 Err(e) => {
179 errs.error(e);
180 return None;
181 }
182 }
183 d.options = o;
184 Some(d)
185}
186
187struct First(Option<FormatError>);
189
190impl ErrorSink for First {
191 fn error(&mut self, e: FormatError) {
192 if self.0.is_none() {
193 self.0 = Some(e);
194 }
195 }
196}
197
198impl<B> DateTimeFunction<B> {
199 fn value<'v>(
202 &self,
203 cx: &FnContext<'_>,
204 value: &Value<'v>,
205 ) -> Result<DateTime<'v>, FormatError> {
206 match (self.role, value) {
207 (Role::Unannotated, v) => {
208 let mut first = First(None);
209 match resolve(Kind::DateTime, cx, Some(v), &Own::default(), &mut first) {
210 Some(d) if first.0.is_none() => Ok(d),
211 _ => Err(first.0.unwrap_or(FormatError::BadOperand)),
212 }
213 }
214 (Role::Function(_), Value::DateTime(d)) => Ok(*d),
215 (Role::Function(_), _) => Err(FormatError::MessageFunctionError),
216 }
217 }
218}
219
220impl<B: Backend> Function for DateTimeFunction<B> {
221 fn resolve<'a>(
222 &self,
223 cx: &FnContext<'_>,
224 operand: Option<&Value<'a>>,
225 options: &Options<'_, 'a>,
226 errs: &mut dyn ErrorSink,
227 ) -> Option<Value<'a>> {
228 let kind = self.kind();
229 let own = options::read(kind, *options, errs);
230 resolve(kind, cx, operand, &own, errs).map(Value::DateTime)
231 }
232
233 fn formattable(&self, cx: &FnContext<'_>, value: &Value<'_>) -> Result<(), FormatError> {
234 let d = self.value(cx, value)?;
235 self.backend.supports(cx, &Plan::new(cx, &d))
236 }
237
238 fn format(&self, cx: &FnContext<'_>, value: &Value<'_>, out: &mut dyn Sink) {
239 if let Ok(d) = self.value(cx, value) {
240 self.backend.format(cx, &Plan::new(cx, &d), out);
241 }
242 }
243
244 fn format_parts(&self, cx: &FnContext<'_>, value: &Value<'_>, out: &mut dyn SubPartSink) {
245 if let Ok(d) = self.value(cx, value) {
246 self.backend.format_parts(cx, &Plan::new(cx, &d), out);
247 }
248 }
249
250 fn part_kind(&self) -> &'static str {
251 "datetime"
252 }
253
254 fn dir(&self, cx: &FnContext<'_>, value: &Value<'_>) -> Dir {
255 match self.value(cx, value) {
256 Ok(d) => self.backend.dir(cx, &Plan::new(cx, &d)),
257 Err(_) => Dir::Auto,
258 }
259 }
260}