1use rustc_span::InnerSpan;
2
3use super::StrCursor as Cur;
4
5#[derive(#[automatically_derived]
impl<'a> ::core::clone::Clone for Substitution<'a> {
#[inline]
fn clone(&self) -> Substitution<'a> {
match self {
Substitution::Format(__self_0) =>
Substitution::Format(::core::clone::Clone::clone(__self_0)),
Substitution::Escape(__self_0) =>
Substitution::Escape(::core::clone::Clone::clone(__self_0)),
}
}
}Clone, #[automatically_derived]
impl<'a> ::core::marker::StructuralPartialEq for Substitution<'a> { }
#[automatically_derived]
impl<'a> ::core::cmp::PartialEq for Substitution<'a> {
#[inline]
fn eq(&self, other: &Substitution<'a>) -> bool {
let __self_discr = ::core::intrinsics::discriminant_value(self);
let __arg1_discr = ::core::intrinsics::discriminant_value(other);
__self_discr == __arg1_discr &&
match (self, other) {
(Substitution::Format(__self_0),
Substitution::Format(__arg1_0)) => __self_0 == __arg1_0,
(Substitution::Escape(__self_0),
Substitution::Escape(__arg1_0)) => __self_0 == __arg1_0,
_ => unsafe { ::core::intrinsics::unreachable() }
}
}
}PartialEq, #[automatically_derived]
impl<'a> ::core::fmt::Debug for Substitution<'a> {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
match self {
Substitution::Format(__self_0) =>
::core::fmt::Formatter::debug_tuple_field1_finish(f, "Format",
&__self_0),
Substitution::Escape(__self_0) =>
::core::fmt::Formatter::debug_tuple_field1_finish(f, "Escape",
&__self_0),
}
}
}Debug)]
7pub(crate) enum Substitution<'a> {
8 Format(Format<'a>),
10 Escape((usize, usize)),
12}
13
14impl ToString for Substitution<'_> {
15 fn to_string(&self) -> String {
16 match self {
17 Substitution::Format(fmt) => fmt.span.into(),
18 Substitution::Escape(_) => "%%".into(),
19 }
20 }
21}
22
23impl Substitution<'_> {
24 pub(crate) fn position(&self) -> InnerSpan {
25 match self {
26 Substitution::Format(fmt) => fmt.position,
27 &Substitution::Escape((start, end)) => InnerSpan::new(start, end),
28 }
29 }
30
31 pub(crate) fn set_position(&mut self, start: usize, end: usize) {
32 match self {
33 Substitution::Format(fmt) => fmt.position = InnerSpan::new(start, end),
34 Substitution::Escape(pos) => *pos = (start, end),
35 }
36 }
37
38 pub(crate) fn translate(&self) -> Result<String, Option<String>> {
43 match self {
44 Substitution::Format(fmt) => fmt.translate(),
45 Substitution::Escape(_) => Err(None),
46 }
47 }
48}
49
50#[derive(#[automatically_derived]
impl<'a> ::core::clone::Clone for Format<'a> {
#[inline]
fn clone(&self) -> Format<'a> {
Format {
span: ::core::clone::Clone::clone(&self.span),
parameter: ::core::clone::Clone::clone(&self.parameter),
flags: ::core::clone::Clone::clone(&self.flags),
width: ::core::clone::Clone::clone(&self.width),
precision: ::core::clone::Clone::clone(&self.precision),
length: ::core::clone::Clone::clone(&self.length),
type_: ::core::clone::Clone::clone(&self.type_),
position: ::core::clone::Clone::clone(&self.position),
}
}
}Clone, #[automatically_derived]
impl<'a> ::core::marker::StructuralPartialEq for Format<'a> { }
#[automatically_derived]
impl<'a> ::core::cmp::PartialEq for Format<'a> {
#[inline]
fn eq(&self, other: &Format<'a>) -> bool {
self.span == other.span && self.parameter == other.parameter &&
self.flags == other.flags && self.width == other.width &&
self.precision == other.precision &&
self.length == other.length && self.type_ == other.type_ &&
self.position == other.position
}
}PartialEq, #[automatically_derived]
impl<'a> ::core::fmt::Debug for Format<'a> {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
let names: &'static _ =
&["span", "parameter", "flags", "width", "precision", "length",
"type_", "position"];
let values: &[&dyn ::core::fmt::Debug] =
&[&self.span, &self.parameter, &self.flags, &self.width,
&self.precision, &self.length, &self.type_,
&&self.position];
::core::fmt::Formatter::debug_struct_fields_finish(f, "Format", names,
values)
}
}Debug)]
51pub(crate) struct Format<'a> {
53 span: &'a str,
55 parameter: Option<u16>,
57 flags: &'a str,
59 width: Option<Num>,
61 precision: Option<Num>,
63 length: Option<&'a str>,
65 type_: &'a str,
67 position: InnerSpan,
69}
70
71impl Format<'_> {
72 pub(crate) fn translate(&self) -> Result<String, Option<String>> {
77 use std::fmt::Write;
78
79 let (c_alt, c_zero, c_left, c_plus) = {
80 let mut c_alt = false;
81 let mut c_zero = false;
82 let mut c_left = false;
83 let mut c_plus = false;
84 for c in self.flags.chars() {
85 match c {
86 '#' => c_alt = true,
87 '0' => c_zero = true,
88 '-' => c_left = true,
89 '+' => c_plus = true,
90 _ => {
91 return Err(Some(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("the flag `{0}` is unknown or unsupported",
c))
})format!("the flag `{c}` is unknown or unsupported")));
92 }
93 }
94 }
95 (c_alt, c_zero, c_left, c_plus)
96 };
97
98 let fill = c_zero.then_some("0");
100
101 let align = c_left.then_some("<");
102
103 let sign = c_plus.then_some("+");
105
106 let alt = c_alt;
108
109 let width = match self.width {
110 Some(Num::Next) => {
111 return Err(Some(
113 "you have to use a positional or named parameter for the width".to_string(),
114 ));
115 }
116 w @ Some(Num::Arg(_)) => w,
117 w @ Some(Num::Num(_)) => w,
118 None => None,
119 };
120
121 let precision = self.precision;
122
123 let (type_, use_zero_fill, is_int) = match self.type_ {
127 "d" | "i" | "u" => (None, true, true),
128 "f" | "F" => (None, false, false),
129 "s" | "c" => (None, false, false),
130 "e" | "E" => (Some(self.type_), true, false),
131 "x" | "X" | "o" => (Some(self.type_), true, true),
132 "p" => (Some(self.type_), false, true),
133 "g" => (Some("e"), true, false),
134 "G" => (Some("E"), true, false),
135 _ => {
136 return Err(Some(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("the conversion specifier `{0}` is unknown or unsupported",
self.type_))
})format!(
137 "the conversion specifier `{}` is unknown or unsupported",
138 self.type_
139 )));
140 }
141 };
142
143 let (fill, width, precision) = match (is_int, width, precision) {
144 (true, Some(_), Some(_)) => {
145 return Err(Some(
147 "width and precision cannot both be specified for integer conversions"
148 .to_string(),
149 ));
150 }
151 (true, None, Some(p)) => (Some("0"), Some(p), None),
152 (true, w, None) => (fill, w, None),
153 (false, w, p) => (fill, w, p),
154 };
155
156 let align = match (self.type_, width.is_some(), align.is_some()) {
157 ("s", true, false) => Some(">"),
158 _ => align,
159 };
160
161 let (fill, zero_fill) = match (fill, use_zero_fill) {
162 (Some("0"), true) => (None, true),
163 (fill, _) => (fill, false),
164 };
165
166 let alt = match type_ {
167 Some("x" | "X") => alt,
168 _ => false,
169 };
170
171 let has_options = fill.is_some()
172 || align.is_some()
173 || sign.is_some()
174 || alt
175 || zero_fill
176 || width.is_some()
177 || precision.is_some()
178 || type_.is_some();
179
180 let cap = self.span.len() + if has_options { 2 } else { 0 };
182 let mut s = String::with_capacity(cap);
183
184 s.push('{');
185
186 if let Some(arg) = self.parameter {
187 s.write_fmt(format_args!("{0}", arg.checked_sub(1).ok_or(None)?))write!(s, "{}", arg.checked_sub(1).ok_or(None)?).map_err(|_| None)?;
188 }
189
190 if has_options {
191 s.push(':');
192
193 let align = if let Some(fill) = fill {
194 s.push_str(fill);
195 align.or(Some(">"))
196 } else {
197 align
198 };
199
200 if let Some(align) = align {
201 s.push_str(align);
202 }
203
204 if let Some(sign) = sign {
205 s.push_str(sign);
206 }
207
208 if alt {
209 s.push('#');
210 }
211
212 if zero_fill {
213 s.push('0');
214 }
215
216 if let Some(width) = width {
217 width.translate(&mut s).map_err(|_| None)?;
218 }
219
220 if let Some(precision) = precision {
221 s.push('.');
222 precision.translate(&mut s).map_err(|_| None)?;
223 }
224
225 if let Some(type_) = type_ {
226 s.push_str(type_);
227 }
228 }
229
230 s.push('}');
231 Ok(s)
232 }
233}
234
235#[derive(#[automatically_derived]
impl ::core::marker::Copy for Num { }Copy, #[automatically_derived]
#[doc(hidden)]
unsafe impl ::core::clone::TrivialClone for Num { }
#[automatically_derived]
impl ::core::clone::Clone for Num {
#[inline]
fn clone(&self) -> Num {
let _: ::core::clone::AssertParamIsClone<u16>;
*self
}
}Clone, #[automatically_derived]
impl ::core::marker::StructuralPartialEq for Num { }
#[automatically_derived]
impl ::core::cmp::PartialEq for Num {
#[inline]
fn eq(&self, other: &Num) -> bool {
let __self_discr = ::core::intrinsics::discriminant_value(self);
let __arg1_discr = ::core::intrinsics::discriminant_value(other);
__self_discr == __arg1_discr &&
match (self, other) {
(Num::Num(__self_0), Num::Num(__arg1_0)) =>
__self_0 == __arg1_0,
(Num::Arg(__self_0), Num::Arg(__arg1_0)) =>
__self_0 == __arg1_0,
_ => true,
}
}
}PartialEq, #[automatically_derived]
impl ::core::fmt::Debug for Num {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
match self {
Num::Num(__self_0) =>
::core::fmt::Formatter::debug_tuple_field1_finish(f, "Num",
&__self_0),
Num::Arg(__self_0) =>
::core::fmt::Formatter::debug_tuple_field1_finish(f, "Arg",
&__self_0),
Num::Next => ::core::fmt::Formatter::write_str(f, "Next"),
}
}
}Debug)]
237enum Num {
238 Num(u16),
245 Arg(u16),
247 Next,
249}
250
251impl Num {
252 fn from_str(s: &str, arg: Option<&str>) -> Option<Self> {
253 if let Some(arg) = arg {
254 arg.parse().ok().map(Num::Arg)
255 } else if s == "*" {
256 Some(Num::Next)
257 } else {
258 s.parse().ok().map(Num::Num)
259 }
260 }
261
262 fn translate(&self, s: &mut String) -> std::fmt::Result {
263 use std::fmt::Write;
264 match *self {
265 Num::Num(n) => s.write_fmt(format_args!("{0}", n))write!(s, "{n}"),
266 Num::Arg(n) => {
267 let n = n.checked_sub(1).ok_or(std::fmt::Error)?;
268 s.write_fmt(format_args!("{0}$", n))write!(s, "{n}$")
269 }
270 Num::Next => s.write_fmt(format_args!("*"))write!(s, "*"),
271 }
272 }
273}
274
275pub(crate) fn iter_subs(s: &str, start_pos: usize) -> Substitutions<'_> {
277 Substitutions { s, pos: start_pos }
278}
279
280pub(crate) struct Substitutions<'a> {
282 s: &'a str,
283 pos: usize,
284}
285
286impl<'a> Iterator for Substitutions<'a> {
287 type Item = Substitution<'a>;
288 fn next(&mut self) -> Option<Self::Item> {
289 let (mut sub, tail) = parse_next_substitution(self.s)?;
290 self.s = tail;
291 let InnerSpan { start, end } = sub.position();
292 sub.set_position(start + self.pos, end + self.pos);
293 self.pos += end;
294 Some(sub)
295 }
296
297 fn size_hint(&self) -> (usize, Option<usize>) {
298 (0, Some(self.s.len() / 2))
300 }
301}
302
303enum State {
304 Start,
305 Flags,
306 Width,
307 WidthArg,
308 Prec,
309 PrecInner,
310 Length,
311 Type,
312}
313
314fn parse_next_substitution(s: &str) -> Option<(Substitution<'_>, &str)> {
316 use self::State::*;
317
318 let at = {
319 let start = s.find('%')?;
320 if let '%' = s[start + 1..].chars().next()? {
321 return Some((Substitution::Escape((start, start + 2)), &s[start + 2..]));
322 }
323
324 Cur::new_at(s, start)
325 };
326
327 let start = at;
348 let mut at = at.at_next_cp()?;
350 let (mut c, mut next) = at.next_cp()?;
352
353 macro_rules! move_to {
355 ($cur:expr) => {{
356 at = $cur;
357 let (c_, next_) = at.next_cp()?;
358 c = c_;
359 next = next_;
360 }};
361 }
362
363 let fallback = move || {
367 Some((
368 Substitution::Format(Format {
369 span: start.slice_between(next).unwrap(),
370 parameter: None,
371 flags: "",
372 width: None,
373 precision: None,
374 length: None,
375 type_: at.slice_between(next).unwrap(),
376 position: InnerSpan::new(start.at, next.at),
377 }),
378 next.slice_after(),
379 ))
380 };
381
382 let mut state = Start;
384
385 let mut parameter: Option<u16> = None;
387 let mut flags: &str = "";
388 let mut width: Option<Num> = None;
389 let mut precision: Option<Num> = None;
390 let mut length: Option<&str> = None;
391 let mut type_: &str = "";
392 let end: Cur<'_>;
393
394 if let Start = state {
395 match c {
396 '1'..='9' => {
397 let end = at_next_cp_while(next, char::is_ascii_digit);
398 match end.next_cp() {
399 Some(('$', end2)) => {
401 state = Flags;
402 parameter = at.slice_between(end).unwrap().parse().ok();
403 { at = end2; let (c_, next_) = at.next_cp()?; c = c_; next = next_; };move_to!(end2);
404 }
405 Some(_) => {
407 state = Prec;
408 parameter = None;
409 flags = "";
410 width = at.slice_between(end).and_then(|num| Num::from_str(num, None));
411 if width.is_none() {
412 return fallback();
413 }
414 { at = end; let (c_, next_) = at.next_cp()?; c = c_; next = next_; };move_to!(end);
415 }
416 None => return fallback(),
418 }
419 }
420 _ => {
421 state = Flags;
422 parameter = None;
423 { at = at; let (c_, next_) = at.next_cp()?; c = c_; next = next_; };move_to!(at);
424 }
425 }
426 }
427
428 if let Flags = state {
429 let end = at_next_cp_while(at, is_flag);
430 state = Width;
431 flags = at.slice_between(end).unwrap();
432 { at = end; let (c_, next_) = at.next_cp()?; c = c_; next = next_; };move_to!(end);
433 }
434
435 if let Width = state {
436 match c {
437 '*' => {
438 state = WidthArg;
439 { at = next; let (c_, next_) = at.next_cp()?; c = c_; next = next_; };move_to!(next);
440 }
441 '1'..='9' => {
442 let end = at_next_cp_while(next, char::is_ascii_digit);
443 state = Prec;
444 width = at.slice_between(end).and_then(|num| Num::from_str(num, None));
445 if width.is_none() {
446 return fallback();
447 }
448 { at = end; let (c_, next_) = at.next_cp()?; c = c_; next = next_; };move_to!(end);
449 }
450 _ => {
451 state = Prec;
452 width = None;
453 { at = at; let (c_, next_) = at.next_cp()?; c = c_; next = next_; };move_to!(at);
454 }
455 }
456 }
457
458 if let WidthArg = state {
459 let end = at_next_cp_while(at, char::is_ascii_digit);
460 match end.next_cp() {
461 Some(('$', end2)) => {
462 state = Prec;
463 width = Num::from_str("", at.slice_between(end));
464 { at = end2; let (c_, next_) = at.next_cp()?; c = c_; next = next_; };move_to!(end2);
465 }
466 _ => {
467 state = Prec;
468 width = Some(Num::Next);
469 { at = end; let (c_, next_) = at.next_cp()?; c = c_; next = next_; };move_to!(end);
470 }
471 }
472 }
473
474 if let Prec = state {
475 match c {
476 '.' => {
477 state = PrecInner;
478 { at = next; let (c_, next_) = at.next_cp()?; c = c_; next = next_; };move_to!(next);
479 }
480 _ => {
481 state = Length;
482 precision = None;
483 { at = at; let (c_, next_) = at.next_cp()?; c = c_; next = next_; };move_to!(at);
484 }
485 }
486 }
487
488 if let PrecInner = state {
489 match c {
490 '*' => {
491 let end = at_next_cp_while(next, char::is_ascii_digit);
492 match end.next_cp() {
493 Some(('$', end2)) => {
494 state = Length;
495 precision = Num::from_str("*", next.slice_between(end));
496 { at = end2; let (c_, next_) = at.next_cp()?; c = c_; next = next_; };move_to!(end2);
497 }
498 _ => {
499 state = Length;
500 precision = Some(Num::Next);
501 { at = end; let (c_, next_) = at.next_cp()?; c = c_; next = next_; };move_to!(end);
502 }
503 }
504 }
505 '0'..='9' => {
506 let end = at_next_cp_while(next, char::is_ascii_digit);
507 state = Length;
508 precision = at.slice_between(end).and_then(|num| Num::from_str(num, None));
509 { at = end; let (c_, next_) = at.next_cp()?; c = c_; next = next_; };move_to!(end);
510 }
511 _ => return fallback(),
512 }
513 }
514
515 if let Length = state {
516 let c1_next1 = next.next_cp();
517 match (c, c1_next1) {
518 ('h', Some(('h', next1))) | ('l', Some(('l', next1))) => {
519 state = Type;
520 length = Some(at.slice_between(next1).unwrap());
521 { at = next1; let (c_, next_) = at.next_cp()?; c = c_; next = next_; };move_to!(next1);
522 }
523
524 ('h' | 'l' | 'L' | 'z' | 'j' | 't' | 'q', _) => {
525 state = Type;
526 length = Some(at.slice_between(next).unwrap());
527 { at = next; let (c_, next_) = at.next_cp()?; c = c_; next = next_; };move_to!(next);
528 }
529
530 ('I', _) => {
531 let end = next
532 .at_next_cp()
533 .and_then(|end| end.at_next_cp())
534 .map(|end| (next.slice_between(end).unwrap(), end));
535 let end = match end {
536 Some(("32" | "64", end)) => end,
537 _ => next,
538 };
539 state = Type;
540 length = Some(at.slice_between(end).unwrap());
541 { at = end; let (c_, next_) = at.next_cp()?; c = c_; next = next_; };move_to!(end);
542 }
543
544 _ => {
545 state = Type;
546 length = None;
547 { at = at; let (c_, next_) = at.next_cp()?; c = c_; next = next_; };move_to!(at);
548 }
549 }
550 }
551
552 if let Type = state {
553 type_ = at.slice_between(next).unwrap();
554
555 at = next;
557 }
558
559 let _ = c; end = at;
562 let position = InnerSpan::new(start.at, end.at);
563
564 let f = Format {
565 span: start.slice_between(end).unwrap(),
566 parameter,
567 flags,
568 width,
569 precision,
570 length,
571 type_,
572 position,
573 };
574 Some((Substitution::Format(f), end.slice_after()))
575}
576
577fn at_next_cp_while<F>(mut cur: Cur<'_>, mut pred: F) -> Cur<'_>
578where
579 F: FnMut(&char) -> bool,
580{
581 loop {
582 match cur.next_cp() {
583 Some((c, next)) if pred(&c) => {
584 cur = next;
585 }
586 _ => return cur,
587 }
588 }
589}
590
591fn is_flag(c: &char) -> bool {
592 #[allow(non_exhaustive_omitted_patterns)] match c {
'0' | '-' | '+' | ' ' | '#' | '\'' => true,
_ => false,
}matches!(c, '0' | '-' | '+' | ' ' | '#' | '\'')
593}
594
595#[cfg(test)]
596mod tests;