1 | use crate::attr::Display;
|
2 | use proc_macro2::TokenStream;
|
3 | use quote::quote_spanned;
|
4 | use syn::{Ident, LitStr};
|
5 |
|
6 | macro_rules! peek_next {
|
7 | ($read:ident) => {
|
8 | match $read.chars().next() {
|
9 | Some(next) => next,
|
10 | None => return,
|
11 | }
|
12 | };
|
13 | }
|
14 |
|
15 | impl Display {
|
16 | // Transform `"error {var}"` to `"error {}", var`.
|
17 | pub(crate) fn expand_shorthand(&mut self) {
|
18 | let span = self.fmt.span();
|
19 | let fmt = self.fmt.value();
|
20 | let mut read = fmt.as_str();
|
21 | let mut out = String::new();
|
22 | let mut args = TokenStream::new();
|
23 |
|
24 | while let Some(brace) = read.find('{' ) {
|
25 | out += &read[..=brace];
|
26 | read = &read[brace + 1..];
|
27 |
|
28 | // skip cases where we find a {{
|
29 | if read.starts_with('{' ) {
|
30 | out.push('{' );
|
31 | read = &read[1..];
|
32 | continue;
|
33 | }
|
34 |
|
35 | let next = peek_next!(read);
|
36 |
|
37 | let var = match next {
|
38 | '0' ..='9' => take_int(&mut read),
|
39 | 'a' ..='z' | 'A' ..='Z' | '_' => take_ident(&mut read),
|
40 | _ => return,
|
41 | };
|
42 |
|
43 | let ident = Ident::new(&var, span);
|
44 |
|
45 | let next = peek_next!(read);
|
46 |
|
47 | let arg = if cfg!(feature = "std" ) && next == '}' {
|
48 | quote_spanned!(span=> , #ident.__displaydoc_display())
|
49 | } else {
|
50 | quote_spanned!(span=> , #ident)
|
51 | };
|
52 |
|
53 | args.extend(arg);
|
54 | }
|
55 |
|
56 | out += read;
|
57 | self.fmt = LitStr::new(&out, self.fmt.span());
|
58 | self.args = args;
|
59 | }
|
60 | }
|
61 |
|
62 | fn take_int(read: &mut &str) -> String {
|
63 | let mut int: String = String::new();
|
64 | int.push(ch:'_' );
|
65 | for (i: usize, ch: char) in read.char_indices() {
|
66 | match ch {
|
67 | '0' ..='9' => int.push(ch),
|
68 | _ => {
|
69 | *read = &read[i..];
|
70 | break;
|
71 | }
|
72 | }
|
73 | }
|
74 | int
|
75 | }
|
76 |
|
77 | fn take_ident(read: &mut &str) -> String {
|
78 | let mut ident: String = String::new();
|
79 | for (i: usize, ch: char) in read.char_indices() {
|
80 | match ch {
|
81 | 'a' ..='z' | 'A' ..='Z' | '0' ..='9' | '_' => ident.push(ch),
|
82 | _ => {
|
83 | *read = &read[i..];
|
84 | break;
|
85 | }
|
86 | }
|
87 | }
|
88 | ident
|
89 | }
|
90 |
|
91 | #[cfg (test)]
|
92 | mod tests {
|
93 | use super::*;
|
94 | use pretty_assertions::assert_eq;
|
95 | use proc_macro2::Span;
|
96 |
|
97 | fn assert(input: &str, fmt: &str, args: &str) {
|
98 | let mut display = Display {
|
99 | fmt: LitStr::new(input, Span::call_site()),
|
100 | args: TokenStream::new(),
|
101 | };
|
102 | display.expand_shorthand();
|
103 | assert_eq!(fmt, display.fmt.value());
|
104 | assert_eq!(args, display.args.to_string());
|
105 | }
|
106 |
|
107 | #[test ]
|
108 | fn test_expand() {
|
109 | assert("fn main() {{ }}" , "fn main() {{ }}" , "" );
|
110 | }
|
111 |
|
112 | #[test ]
|
113 | #[cfg_attr (not(feature = "std" ), ignore)]
|
114 | fn test_std_expand() {
|
115 | assert(
|
116 | "{v} {v:?} {0} {0:?}" ,
|
117 | "{} {:?} {} {:?}" ,
|
118 | ", v . __displaydoc_display () , v , _0 . __displaydoc_display () , _0" ,
|
119 | );
|
120 | assert("error {var}" , "error {}" , ", var . __displaydoc_display ()" );
|
121 |
|
122 | assert(
|
123 | "error {var1}" ,
|
124 | "error {}" ,
|
125 | ", var1 . __displaydoc_display ()" ,
|
126 | );
|
127 |
|
128 | assert(
|
129 | "error {var1var}" ,
|
130 | "error {}" ,
|
131 | ", var1var . __displaydoc_display ()" ,
|
132 | );
|
133 |
|
134 | assert(
|
135 | "The path {0}" ,
|
136 | "The path {}" ,
|
137 | ", _0 . __displaydoc_display ()" ,
|
138 | );
|
139 | assert("The path {0:?}" , "The path {:?}" , ", _0" );
|
140 | }
|
141 |
|
142 | #[test ]
|
143 | #[cfg_attr (feature = "std" , ignore)]
|
144 | fn test_nostd_expand() {
|
145 | assert(
|
146 | "{v} {v:?} {0} {0:?}" ,
|
147 | "{} {:?} {} {:?}" ,
|
148 | ", v , v , _0 , _0" ,
|
149 | );
|
150 | assert("error {var}" , "error {}" , ", var" );
|
151 |
|
152 | assert("The path {0}" , "The path {}" , ", _0" );
|
153 | assert("The path {0:?}" , "The path {:?}" , ", _0" );
|
154 |
|
155 | assert("error {var1}" , "error {}" , ", var1" );
|
156 |
|
157 | assert("error {var1var}" , "error {}" , ", var1var" );
|
158 | }
|
159 | }
|
160 | |