1use crate::{PrintfError, Result};
4
5#[derive(Debug, Clone, PartialEq, Eq)]
9pub enum FormatElement<'a> {
10 Verbatim(&'a str),
12 Format(ConversionSpecifier),
14}
15
16#[derive(Debug, Clone, Copy, PartialEq, Eq)]
18pub struct ConversionSpecifier {
19 pub alt_form: bool,
21 pub zero_pad: bool,
23 pub left_adj: bool,
25 pub space_sign: bool,
27 pub force_sign: bool,
29 pub width: NumericParam,
31 pub precision: NumericParam,
33 pub conversion_type: ConversionType,
35}
36
37#[derive(Debug, Clone, Copy, PartialEq, Eq)]
39pub enum NumericParam {
40 Literal(i32),
42 FromArgument,
46}
47
48#[derive(Debug, Clone, Copy, PartialEq, Eq)]
50pub enum ConversionType {
51 DecInt,
53 OctInt,
55 HexIntLower,
57 HexIntUpper,
59 SciFloatLower,
61 SciFloatUpper,
63 DecFloatLower,
65 DecFloatUpper,
67 CompactFloatLower,
69 CompactFloatUpper,
71 Char,
73 String,
75 PercentSign,
77}
78
79pub fn parse_format_string(fmt: &str) -> Result<Vec<FormatElement>> {
104 let mut res = Vec::new();
106
107 let mut rem = fmt;
108
109 while !rem.is_empty() {
110 if let Some((verbatim_prefix, rest)) = rem.split_once('%') {
111 if !verbatim_prefix.is_empty() {
112 res.push(FormatElement::Verbatim(verbatim_prefix));
113 }
114 let (spec, rest) = take_conversion_specifier(rest)?;
115 res.push(FormatElement::Format(spec));
116 rem = rest;
117 } else {
118 res.push(FormatElement::Verbatim(rem));
119 break;
120 }
121 }
122
123 Ok(res)
124}
125
126fn take_conversion_specifier(s: &str) -> Result<(ConversionSpecifier, &str)> {
127 let mut spec = ConversionSpecifier {
128 alt_form: false,
129 zero_pad: false,
130 left_adj: false,
131 space_sign: false,
132 force_sign: false,
133 width: NumericParam::Literal(0),
134 precision: NumericParam::FromArgument, conversion_type: ConversionType::DecInt,
137 };
138
139 let mut s = s;
140
141 loop {
143 match s.chars().next() {
144 Some('#') => {
145 spec.alt_form = true;
146 }
147 Some('0') => {
148 spec.zero_pad = true;
149 }
150 Some('-') => {
151 spec.left_adj = true;
152 }
153 Some(' ') => {
154 spec.space_sign = true;
155 }
156 Some('+') => {
157 spec.force_sign = true;
158 }
159 _ => {
160 break;
161 }
162 }
163 s = &s[1..];
164 }
165 let (w, mut s) = take_numeric_param(s);
167 spec.width = w;
168 if matches!(s.chars().next(), Some('.')) {
170 s = &s[1..];
171 let (p, s2) = take_numeric_param(s);
172 spec.precision = p;
173 s = s2;
174 }
175 for len_spec in ["hh", "h", "ll", "l", "q", "L", "j", "z", "Z", "t"] {
177 if s.starts_with(len_spec) {
178 s = s.strip_prefix(len_spec).ok_or(PrintfError::ParseError)?;
179 break; }
181 }
182 spec.conversion_type = match s.chars().next() {
184 Some('i') | Some('d') | Some('u') => ConversionType::DecInt,
185 Some('o') => ConversionType::OctInt,
186 Some('x') => ConversionType::HexIntLower,
187 Some('X') => ConversionType::HexIntUpper,
188 Some('e') => ConversionType::SciFloatLower,
189 Some('E') => ConversionType::SciFloatUpper,
190 Some('f') => ConversionType::DecFloatLower,
191 Some('F') => ConversionType::DecFloatUpper,
192 Some('g') => ConversionType::CompactFloatLower,
193 Some('G') => ConversionType::CompactFloatUpper,
194 Some('c') | Some('C') => ConversionType::Char,
195 Some('s') | Some('S') => ConversionType::String,
196 Some('p') => {
197 spec.alt_form = true;
198 ConversionType::HexIntLower
199 }
200 Some('%') => ConversionType::PercentSign,
201 _ => {
202 return Err(PrintfError::ParseError);
203 }
204 };
205
206 if spec.precision == NumericParam::FromArgument {
207 let p = if spec.conversion_type == ConversionType::String {
209 i32::MAX
211 } else {
212 6
214 };
215 spec.precision = NumericParam::Literal(p);
216 }
217
218 Ok((spec, &s[1..]))
219}
220
221fn take_numeric_param(s: &str) -> (NumericParam, &str) {
222 match s.chars().next() {
223 Some('*') => (NumericParam::FromArgument, &s[1..]),
224 Some(digit) if ('1'..='9').contains(&digit) => {
225 let mut s = s;
226 let mut w = 0;
227 loop {
228 match s.chars().next() {
229 Some(digit) if ('0'..='9').contains(&digit) => {
230 w = 10 * w + (digit as i32 - '0' as i32);
231 }
232 _ => {
233 break;
234 }
235 }
236 s = &s[1..];
237 }
238 (NumericParam::Literal(w), s)
239 }
240 _ => (NumericParam::Literal(0), s),
241 }
242}