log4rs/encode/pattern/
parser.rs1use 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}