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
#![allow(clippy::single_char_lifetime_names)]
use core::fmt;
use std::{
    borrow::Cow,
    collections::HashSet,
    ops::RangeInclusive,
};

use tracing_subscriber::fmt::format;

#[derive(Debug, thiserror::Error)]
pub enum Error {
    #[error("Illegal character {0}: {1}")]
    IllegalCharacter(char, String),

    #[error("Unknown field: {0}")]
    UnknownField(String),

    #[error("Empty field found")]
    EmptyField,
}

#[derive(Debug)]
pub struct Fmtr<'fmtstr> {
    /// The owned or static borrowed format string.
    fmt_str:      Cow<'fmtstr, str>,
    /// The ranges indexing `fmt_str` which 1-1 index `ordered_fields`.
    /// # Invariants
    /// Ranges are strictly within bounds of fmt_str
    field_ranges: Vec<RangeInclusive<usize>>,
    /// The names of fields indexed identically to field_ranges.
    field_names:  Vec<&'static str>,
}
impl<'fmtstr> Fmtr<'fmtstr> {
    /// Unrecognized fields should be an error
    /// # Errors
    /// We could encounter an illegal character or unknown field
    pub fn new(
        fmt_str: impl Into<Cow<'fmtstr, str>>,
        fields: &HashSet<&'static str>,
    ) -> Result<Self, Error> {
        let fmt_str = fmt_str.into();
        let mut start = None;
        let mut in_escape = false;
        let mut field_ranges: Vec<RangeInclusive<usize>> = Vec::with_capacity(fields.len());
        let mut field_names: Vec<&'static str> = Vec::with_capacity(fields.len());

        for (xi, x) in fmt_str.char_indices() {
            // inside a field match
            if let Some(strt) = start {
                // illegal chars
                if x == '{' || x == '\\' {
                    return Err(Error::IllegalCharacter(
                        x,
                        "found inside field name block".to_string(),
                    ));
                }
                // end match
                if x == '}' {
                    #[allow(clippy::integer_arithmetic)] // no overflow potential
                    if strt + 1 == xi {
                        return Err(Error::EmptyField);
                    }
                    field_ranges.push(strt..=xi);
                    // safe since we know the slice is non-empty and xi in bounds
                    // and no overflow potential
                    #[allow(clippy::indexing_slicing, clippy::integer_arithmetic)]
                    let ff = &fmt_str[(strt + 1)..xi];
                    if let Some(f) = fields.get(ff) {
                        field_names.push(*f);
                        start = None;
                    } else {
                        return Err(Error::UnknownField(ff.to_string()));
                    }
                }
            } else {
                // match escape
                if x == '\\' {
                    in_escape = true;
                    continue; // avoid the reset at bottom
                }
                // match unescaped brackets
                if !in_escape {
                    if x == '}' {
                        return Err(Error::IllegalCharacter(
                            x,
                            "found outside field name block".to_string(),
                        ));
                    } else if x == '{' {
                        start = Some(xi);
                    }
                }
            }
            in_escape = false;
        }
        Ok(Self {
            fmt_str,
            field_ranges,
            field_names,
        })
    }

    pub fn field_from_id(&self, i: usize) -> Option<&'static str> {
        self.field_names.get(i).copied()
    }

    /// # Errors
    /// If we fail to format the value a fmt Err will be returned
    ///
    /// # Panics
    /// Panics should only happen on bugs.
    #[allow(clippy::unwrap_in_result)]
    pub fn write<'writer>(
        &self,
        mut writer: format::Writer<'writer>,
        value_writer: &impl FieldValueWriter,
    ) -> fmt::Result {
        let mut last = 0;
        for (i, range) in self.field_ranges.iter().enumerate() {
            // write everything from the last field to start of next

            // safe since last range.start is inbounds as invariant of Fmtr.
            #[allow(clippy::indexing_slicing)]
            write!(writer.by_ref(), "{}", &self.fmt_str[last..*range.start()])?;

            // unwrap ok since idxs coming from same vec
            #[allow(clippy::unwrap_used)]
            let field = self.field_from_id(i).unwrap();

            value_writer.write_value(writer.by_ref(), field)?;

            // last may run off the end if last range
            #[allow(clippy::integer_arithmetic)]
            {
                last = range.end() + 1;
            }
        }
        // safe since if we're off the end it will just be an empty slice
        #[allow(clippy::indexing_slicing)]
        write!(writer, "{}", &self.fmt_str[last..])?;
        writeln!(writer)
    }
}

pub trait FieldValueWriter {
    /// # Errors
    /// If we fail to format the value a fmt Err will be returned
    fn write_value(&self, writer: format::Writer<'_>, field: &'static str) -> fmt::Result;
}