log4rs/encode/pattern/
parser.rs

1// cribbed to a large extent from libfmt_macros
2use std::{iter::Peekable, str::CharIndices};
3
4#[derive(Clone, Eq, PartialEq, Hash, Debug)]
5pub enum Piece<'a> {
6    Text(&'a str),
7    Argument {
8        formatter: Formatter<'a>,
9        parameters: Parameters,
10    },
11    Error(String),
12}
13
14#[derive(Clone, Eq, PartialEq, Hash, Debug)]
15pub struct Formatter<'a> {
16    pub name: &'a str,
17    pub args: Vec<Vec<Piece<'a>>>,
18}
19
20#[derive(Clone, Eq, PartialEq, Hash, Debug)]
21pub struct Parameters {
22    pub fill: char,
23    pub right_truncate: bool,
24    pub align: Alignment,
25    pub min_width: Option<usize>,
26    pub max_width: Option<usize>,
27}
28
29#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
30pub enum Alignment {
31    Left,
32    Right,
33}
34
35#[derive(Clone, Debug)]
36pub struct Parser<'a> {
37    pattern: &'a str,
38    it: Peekable<CharIndices<'a>>,
39}
40
41impl<'a> Parser<'a> {
42    pub fn new(pattern: &'a str) -> Parser<'a> {
43        Parser {
44            pattern,
45            it: pattern.char_indices().peekable(),
46        }
47    }
48
49    fn consume(&mut self, ch: char) -> bool {
50        match self.it.peek() {
51            Some(&(_, c)) if c == ch => {
52                self.it.next();
53                true
54            }
55            _ => false,
56        }
57    }
58
59    fn argument(&mut self) -> Piece<'a> {
60        let formatter = match self.formatter() {
61            Ok(formatter) => formatter,
62            Err(err) => return Piece::Error(err),
63        };
64
65        Piece::Argument {
66            formatter,
67            parameters: self.parameters(),
68        }
69    }
70
71    fn formatter(&mut self) -> Result<Formatter<'a>, String> {
72        Ok(Formatter {
73            name: self.name(),
74            args: self.args()?,
75        })
76    }
77
78    fn name(&mut self) -> &'a str {
79        let start = match self.it.peek() {
80            Some(&(pos, ch)) if ch.is_alphabetic() => {
81                self.it.next();
82                pos
83            }
84            _ => return "",
85        };
86
87        loop {
88            match self.it.peek() {
89                Some(&(_, ch)) if ch.is_alphanumeric() => {
90                    self.it.next();
91                }
92                Some(&(end, _)) => return &self.pattern[start..end],
93                None => return &self.pattern[start..],
94            }
95        }
96    }
97
98    fn args(&mut self) -> Result<Vec<Vec<Piece<'a>>>, String> {
99        let mut args = vec![];
100        while let Some(&(_, '(')) = self.it.peek() {
101            args.push(self.arg()?);
102        }
103        Ok(args)
104    }
105
106    fn arg(&mut self) -> Result<Vec<Piece<'a>>, String> {
107        if !self.consume('(') {
108            return Ok(vec![]);
109        }
110
111        let mut arg = vec![];
112        loop {
113            if self.consume(')') {
114                return Ok(arg);
115            } else {
116                match self.next() {
117                    Some(piece) => arg.push(piece),
118                    None => return Err("unclosed '('".to_owned()),
119                }
120            }
121        }
122    }
123
124    fn parameters(&mut self) -> Parameters {
125        let mut params = Parameters {
126            fill: ' ',
127            right_truncate: true,
128            align: Alignment::Left,
129            min_width: None,
130            max_width: None,
131        };
132
133        if !self.consume(':') {
134            return params;
135        }
136
137        if let Some(&(_, ch)) = self.it.peek() {
138            match self.it.clone().nth(1) {
139                Some((_, '<')) | Some((_, '>')) => {
140                    self.it.next();
141                    params.fill = ch;
142                }
143                _ => {}
144            }
145        }
146
147        if self.consume('<') {
148            params.align = Alignment::Left;
149        } else if self.consume('>') {
150            params.align = Alignment::Right;
151        }
152
153        if self.consume('-') {
154            params.right_truncate = false;
155        }
156
157        if let Some(min_width) = self.integer() {
158            params.min_width = Some(min_width);
159        }
160
161        if self.consume('.') {
162            if let Some(max_width) = self.integer() {
163                params.max_width = Some(max_width);
164            }
165        }
166
167        params
168    }
169
170    fn integer(&mut self) -> Option<usize> {
171        let mut cur = 0;
172        let mut found = false;
173        while let Some(&(_, ch)) = self.it.peek() {
174            if let Some(digit) = ch.to_digit(10) {
175                cur = cur * 10 + digit as usize;
176                found = true;
177                self.it.next();
178            } else {
179                break;
180            }
181        }
182
183        if found {
184            Some(cur)
185        } else {
186            None
187        }
188    }
189
190    fn text(&mut self, start: usize) -> Piece<'a> {
191        while let Some(&(pos, ch)) = self.it.peek() {
192            match ch {
193                '{' | '}' | '(' | ')' | '\\' => return Piece::Text(&self.pattern[start..pos]),
194                _ => {
195                    self.it.next();
196                }
197            }
198        }
199        Piece::Text(&self.pattern[start..])
200    }
201}
202
203impl<'a> Iterator for Parser<'a> {
204    type Item = Piece<'a>;
205
206    fn next(&mut self) -> Option<Piece<'a>> {
207        match self.it.peek() {
208            Some(&(_, '{')) => {
209                self.it.next();
210                if self.consume('{') {
211                    Some(Piece::Text("{"))
212                } else {
213                    let piece = self.argument();
214                    if self.consume('}') {
215                        Some(piece)
216                    } else {
217                        for _ in &mut self.it {}
218                        Some(Piece::Error("expected '}'".to_owned()))
219                    }
220                }
221            }
222            Some(&(_, '}')) => {
223                self.it.next();
224                if self.consume('}') {
225                    Some(Piece::Text("}"))
226                } else {
227                    Some(Piece::Error("unmatched '}'".to_owned()))
228                }
229            }
230            Some(&(_, '(')) => {
231                self.it.next();
232                if self.consume('(') {
233                    Some(Piece::Text("("))
234                } else {
235                    Some(Piece::Error("unexpected '('".to_owned()))
236                }
237            }
238            Some(&(_, ')')) => {
239                self.it.next();
240                if self.consume(')') {
241                    Some(Piece::Text(")"))
242                } else {
243                    Some(Piece::Error("unexpected ')'".to_owned()))
244                }
245            }
246            Some(&(_, '\\')) => {
247                self.it.next();
248                match self.it.peek() {
249                    Some(&(_, '{')) => {
250                        self.it.next();
251                        Some(Piece::Text("{"))
252                    }
253                    Some(&(_, '}')) => {
254                        self.it.next();
255                        Some(Piece::Text("}"))
256                    }
257                    Some(&(_, '(')) => {
258                        self.it.next();
259                        Some(Piece::Text("("))
260                    }
261                    Some(&(_, ')')) => {
262                        self.it.next();
263                        Some(Piece::Text(")"))
264                    }
265                    Some(&(_, '\\')) => {
266                        self.it.next();
267                        Some(Piece::Text("\\"))
268                    }
269                    _ => Some(Piece::Error("unexpected '\\'".to_owned())),
270                }
271            }
272            Some(&(pos, _)) => Some(self.text(pos)),
273            None => None,
274        }
275    }
276}