2 use super::strcursor
::StrCursor
as Cur
;
3 use rustc_span
::InnerSpan
;
5 /// Represents a single `printf`-style substitution.
6 #[derive(Clone, PartialEq, Debug)]
7 pub enum Substitution
<'a
> {
8 /// A formatted output substitution with its internal byte offset.
10 /// A literal `%%` escape.
14 impl<'a
> Substitution
<'a
> {
15 pub fn as_str(&self) -> &str {
17 Substitution
::Format(ref fmt
) => fmt
.span
,
18 Substitution
::Escape
=> "%%",
22 pub fn position(&self) -> Option
<InnerSpan
> {
24 Substitution
::Format(ref fmt
) => Some(fmt
.position
),
29 pub fn set_position(&mut self, start
: usize, end
: usize) {
30 if let Substitution
::Format(ref mut fmt
) = self {
31 fmt
.position
= InnerSpan
::new(start
, end
);
35 /// Translate this substitution into an equivalent Rust formatting directive.
37 /// This ignores cases where the substitution does not have an exact equivalent, or where
38 /// the substitution would be unnecessary.
39 pub fn translate(&self) -> Option
<String
> {
41 Substitution
::Format(ref fmt
) => fmt
.translate(),
42 Substitution
::Escape
=> None
,
47 #[derive(Clone, PartialEq, Debug)]
48 /// A single `printf`-style formatting directive.
49 pub struct Format
<'a
> {
50 /// The entire original formatting directive.
52 /// The (1-based) parameter to be converted.
53 pub parameter
: Option
<u16>,
56 /// Minimum width of the output.
57 pub width
: Option
<Num
>,
58 /// Precision of the conversion.
59 pub precision
: Option
<Num
>,
60 /// Length modifier for the conversion.
61 pub length
: Option
<&'a
str>,
62 /// Type of parameter being converted.
64 /// Byte offset for the start and end of this formatting directive.
65 pub position
: InnerSpan
,
69 /// Translate this directive into an equivalent Rust formatting directive.
71 /// Returns `None` in cases where the `printf` directive does not have an exact Rust
72 /// equivalent, rather than guessing.
73 pub fn translate(&self) -> Option
<String
> {
76 let (c_alt
, c_zero
, c_left
, c_plus
) = {
77 let mut c_alt
= false;
78 let mut c_zero
= false;
79 let mut c_left
= false;
80 let mut c_plus
= false;
81 for c
in self.flags
.chars() {
90 (c_alt
, c_zero
, c_left
, c_plus
)
93 // Has a special form in Rust for numbers.
94 let fill
= c_zero
.then_some("0");
96 let align
= c_left
.then_some("<");
98 // Rust doesn't have an equivalent to the `' '` flag.
99 let sign
= c_plus
.then_some("+");
101 // Not *quite* the same, depending on the type...
104 let width
= match self.width
{
106 // NOTE: Rust doesn't support this.
109 w @
Some(Num
::Arg(_
)) => w
,
110 w @
Some(Num
::Num(_
)) => w
,
114 let precision
= self.precision
;
116 // NOTE: although length *can* have an effect, we can't duplicate the effect in Rust, so
117 // we just ignore it.
119 let (type_
, use_zero_fill
, is_int
) = match self.type_
{
120 "d" | "i" | "u" => (None
, true, true),
121 "f" | "F" => (None
, false, false),
122 "s" | "c" => (None
, false, false),
123 "e" | "E" => (Some(self.type_
), true, false),
124 "x" | "X" | "o" => (Some(self.type_
), true, true),
125 "p" => (Some(self.type_
), false, true),
126 "g" => (Some("e"), true, false),
127 "G" => (Some("E"), true, false),
131 let (fill
, width
, precision
) = match (is_int
, width
, precision
) {
132 (true, Some(_
), Some(_
)) => {
133 // Rust can't duplicate this insanity.
136 (true, None
, Some(p
)) => (Some("0"), Some(p
), None
),
137 (true, w
, None
) => (fill
, w
, None
),
138 (false, w
, p
) => (fill
, w
, p
),
141 let align
= match (self.type_
, width
.is_some(), align
.is_some()) {
142 ("s", true, false) => Some(">"),
146 let (fill
, zero_fill
) = match (fill
, use_zero_fill
) {
147 (Some("0"), true) => (None
, true),
148 (fill
, _
) => (fill
, false),
151 let alt
= match type_
{
152 Some("x" | "X") => alt
,
156 let has_options
= fill
.is_some()
162 || precision
.is_some()
165 // Initialise with a rough guess.
166 let cap
= self.span
.len() + if has_options { 2 }
else { 0 }
;
167 let mut s
= String
::with_capacity(cap
);
171 if let Some(arg
) = self.parameter
{
172 write
!(s
, "{}", arg
.checked_sub(1)?
).ok()?
;
178 let align
= if let Some(fill
) = fill
{
185 if let Some(align
) = align
{
189 if let Some(sign
) = sign
{
201 if let Some(width
) = width
{
202 width
.translate(&mut s
).ok()?
;
205 if let Some(precision
) = precision
{
207 precision
.translate(&mut s
).ok()?
;
210 if let Some(type_
) = type_
{
220 /// A general number used in a `printf` formatting directive.
221 #[derive(Copy, Clone, PartialEq, Debug)]
223 // The range of these values is technically bounded by `NL_ARGMAX`... but, at least for GNU
224 // libc, it apparently has no real fixed limit. A `u16` is used here on the basis that it
225 // is *vanishingly* unlikely that *anyone* is going to try formatting something wider, or
226 // with more precision, than 32 thousand positions which is so wide it couldn't possibly fit
228 /// A specific, fixed value.
230 /// The value is derived from a positional argument.
232 /// The value is derived from the "next" unconverted argument.
237 fn from_str(s
: &str, arg
: Option
<&str>) -> Self {
238 if let Some(arg
) = arg
{
239 Num
::Arg(arg
.parse().unwrap_or_else(|_
| panic
!("invalid format arg `{:?}`", arg
)))
243 Num
::Num(s
.parse().unwrap_or_else(|_
| panic
!("invalid format num `{:?}`", s
)))
247 fn translate(&self, s
: &mut String
) -> std
::fmt
::Result
{
250 Num
::Num(n
) => write
!(s
, "{}", n
),
252 let n
= n
.checked_sub(1).ok_or(std
::fmt
::Error
)?
;
255 Num
::Next
=> write
!(s
, "*"),
260 /// Returns an iterator over all substitutions in a given string.
261 pub fn iter_subs(s
: &str, start_pos
: usize) -> Substitutions
<'_
> {
262 Substitutions { s, pos: start_pos }
265 /// Iterator over substitutions in a string.
266 pub struct Substitutions
<'a
> {
271 impl<'a
> Iterator
for Substitutions
<'a
> {
272 type Item
= Substitution
<'a
>;
273 fn next(&mut self) -> Option
<Self::Item
> {
274 let (mut sub
, tail
) = parse_next_substitution(self.s
)?
;
277 Substitution
::Format(_
) => {
278 if let Some(inner_span
) = sub
.position() {
279 sub
.set_position(inner_span
.start
+ self.pos
, inner_span
.end
+ self.pos
);
280 self.pos
+= inner_span
.end
;
283 Substitution
::Escape
=> self.pos
+= 2,
288 fn size_hint(&self) -> (usize, Option
<usize>) {
289 // Substitutions are at least 2 characters long.
290 (0, Some(self.s
.len() / 2))
305 /// Parse the next substitution from the input string.
306 pub fn parse_next_substitution(s
: &str) -> Option
<(Substitution
<'_
>, &str)> {
310 let start
= s
.find('
%'
)?
;
311 if let '
%'
= s
[start
+ 1..].chars().next()?
{
312 return Some((Substitution
::Escape
, &s
[start
+ 2..]));
315 Cur
::new_at(&s
[..], start
)
318 // This is meant to be a translation of the following regex:
323 // (?: (?P<parameter> \d+) \$ )?
324 // (?P<flags> [-+ 0\#']* )
325 // (?P<width> \d+ | \* (?: (?P<widtha> \d+) \$ )? )?
326 // (?: \. (?P<precision> \d+ | \* (?: (?P<precisiona> \d+) \$ )? ) )?
329 // hh | h | ll | l | L | z | j | t
332 // | I32 | I64 | I | q
337 // Used to establish the full span at the end.
339 // The current position within the string.
340 let mut at
= at
.at_next_cp()?
;
341 // `c` is the next codepoint, `next` is a cursor after it.
342 let (mut c
, mut next
) = at
.next_cp()?
;
344 // Update `at`, `c`, and `next`, exiting if we're out of input.
345 macro_rules
! move_to
{
348 let (c_
, next_
) = at
.next_cp()?
;
354 // Constructs a result when parsing fails.
356 // Note: `move` used to capture copies of the cursors as they are *now*.
357 let fallback
= move || {
359 Substitution
::Format(Format
{
360 span
: start
.slice_between(next
).unwrap(),
366 type_
: at
.slice_between(next
).unwrap(),
367 position
: InnerSpan
::new(start
.at
, next
.at
),
373 // Next parsing state.
374 let mut state
= Start
;
376 // Sadly, Rust isn't *quite* smart enough to know these *must* be initialised by the end.
377 let mut parameter
: Option
<u16> = None
;
378 let mut flags
: &str = "";
379 let mut width
: Option
<Num
> = None
;
380 let mut precision
: Option
<Num
> = None
;
381 let mut length
: Option
<&str> = None
;
382 let mut type_
: &str = "";
385 if let Start
= state
{
388 let end
= at_next_cp_while(next
, is_digit
);
389 match end
.next_cp() {
390 // Yes, this *is* the parameter.
391 Some(('$'
, end2
)) => {
393 parameter
= Some(at
.slice_between(end
).unwrap().parse().unwrap());
396 // Wait, no, actually, it's the width.
401 width
= Some(Num
::from_str(at
.slice_between(end
).unwrap(), None
));
404 // It's invalid, is what it is.
405 None
=> return fallback(),
416 if let Flags
= state
{
417 let end
= at_next_cp_while(at
, is_flag
);
419 flags
= at
.slice_between(end
).unwrap();
423 if let Width
= state
{
430 let end
= at_next_cp_while(next
, is_digit
);
432 width
= Some(Num
::from_str(at
.slice_between(end
).unwrap(), None
));
443 if let WidthArg
= state
{
444 let end
= at_next_cp_while(at
, is_digit
);
445 match end
.next_cp() {
446 Some(('$'
, end2
)) => {
448 width
= Some(Num
::from_str("", Some(at
.slice_between(end
).unwrap())));
453 width
= Some(Num
::Next
);
459 if let Prec
= state
{
473 if let PrecInner
= state
{
476 let end
= at_next_cp_while(next
, is_digit
);
477 match end
.next_cp() {
478 Some(('$'
, end2
)) => {
480 precision
= Some(Num
::from_str("*", next
.slice_between(end
)));
485 precision
= Some(Num
::Next
);
491 let end
= at_next_cp_while(next
, is_digit
);
493 precision
= Some(Num
::from_str(at
.slice_between(end
).unwrap(), None
));
496 _
=> return fallback(),
500 if let Length
= state
{
501 let c1_next1
= next
.next_cp();
502 match (c
, c1_next1
) {
503 ('h'
, Some(('h'
, next1
))) | ('l'
, Some(('l'
, next1
))) => {
505 length
= Some(at
.slice_between(next1
).unwrap());
509 ('h'
| 'l'
| 'L'
| 'z'
| 'j'
| 't'
| 'q'
, _
) => {
511 length
= Some(at
.slice_between(next
).unwrap());
518 .and_then(|end
| end
.at_next_cp())
519 .map(|end
| (next
.slice_between(end
).unwrap(), end
));
520 let end
= match end
{
521 Some(("32" | "64", end
)) => end
,
525 length
= Some(at
.slice_between(end
).unwrap());
537 if let Type
= state
{
539 type_
= at
.slice_between(next
).unwrap();
541 // Don't use `move_to!` here, as we *can* be at the end of the input.
549 let position
= InnerSpan
::new(start
.at
, end
.at
);
552 span
: start
.slice_between(end
).unwrap(),
561 Some((Substitution
::Format(f
), end
.slice_after()))
564 fn at_next_cp_while
<F
>(mut cur
: Cur
<'_
>, mut pred
: F
) -> Cur
<'_
>
566 F
: FnMut(char) -> bool
,
569 match cur
.next_cp() {
582 fn is_digit(c
: char) -> bool
{
589 fn is_flag(c
: char) -> bool
{
591 '
0'
| '
-'
| '
+'
| ' '
| '
#' | '\'' => true,
601 use super::strcursor
::StrCursor
as Cur
;
602 use rustc_span
::InnerSpan
;
604 #[derive(Clone, PartialEq, Debug)]
605 pub enum Substitution
<'a
> {
606 Ordinal(u8, (usize, usize)),
607 Name(&'a
str, (usize, usize)),
608 Escape((usize, usize)),
611 impl Substitution
<'_
> {
612 pub fn as_str(&self) -> String
{
614 Substitution
::Ordinal(n
, _
) => format
!("${}", n
),
615 Substitution
::Name(n
, _
) => format
!("${}", n
),
616 Substitution
::Escape(_
) => "$$".into(),
620 pub fn position(&self) -> Option
<InnerSpan
> {
622 Substitution
::Ordinal(_
, pos
)
623 | Substitution
::Name(_
, pos
)
624 | Substitution
::Escape(pos
) => Some(InnerSpan
::new(pos
.0, pos
.1)),
628 pub fn set_position(&mut self, start
: usize, end
: usize) {
630 Substitution
::Ordinal(_
, ref mut pos
)
631 | Substitution
::Name(_
, ref mut pos
)
632 | Substitution
::Escape(ref mut pos
) => *pos
= (start
, end
),
636 pub fn translate(&self) -> Option
<String
> {
638 Substitution
::Ordinal(n
, _
) => Some(format
!("{{{}}}", n
)),
639 Substitution
::Name(n
, _
) => Some(format
!("{{{}}}", n
)),
640 Substitution
::Escape(_
) => None
,
645 /// Returns an iterator over all substitutions in a given string.
646 pub fn iter_subs(s
: &str, start_pos
: usize) -> Substitutions
<'_
> {
647 Substitutions { s, pos: start_pos }
650 /// Iterator over substitutions in a string.
651 pub struct Substitutions
<'a
> {
656 impl<'a
> Iterator
for Substitutions
<'a
> {
657 type Item
= Substitution
<'a
>;
658 fn next(&mut self) -> Option
<Self::Item
> {
659 match parse_next_substitution(self.s
) {
660 Some((mut sub
, tail
)) => {
662 if let Some(InnerSpan { start, end }
) = sub
.position() {
663 sub
.set_position(start
+ self.pos
, end
+ self.pos
);
672 fn size_hint(&self) -> (usize, Option
<usize>) {
673 (0, Some(self.s
.len()))
677 /// Parse the next substitution from the input string.
678 pub fn parse_next_substitution(s
: &str) -> Option
<(Substitution
<'_
>, &str)> {
680 let start
= s
.find('$'
)?
;
681 match s
[start
+ 1..].chars().next()?
{
682 '$'
=> return Some((Substitution
::Escape((start
, start
+ 2)), &s
[start
+ 2..])),
684 let n
= (c
as u8) - b'
0'
;
685 return Some((Substitution
::Ordinal(n
, (start
, start
+ 2)), &s
[start
+ 2..]));
687 _
=> { /* fall-through */ }
690 Cur
::new_at(&s
[..], start
)
693 let at
= at
.at_next_cp()?
;
694 let (c
, inner
) = at
.next_cp()?
;
696 if !is_ident_head(c
) {
699 let end
= at_next_cp_while(inner
, is_ident_tail
);
700 let slice
= at
.slice_between(end
).unwrap();
701 let start
= at
.at
- 1;
702 let end_pos
= at
.at
+ slice
.len();
703 Some((Substitution
::Name(slice
, (start
, end_pos
)), end
.slice_after()))
707 fn at_next_cp_while
<F
>(mut cur
: Cur
<'_
>, mut pred
: F
) -> Cur
<'_
>
709 F
: FnMut(char) -> bool
,
712 match cur
.next_cp() {
725 fn is_ident_head(c
: char) -> bool
{
727 'a'
..='z'
| 'A'
..='Z'
| '_'
=> true,
732 fn is_ident_tail(c
: char) -> bool
{
735 c
=> is_ident_head(c
),
744 pub struct StrCursor
<'a
> {
749 impl<'a
> StrCursor
<'a
> {
750 pub fn new_at(s
: &'a
str, at
: usize) -> StrCursor
<'a
> {
754 pub fn at_next_cp(mut self) -> Option
<StrCursor
<'a
>> {
755 match self.try_seek_right_cp() {
761 pub fn next_cp(mut self) -> Option
<(char, StrCursor
<'a
>)> {
762 let cp
= self.cp_after()?
;
763 self.seek_right(cp
.len_utf8());
767 fn slice_before(&self) -> &'a
str {
771 pub fn slice_after(&self) -> &'a
str {
775 pub fn slice_between(&self, until
: StrCursor
<'a
>) -> Option
<&'a
str> {
776 if !str_eq_literal(self.s
, until
.s
) {
779 use std
::cmp
::{max, min}
;
780 let beg
= min(self.at
, until
.at
);
781 let end
= max(self.at
, until
.at
);
782 Some(&self.s
[beg
..end
])
786 fn cp_after(&self) -> Option
<char> {
787 self.slice_after().chars().next()
790 fn try_seek_right_cp(&mut self) -> bool
{
791 match self.slice_after().chars().next() {
793 self.at
+= c
.len_utf8();
800 fn seek_right(&mut self, bytes
: usize) {
805 impl Copy
for StrCursor
<'_
> {}
807 impl<'a
> Clone
for StrCursor
<'a
> {
808 fn clone(&self) -> StrCursor
<'a
> {
813 impl std
::fmt
::Debug
for StrCursor
<'_
> {
814 fn fmt(&self, fmt
: &mut std
::fmt
::Formatter
<'_
>) -> std
::fmt
::Result
{
815 write
!(fmt
, "StrCursor({:?} | {:?})", self.slice_before(), self.slice_after())
819 fn str_eq_literal(a
: &str, b
: &str) -> bool
{
820 a
.as_bytes().as_ptr() == b
.as_bytes().as_ptr() && a
.len() == b
.len()