1use nom::{
2 bytes::complete::{tag, take, take_till, take_while},
3 character::complete::{anychar, digit1, one_of},
4 combinator::{opt, peek},
5 Err, IResult, Needed,
6};
7use std::num::NonZeroUsize;
8use unicode_xid::UnicodeXID;
9
10fn parse_count(input: &str) -> IResult<&str, Count> {
11 let error = Err(Err::Error(nom::error::Error {
12 input: "",
13 code: nom::error::ErrorKind::Fail,
14 }));
15 if input.is_empty() {
16 return error;
17 }
18 if let (new_input, Some(arg)) = opt(parse_identifier)(input)? {
19 if let (new_new_input, Some(_)) = opt(tag("$"))(new_input)? {
20 Ok((new_new_input, Count::Parameter(Argument::Identifier(arg))))
21 } else {
22 Err(Err::Error(nom::error::Error {
23 input: new_input,
24 code: nom::error::ErrorKind::Fail,
25 }))
26 }
27 } else if let (new_input, Some(count)) = opt(digit1)(input)? {
28 Ok((new_input, Count::Integer(count.parse::<usize>().unwrap())))
29 } else {
30 error
31 }
32}
33
34fn parse_identifier(input: &str) -> IResult<&str, String> {
35 let (car, cdr) = (
36 input
37 .chars()
38 .nth(0)
39 .ok_or(Err::Incomplete(Needed::Size(unsafe {
40 NonZeroUsize::new_unchecked(1)
41 })))?,
42 &input[1..],
43 );
44 if !UnicodeXID::is_xid_start(car) {
45 return Err(Err::Error(nom::error::Error {
46 input: "",
47 code: nom::error::ErrorKind::Fail,
48 }));
49 }
50 let out: IResult<&str, &str> = take_while(UnicodeXID::is_xid_continue)(cdr);
51 let (input, cdr) = out?;
52 let out = format!("{}{}", car, cdr);
53 return Ok((input, out));
54}
55
56fn parse_argument(input: &str) -> IResult<&str, Argument> {
57 if let (new_input, Some(arg)) = opt(parse_identifier)(input)? {
58 Ok((new_input, Argument::Identifier(arg)))
59 } else if let (new_input, Some(arg)) = opt(digit1)(input)? {
60 Ok((new_input, Argument::Integer(arg.parse::<usize>().unwrap())))
61 } else {
62 Err(Err::Error(nom::error::Error {
63 input: "",
64 code: nom::error::ErrorKind::Fail,
65 }))
66 }
67}
68
69pub fn parse_fmt_spec(input: &str) -> IResult<&str, FormatSlot> {
70 let (input, arg) = if let (input, Some(ident)) = opt(parse_argument)(input)? {
72 (input, Some(ident))
73 } else {
74 (input, None)
75 };
76 let (input, fmt_spec) = if let (input, Some(_)) = opt(tag(":"))(input)? {
77 let (input, fill) = if let (input, Some(fill)) = opt(anychar)(input)? {
78 (input, Some(fill))
79 } else {
80 (input, None)
81 };
82 let (input, align) =
83 if let (input, Some(align)) = opt(one_of("<^>"))(input)? {
84 (
85 input,
86 Some(match align {
87 '<' => Align::Left,
88 '^' => Align::Center,
89 '>' => Align::Right,
90 _ => {
91 unreachable!()
92 }
93 }),
94 )
95 } else {
96 (input, None)
97 };
98 let (input, sign) = if let (input, Some(sign)) = opt(one_of("+-"))(input)? {
99 (
100 input,
101 Some(match sign {
102 '+' => Sign::Positive,
103 '-' => Sign::Negative,
104 _ => {
105 unreachable!()
106 }
107 }),
108 )
109 } else {
110 (input, None)
111 };
112 let (input, alternate) = if let (input, Some(_)) = opt(tag("#"))(input)? {
113 (input, true)
114 } else {
115 (input, false)
116 };
117 let (input, pad_with_zeros) =
118 if let (input, Some(_)) = opt(tag("0"))(input)? {
119 (input, true)
120 } else {
121 (input, false)
122 };
123 let (input, width) = if let (input, Some(width)) = opt(parse_count)(input)?
124 {
125 (input, Some(width))
126 } else {
127 (input, None)
128 };
129 let (input, percision) = if let (input, Some(_)) = opt(tag("."))(input)? {
130 let input = input;
131 if let (input, Some(percision)) = opt(parse_count)(input)? {
132 (input, Some(Precision::Count(percision)))
133 } else if let (input, Some(_)) = opt(tag("*"))(input)? {
134 (input, Some(Precision::SpecifiedPrecision))
135 } else {
136 (input, None)
137 }
138 } else {
139 (input, None)
140 };
141 let (input, kind) =
142 if let (input, Some(kind)) = opt(one_of("?oxXpbeE"))(input)? {
143 match kind {
144 'x' => {
145 if let (input, Some(_)) = opt(tag("?"))(input)? {
146 (input, Type::DebugLowerHex)
147 } else {
148 (input, Type::LowerHex)
149 }
150 }
151 'X' => {
152 if let (input, Some(_)) = opt(tag("?"))(input)? {
153 (input, Type::DebugUpperHex)
154 } else {
155 (input, Type::UpperHex)
156 }
157 }
158 _ => (
159 input,
160 match kind {
161 '?' => Type::Debug,
162 'o' => Type::Octal,
163 'p' => Type::Pointer,
164 'b' => Type::Binary,
165 'e' => Type::LowerExp,
166 'E' => Type::UpperExp,
167 _ => {
168 unreachable!()
169 }
170 },
171 ),
172 }
173 } else {
174 (input, Type::None)
175 };
176 (
177 input,
178 Some(FormatSpec {
179 fill,
180 align,
181 sign,
182 alternate,
183 pad_with_zeros,
184 width,
185 percision,
186 kind,
187 }),
188 )
189 } else {
190 (input, None)
191 };
192 Ok((input, FormatSlot { arg, fmt_spec }))
193}
194
195#[derive(Debug, PartialEq, Eq)]
196enum State {
197 Text,
198 MaybeFormat,
199}
200
201pub fn parse_fmt_str(input: &str) -> Result<FormatString, &str> {
202 let mut strings: Vec<String> = vec![];
203 let mut slots: Vec<PossibleFormatSlot> = vec![];
204 let mut input: String = input.to_string();
205 let mut state: State = State::MaybeFormat;
206 loop {
207 println!(
208 "INPUT: {:?}, strings: {:?}, state: {:?}",
209 input, strings, state
210 );
211 if input.is_empty() {
212 break;
213 }
214 let part1: IResult<&str, &str> = peek(tag("{{"))(&input);
215 let part2: IResult<&str, &str> = peek(tag("}}"))(&input);
216 if !part1.is_err() || !part2.is_err() {
217 if state == State::MaybeFormat {
218 strings.push("".to_string());
219 }
220 let out: IResult<&str, &str> = take(1usize)(&input);
221 let (input_str, push2str) = out.unwrap();
222 let mut input_str = input_str.to_string();
223 slots.push(match push2str {
224 "{" => PossibleFormatSlot::LeftBrace,
225 "}" => PossibleFormatSlot::RightBrace,
226 _ => {
227 unreachable!()
228 }
229 });
230 input_str.remove(0);
231 input = input_str.to_string();
232 state = State::MaybeFormat;
233 continue;
234 }
235 if input.starts_with("{") {
237 if state == State::MaybeFormat {
238 strings.push("".to_string());
239 state = State::Text;
240 }
241 let out: IResult<&str, &str> = take_till(|chr| chr == '}')(&input);
242 let (input_str, format) = out.unwrap();
243 let mut input_str = input_str.to_string();
244 let mut format = format.to_string();
245 if format.is_empty() {
246 strings.push(input_str);
247 break;
248 }
249 if !input_str.is_empty() {
250 input_str.remove(0);
251 }
252 format.remove(0);
253 if format.is_empty() {
254 strings.push(input_str);
255 break;
256 }
257 input = input_str.to_string();
258 let next = parse_fmt_spec(&format);
259 if next.is_err() {
260 return Err("Invalid format string. (slot didn't parse)");
261 }
262 let (left, slot) = next.unwrap();
263 if !left.is_empty() {
264 return Err("Invalid format string. (slot had additional data)");
265 }
266 slots.push(PossibleFormatSlot::FormatSlot(slot));
267 state = State::MaybeFormat;
268 } else {
269 assert_eq!(state, State::MaybeFormat);
270 let cloned_input = input.clone();
271 let out: IResult<&str, &str> = take_till(|chr| chr == '{' || chr == '}')(&cloned_input);
272 let (input_str, push2str) = out.unwrap();
273 input = input_str.to_string();
274 strings.push(push2str.to_string());
275 state = State::Text;
276 }
277 }
278 if state == State::MaybeFormat {
279 strings.push("".to_string());
280 }
281 Ok(FormatString {
282 text: strings,
283 maybe_fmt: slots,
284 })
285}
286
287pub type Fill = char;
288
289#[derive(Debug, PartialEq, Eq)]
290pub enum Align {
291 Left,
292 Center,
293 Right,
294}
295
296#[derive(Debug, PartialEq, Eq)]
297pub enum Sign {
298 Positive,
299 Negative,
300}
301
302#[derive(Debug, PartialEq, Eq)]
303pub enum Count {
304 Parameter(Argument),
305 Integer(usize),
306}
307
308#[derive(Debug, PartialEq, Eq)]
309pub enum Precision {
310 Count(Count),
311 SpecifiedPrecision,
312}
313
314#[derive(Debug, PartialEq, Eq)]
315pub enum Type {
316 Debug,
317 DebugLowerHex,
318 DebugUpperHex,
319 Octal,
320 LowerHex,
321 UpperHex,
322 Pointer,
323 Binary,
324 LowerExp,
325 UpperExp,
326 None,
327}
328
329#[derive(Debug, PartialEq, Eq)]
330pub struct FormatSpec {
331 pub fill: Option<Fill>,
332 pub align: Option<Align>,
333 pub sign: Option<Sign>,
334 pub alternate: bool,
335 pub pad_with_zeros: bool,
336 pub width: Option<Count>,
337 pub percision: Option<Precision>,
338 pub kind: Type,
339}
340
341#[derive(Debug, PartialEq, Eq)]
342pub enum Argument {
343 Identifier(String),
344 Integer(usize),
345}
346
347#[derive(Debug, PartialEq, Eq)]
348pub struct FormatSlot {
349 pub arg: Option<Argument>,
350 pub fmt_spec: Option<FormatSpec>,
351}
352
353#[derive(Debug, PartialEq, Eq)]
354pub enum PossibleFormatSlot {
355 FormatSlot(FormatSlot),
356 LeftBrace,
357 RightBrace,
358}
359
360#[derive(Debug, PartialEq, Eq)]
361pub struct FormatString {
362 pub text: Vec<String>,
363 pub maybe_fmt: Vec<PossibleFormatSlot>,
364}