1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
//! Formatting, but processed at runtime.
//!
//! ```
//! use runtime_format::{FormatArgs, FormatKey, FormatKeyError};
//! use core::fmt;
//! # struct DateTime;
//! # impl DateTime { fn now() -> Self { Self } }
//! # impl DateTime { fn day(&self) -> i32 { 25 } fn short_month_name(&self) -> &'static str { "Jan" } fn year(&self) -> i32 { 2023 } }
//! # impl DateTime { fn hours(&self) -> i32 { 16 } fn minutes(&self) -> i32 { 27 } fn seconds(&self) -> i32 { 53 } }
//! impl FormatKey for DateTime {
//!     fn fmt(&self, key: &str, f: &mut fmt::Formatter<'_>) -> Result<(), FormatKeyError> {
//!         use core::fmt::Write;
//!         match key {
//!             "year"    => write!(f, "{}", self.year()).map_err(FormatKeyError::Fmt),
//!             "month"   => write!(f, "{}", self.short_month_name()).map_err(FormatKeyError::Fmt),
//!             "day"     => write!(f, "{}", self.day()).map_err(FormatKeyError::Fmt),
//!             "hours"   => write!(f, "{}", self.hours()).map_err(FormatKeyError::Fmt),
//!             "minutes" => write!(f, "{}", self.minutes()).map_err(FormatKeyError::Fmt),
//!             "seconds" => write!(f, "{}", self.seconds()).map_err(FormatKeyError::Fmt),
//!             _ => Err(FormatKeyError::UnknownKey),
//!         }
//!     }
//! }
//!
//! let now = DateTime::now();
//! let fmt = "{month} {day} {year} {hours}:{minutes}:{seconds}";
//! let args = FormatArgs::new(fmt, &now);
//! let expected = "Jan 25 2023 16:27:53";
//! assert_eq!(args.to_string(), expected);
//! ```
//! 
//! See [`ParsedFmt`] if you need to repeatedly format a given string, but with
//! different args.
#![cfg_attr(not(feature = "std"), no_std)]

#[cfg(feature = "std")]
mod alloc_impls;
#[cfg(feature = "std")]
pub use alloc_impls::*;

#[cfg(feature = "std")]
mod compiled;
#[cfg(feature = "std")]
pub use compiled::ParsedFmt;

mod parse;
pub use parse::{FromStr, ParseSegment};

use core::cell::Cell;
use core::fmt;

#[derive(Debug, Clone, PartialEq)]
/// Error produced when formatting
pub enum FormatKeyError {
    /// The formatter had an error
    Fmt(fmt::Error),
    /// The requested key is unknown
    UnknownKey,
}

#[cfg(feature = "std")]
impl std::error::Error for FormatKeyError {
    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
        match self {
            FormatKeyError::Fmt(f) => Some(f),
            FormatKeyError::UnknownKey => None,
        }
    }
}

impl fmt::Display for FormatKeyError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            FormatKeyError::Fmt(_) => f.write_str("There was an error writing to the formatter"),
            FormatKeyError::UnknownKey => f.write_str("The requested key is unknown"),
        }
    }
}

impl From<fmt::Error> for FormatKeyError {
    fn from(value: fmt::Error) -> Self {
        FormatKeyError::Fmt(value)
    }
}

/// A trait like [`fmt::Display`] or [`fmt::Debug`] by with a keyed field.
///
/// It has a `fmt` method that accepts a [`fmt::Formatter`] argument. The important feature is the
/// `key` field which indicates what value should be written to the formatter.
///
/// ```
/// use runtime_format::{FormatArgs, FormatKey, FormatKeyError};
/// use core::fmt;
/// # struct DateTime;
/// # impl DateTime { fn now() -> Self { Self } }
/// # impl DateTime { fn day(&self) -> i32 { 25 } fn short_month_name(&self) -> &'static str { "Jan" } fn year(&self) -> i32 { 2023 } }
/// # impl DateTime { fn hours(&self) -> i32 { 16 } fn minutes(&self) -> i32 { 27 } fn seconds(&self) -> i32 { 53 } }
/// impl FormatKey for DateTime {
///     fn fmt(&self, key: &str, f: &mut fmt::Formatter<'_>) -> Result<(), FormatKeyError> {
///         use core::fmt::Write;
///         match key {
///             "year"    => write!(f, "{}", self.year()).map_err(FormatKeyError::Fmt),
///             "month"   => write!(f, "{}", self.short_month_name()).map_err(FormatKeyError::Fmt),
///             "day"     => write!(f, "{}", self.day()).map_err(FormatKeyError::Fmt),
///             "hours"   => write!(f, "{}", self.hours()).map_err(FormatKeyError::Fmt),
///             "minutes" => write!(f, "{}", self.minutes()).map_err(FormatKeyError::Fmt),
///             "seconds" => write!(f, "{}", self.seconds()).map_err(FormatKeyError::Fmt),
///             _ => Err(FormatKeyError::UnknownKey),
///         }
///     }
/// }
///
/// let now = DateTime::now();
/// let fmt = "{month} {day} {year} {hours}:{minutes}:{seconds}";
/// let args = FormatArgs::new(fmt, &now);
/// let expected = "Jan 25 2023 16:27:53";
/// assert_eq!(args.to_string(), expected);
/// ```
pub trait FormatKey {
    /// Write the value with the associated with the given `key` to the formatter.
    ///
    /// # Errors
    /// If the formatter returns an error, or if the key is unknown.
    fn fmt(&self, key: &str, f: &mut fmt::Formatter<'_>) -> Result<(), FormatKeyError>;
}

/// Turn a value into parsed formatting segments on the fly.
pub trait ToFormatParser<'a> {
    /// The Parser type that returns the [`ParseSegment`]s
    type Parser: Iterator<Item = ParseSegment<'a>>;

    /// Turn this value into the parser
    fn to_parser(&'a self) -> Self::Parser;
    /// Get the unparsed str from this parser.
    /// Used to determine if there was an error while parsing.
    fn unparsed(iter: Self::Parser) -> &'a str;
}

#[derive(Debug, Clone, PartialEq)]
#[non_exhaustive]
/// Error returned when formatting or parsing.
pub enum FormatError<'a> {
    /// The key was invalid
    Key(&'a str),
    /// Could not parse the string
    Parse(&'a str),
}

#[cfg(feature = "std")]
impl std::error::Error for FormatError<'_> {}

impl fmt::Display for FormatError<'_> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            FormatError::Key(key) => write!(f, "The requested key {key:?} is unknown"),
            FormatError::Parse(rest) => write!(f, "Failed to parse {rest:?}"),
        }
    }
}

/// Performs formatting.
pub struct FormatArgs<'a, FS: ?Sized, FK: ?Sized> {
    format_segments: &'a FS,
    format_keys: &'a FK,
    error: Cell<Option<FormatError<'a>>>,
}

impl<'a, FS: ?Sized, FK: ?Sized> FormatArgs<'a, FS, FK> {
    /// Create a new `FormatArgs` using the format specifier and the format keys
    pub fn new(format_specified: &'a FS, format_keys: &'a FK) -> Self {
        FormatArgs {
            format_segments: format_specified,
            format_keys,
            error: Cell::new(None),
        }
    }

    /// If there was an error when formatting, then that error is available here.
    pub fn status(&self) -> Result<(), FormatError<'a>> {
        match self.error.take() {
            Some(err) => Err(err),
            None => Ok(()),
        }
    }
}

impl<'a, FS, FK> fmt::Display for FormatArgs<'a, FS, FK>
where
    FS: ?Sized + ToFormatParser<'a>,
    FK: ?Sized + FormatKey,
{
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let mut segments = self.format_segments.to_parser();
        for segment in &mut segments {
            match segment {
                ParseSegment::Literal(s) => f.write_str(s)?,
                ParseSegment::Key(key) => match self.format_keys.fmt(key, f) {
                    Ok(_) => {}
                    Err(FormatKeyError::Fmt(e)) => return Err(e),
                    Err(FormatKeyError::UnknownKey) => {
                        self.error.set(Some(FormatError::Key(key)));
                        return Err(fmt::Error);
                    }
                },
            }
        }
        let remaining = FS::unparsed(segments);
        if !remaining.is_empty() {
            self.error.set(Some(FormatError::Parse(remaining)));
            Err(fmt::Error)
        } else {
            Ok(())
        }
    }
}

impl<'a, FS, FK> fmt::Debug for FormatArgs<'a, FS, FK>
where
    FS: ?Sized + ToFormatParser<'a>,
    FK: ?Sized + FormatKey,
{
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        fmt::Display::fmt(self, f)
    }
}

#[cfg(test)]
mod tests {
    use core::fmt::{self, Write};

    use crate::{FormatArgs, FormatError, FormatKey, FormatKeyError};

    struct WriteShim<'a> {
        w: &'a mut [u8],
        n: usize,
    }
    impl fmt::Write for WriteShim<'_> {
        fn write_str(&mut self, s: &str) -> fmt::Result {
            let remaining = self.w.len() - self.n;
            if let Some(prefix) = s.as_bytes().get(..remaining) {
                self.w[self.n..].copy_from_slice(prefix);
                self.n = self.w.len();
                Err(fmt::Error)
            } else {
                let n = self.n + s.len();
                self.w[self.n..n].copy_from_slice(s.as_bytes());
                self.n = n;
                Ok(())
            }
        }
    }

    fn format<'a, F: FormatKey>(
        s: &'a str,
        fmt: &'a F,
        f: impl FnOnce(&[u8]),
    ) -> Result<(), FormatError<'a>> {
        let mut bytes = WriteShim {
            w: &mut [0; 1024],
            n: 0,
        };
        let fmt = FormatArgs::new(s, fmt);
        let _ = write!(bytes, "{}", fmt);
        if let Some(err) = fmt.error.take() {
            return Err(err);
        }

        f(&bytes.w[..bytes.n]);
        Ok(())
    }

    struct Message;
    impl FormatKey for Message {
        fn fmt(&self, key: &str, f: &mut fmt::Formatter<'_>) -> Result<(), FormatKeyError> {
            match key {
                "recipient" => f.write_str("World").map_err(FormatKeyError::Fmt),
                "time_descriptor" => f.write_str("morning").map_err(FormatKeyError::Fmt),
                _ => Err(FormatKeyError::UnknownKey),
            }
        }
    }

    #[test]
    fn happy_path() {
        let format_str = "Hello, {recipient}. Hope you are having a nice {time_descriptor}.";
        let expected = "Hello, World. Hope you are having a nice morning.";
        format(format_str, &Message, |output| {
            assert_eq!(output, expected.as_bytes())
        })
        .unwrap();
    }

    #[test]
    fn missing_key() {
        let format_str = "Hello, {recipient}. Hope you are having a nice {time_descriptr}.";
        assert_eq!(
            format(format_str, &Message, |_| {}),
            Err(FormatError::Key("time_descriptr"))
        );
    }

    #[test]
    fn failed_parsing() {
        let format_str = "Hello, {recipient}. Hope you are having a nice {time_descriptor.";
        assert_eq!(
            format(format_str, &Message, |_| {}),
            Err(FormatError::Parse("time_descriptor."))
        );
    }

    #[test]
    fn escape_brackets() {
        let format_str = "You can make custom formatting terms using {{foo}!";
        let expected = "You can make custom formatting terms using {foo}!";
        format(format_str, &Message, |output| {
            assert_eq!(output, expected.as_bytes())
        })
        .unwrap();
    }
}