runtime_format/
compiled.rs

1use core::fmt;
2
3use crate::{parse::ParseSegment, FormatArgs, FormatError, FormatKey, ToFormatParser};
4
5/// Preparsed formatting terms.
6///
7/// This is faster if you will be using the same format string again and again with
8/// different inputs.
9///
10/// ```
11/// use runtime_format::{FormatArgs, FormatKey, FormatKeyError, ParsedFmt};
12/// use core::fmt;
13/// # struct DateTime;
14/// # impl DateTime { fn now() -> Self { Self } }
15/// # impl DateTime { fn day(&self) -> i32 { 25 } fn short_month_name(&self) -> &'static str { "Jan" } fn year(&self) -> i32 { 2023 } }
16/// # impl DateTime { fn hours(&self) -> i32 { 16 } fn minutes(&self) -> i32 { 27 } fn seconds(&self) -> i32 { 53 } }
17/// impl FormatKey for DateTime {
18///     fn fmt(&self, key: &str, f: &mut fmt::Formatter<'_>) -> Result<(), FormatKeyError> {
19///          // ...
20/// #        use core::fmt::Write;
21/// #        match key {
22/// #            "year"    => write!(f, "{}", self.year()).map_err(FormatKeyError::Fmt),
23/// #            "month"   => write!(f, "{}", self.short_month_name()).map_err(FormatKeyError::Fmt),
24/// #            "day"     => write!(f, "{}", self.day()).map_err(FormatKeyError::Fmt),
25/// #            "hours"   => write!(f, "{}", self.hours()).map_err(FormatKeyError::Fmt),
26/// #            "minutes" => write!(f, "{}", self.minutes()).map_err(FormatKeyError::Fmt),
27/// #            "seconds" => write!(f, "{}", self.seconds()).map_err(FormatKeyError::Fmt),
28/// #            _ => Err(FormatKeyError::UnknownKey),
29/// #        }
30///     }
31/// }
32///
33/// let now = DateTime::now();
34/// let fmt = ParsedFmt::new("{month} {day} {year} {hours}:{minutes}:{seconds}").unwrap();
35/// let args = FormatArgs::new(&fmt, &now);
36/// let expected = "Jan 25 2023 16:27:53";
37/// assert_eq!(args.to_string(), expected);
38pub struct ParsedFmt<'a> {
39    segments: tinyvec::TinyVec<[ParseSegment<'a>; 8]>,
40}
41
42impl<'a> ToFormatParser<'a> for ParsedFmt<'a> {
43    type Parser = std::iter::Copied<std::slice::Iter<'a, ParseSegment<'a>>>;
44
45    fn to_parser(&'a self) -> Self::Parser {
46        self.segments.iter().copied()
47    }
48
49    fn unparsed(_: Self::Parser) -> &'a str {
50        ""
51    }
52}
53
54impl fmt::Debug for ParsedFmt<'_> {
55    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
56        f.debug_struct("CompiledFormatter")
57            .field("segments", &self.segments)
58            .finish()
59    }
60}
61
62impl<'a> ParsedFmt<'a> {
63    /// Parse the given format string.
64    ///
65    /// # Errors
66    /// If the string could not be parsed, or there is a key that is unacceptable.
67    pub fn new(s: &'a str) -> Result<Self, FormatError<'a>> {
68        let mut segments = s.to_parser();
69        let this = Self {
70            segments: segments.by_ref().collect(),
71        };
72
73        if !segments.s.is_empty() {
74            Err(FormatError::Parse(segments.s))
75        } else {
76            Ok(this)
77        }
78    }
79
80    /// Return the keys that will be used when formatting.
81    ///
82    /// ```
83    /// # use runtime_format::ParsedFmt;
84    /// let fmt = "Hello, {recipient}. Hope you are having a nice {time_descriptor}.";
85    /// let parsed = ParsedFmt::new(fmt).unwrap();
86    /// let keys: Vec<_> = parsed.keys().collect();
87    /// assert_eq!(keys, ["recipient", "time_descriptor"]);
88    /// ```
89    pub fn keys(&self) -> impl Iterator<Item = &'_ str> {
90        self.segments.iter().filter_map(|segment| match segment {
91            ParseSegment::Literal(_) => None,
92            ParseSegment::Key(key) => Some(*key),
93        })
94    }
95
96    /// Combine this parsed format with the given values into a [`FormatArgs`]
97    pub fn with_args<'fs, 'fk, F: FormatKey>(
98        &'fs self,
99        fmt: &'fk F,
100    ) -> FormatArgs<'fs, 'fk, Self, F> {
101        FormatArgs::new(self, fmt)
102    }
103}
104
105impl<'a> TryFrom<&'a str> for ParsedFmt<'a> {
106    type Error = FormatError<'a>;
107
108    fn try_from(value: &'a str) -> Result<Self, Self::Error> {
109        Self::new(value)
110    }
111}
112
113impl<'a> FromIterator<ParseSegment<'a>> for ParsedFmt<'a> {
114    fn from_iter<T: IntoIterator<Item = ParseSegment<'a>>>(iter: T) -> Self {
115        Self {
116            segments: FromIterator::from_iter(iter),
117        }
118    }
119}
120
121#[cfg(test)]
122mod tests {
123    use core::fmt;
124
125    use crate::{FormatError, FormatKey, FormatKeyError};
126
127    use super::ParsedFmt;
128
129    struct Message;
130    impl FormatKey for Message {
131        fn fmt(&self, key: &str, f: &mut fmt::Formatter<'_>) -> Result<(), FormatKeyError> {
132            match key {
133                "recipient" => f.write_str("World").map_err(FormatKeyError::Fmt),
134                "time_descriptor" => f.write_str("morning").map_err(FormatKeyError::Fmt),
135                _ => Err(FormatKeyError::UnknownKey),
136            }
137        }
138    }
139
140    #[test]
141    fn compiled_happy_path() {
142        let formatter =
143            ParsedFmt::new("Hello, {recipient}. Hope you are having a nice {time_descriptor}.")
144                .unwrap();
145        let expected = "Hello, World. Hope you are having a nice morning.";
146        assert_eq!(formatter.with_args(&Message).to_string(), expected);
147    }
148
149    #[test]
150    fn compiled_failed_parsing() {
151        let err =
152            ParsedFmt::new("Hello, {recipient}. Hope you are having a nice {time_descriptor.")
153                .unwrap_err();
154        assert_eq!(err, FormatError::Parse("time_descriptor."));
155    }
156
157    #[test]
158    fn compiled_keys() {
159        let parsed =
160            ParsedFmt::new("Hello, {recipient}. Hope you are having a nice {time_descriptr}.")
161                .unwrap();
162        let keys: Vec<_> = parsed.keys().collect();
163        assert_eq!(keys, ["recipient", "time_descriptr"]);
164    }
165}