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 | |