citum_schema_style/locale/date_patterns.rs
1/*
2SPDX-License-Identifier: MIT OR Apache-2.0
3SPDX-FileCopyrightText: © 2023-2026 Bruce D'Arcus and Citum contributors
4*/
5
6use super::Locale;
7use super::message::MessageArgs;
8use super::types::MessageSyntax;
9
10impl Locale {
11 /// Resolve a `pattern.date-*` message with locale-specific year/month/day
12 /// components.
13 ///
14 /// Returns `Some(rendered)` only when the locale carries an MF2 message
15 /// at `message_id` and the evaluator produces output. Callers fall back
16 /// to the engine's hardcoded English assembly on `None`.
17 ///
18 /// A component is forwarded to the evaluator only when non-empty; an
19 /// authored pattern that references `{$day}` therefore yields `None` if
20 /// the input date carries no day, letting the caller pick a shorter form.
21 ///
22 /// The day argument is taken as `Option<u32>` rather than a pre-formatted
23 /// string so the digit-to-string allocation is deferred until after the
24 /// message lookup succeeds - the common case for legacy locales (`en-US`,
25 /// every v1 file) is the lookup miss, which now incurs zero allocation.
26 ///
27 /// `zero_pad_day` mirrors `DateConfig::day_zero_pad`: when set, the day
28 /// component is formatted as two digits (`"07"`) rather than bare
29 /// (`"7"`) before being forwarded to the evaluator.
30 pub fn resolve_date_pattern(
31 &self,
32 message_id: &str,
33 year: Option<&str>,
34 month: Option<&str>,
35 day: Option<u32>,
36 zero_pad_day: bool,
37 ) -> Option<String> {
38 let message = self.messages.get(message_id)?;
39 if self.evaluation.message_syntax == MessageSyntax::Static {
40 return None;
41 }
42
43 let day_str = day.map(|d| {
44 if zero_pad_day {
45 format!("{d:02}")
46 } else {
47 d.to_string()
48 }
49 });
50 let args = MessageArgs {
51 year: year.filter(|s| !s.is_empty()),
52 month: month.filter(|s| !s.is_empty()),
53 day: day_str.as_deref(),
54 ..MessageArgs::default()
55 };
56 self.evaluator.evaluate(message, &args)
57 }
58
59 /// Resolve a shared-year `pattern.date-range-*` message with pre-formatted
60 /// endpoint fragments and their common year.
61 ///
62 /// The message is evaluated only for MF2 locales. Callers fall back to
63 /// their established date-form assembly when a locale has not authored
64 /// the requested interval pattern.
65 pub fn resolve_date_range_pattern(
66 &self,
67 message_id: &str,
68 start: &str,
69 end: &str,
70 year: Option<&str>,
71 ) -> Option<String> {
72 let message = self.messages.get(message_id)?;
73 if self.evaluation.message_syntax == MessageSyntax::Static {
74 return None;
75 }
76
77 let args = MessageArgs {
78 start: (!start.is_empty()).then_some(start),
79 end: (!end.is_empty()).then_some(end),
80 year: year.filter(|value| !value.is_empty()),
81 ..MessageArgs::default()
82 };
83 self.evaluator.evaluate(message, &args)
84 }
85}