1use regex::{CaptureMatches, Captures, Regex};
8
9use crate::{Alignment, ArgumentResult, ArgumentSpec, Count, Error, Format, FormatType, Position};
10
11lazy_static::lazy_static! {
12static ref PYTHON_RE: Regex = Regex::new(r"(?x)
14 %
15 (?:\((?P<key>\w+)\))? # Mapping key
16 (?P<flags>[\#0\- +]*)? # Conversion flags
17 (?P<width>\*|\d+)? # Minimum field width
18 (?:.(?P<precision>\*|\d+))? # Precision after decimal point
19 [hlL]* # Ignored length modifier
20 (?P<type>[diouxXeEfFgGcrs%]) # Conversion type
21 ").unwrap();
22}
23
24fn parse_next(captures: Captures<'_>) -> ArgumentResult<'_> {
25 let group = captures.get(0).unwrap();
26
27 let position = captures
28 .name("key")
29 .map(|m| Position::Key(m.as_str()))
30 .unwrap_or_else(|| Position::Auto);
31
32 let format = match &captures["type"] {
33 "d" | "i" | "u" => FormatType::Display,
34 "o" => FormatType::Octal,
35 "x" => FormatType::LowerHex,
36 "X" => FormatType::UpperHex,
37 "e" => FormatType::LowerExp,
38 "E" => FormatType::UpperExp,
39 "f" | "F" | "g" | "G" => FormatType::Display,
40 "c" | "s" => FormatType::Display,
41 "r" => FormatType::Object,
42 "%" => FormatType::Literal("%"),
43 s => return Err(Error::BadFormat(s.chars().next().unwrap_or_default())),
44 };
45
46 let mut alternate = false;
47 let mut pad_zero = false;
48 let mut alignment = Alignment::Right;
49 let mut sign = false;
50
51 if let Some(flags) = captures.name("flags") {
52 for flag in flags.as_str().chars() {
53 match flag {
54 '#' => alternate = true,
55 '0' => pad_zero = true,
56 '-' => alignment = Alignment::Left,
57 ' ' => (), '+' => sign = true,
59 c => unreachable!("unknown conversion flag \"{}\"", c),
60 }
61 }
62 }
63
64 let width = captures.name("width").and_then(|m| match m.as_str() {
65 "*" => Some(Count::Ref(Position::Auto)),
66 value => value.parse().ok().map(Count::Value),
67 });
68
69 let precision = captures.name("precision").and_then(|m| match m.as_str() {
70 "*" => Some(Count::Ref(Position::Auto)),
71 value => value.parse().ok().map(Count::Value),
72 });
73
74 let spec = ArgumentSpec::new(group.start(), group.end())
75 .with_position(position)
76 .with_format(format)
77 .with_alternate(alternate)
78 .with_zeros(pad_zero)
79 .with_alignment(alignment)
80 .with_sign(sign)
81 .with_width(width)
82 .with_precision(precision);
83
84 Ok(spec)
85}
86
87#[derive(Debug)]
91pub struct PythonIter<'f> {
92 captures: CaptureMatches<'static, 'f>,
93}
94
95impl<'f> PythonIter<'f> {
96 fn new(format: &'f str) -> Self {
97 PythonIter {
98 captures: PYTHON_RE.captures_iter(format),
99 }
100 }
101}
102
103impl<'f> Iterator for PythonIter<'f> {
104 type Item = ArgumentResult<'f>;
105
106 fn next(&mut self) -> Option<Self::Item> {
107 self.captures.next().map(parse_next)
108 }
109}
110
111#[derive(Debug)]
145pub struct PythonFormat;
146
147impl<'f> Format<'f> for PythonFormat {
148 type Iter = PythonIter<'f>;
149
150 fn iter_args(&self, format: &'f str) -> Result<Self::Iter, Error<'f>> {
151 Ok(PythonIter::new(format))
152 }
153}