Skip to content

Commit 68645d3

Browse files
committed
Improve type_name stringification
1 parent 9bb0861 commit 68645d3

File tree

1 file changed

+70
-2
lines changed

1 file changed

+70
-2
lines changed

partialdebug-derive/src/lib.rs

Lines changed: 70 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,8 +73,7 @@ pub fn derive_placeholder(input: TokenStream) -> TokenStream {
7373

7474
let as_debug_all_fields = fields.iter().map(|field| {
7575
let name = &field.ident;
76-
let mut type_name = field.ty.to_token_stream().to_string();
77-
type_name.retain(|c| !c.is_whitespace()); // remove whitespace
76+
let type_name = get_type_name(&field.ty);
7877

7978
// type name or given placeholder string
8079
let placeholder_string = placeholder.as_ref().unwrap_or(&type_name);
@@ -136,3 +135,72 @@ fn get_placeholder(input: &ItemStruct) -> Result<Option<String>> {
136135
})
137136
.transpose()
138137
}
138+
139+
/// returns the type as a string with unnecessary whitespace removed
140+
fn get_type_name(ty: &Type) -> String {
141+
let mut type_name = String::new();
142+
let chars: Vec<char> = ty.to_token_stream().to_string().trim().chars().collect();
143+
144+
for (i, char) in chars.iter().enumerate() {
145+
if char.is_whitespace() {
146+
// remove whitespace surrounding punctuation
147+
// exceptions are:
148+
// - whitespace surrounding `->`
149+
// - whitespace following `,` or `;`
150+
let (before, after) = (chars[i - 1], chars[i + 1]); // always valid because string was trimmed before
151+
let before_wide = chars.get(i.saturating_sub(2)..i);
152+
let after_wide = chars.get(i + 1..=i + 2);
153+
154+
if (before.is_ascii_punctuation() || after.is_ascii_punctuation())
155+
&& !matches!(before, ';' | ',')
156+
&& !matches!(before_wide, Some(['-', '>']))
157+
&& !matches!(after_wide, Some(['-', '>']))
158+
{
159+
continue;
160+
}
161+
}
162+
163+
type_name.push(*char);
164+
}
165+
166+
type_name
167+
}
168+
169+
#[cfg(test)]
170+
mod tests {
171+
use super::*;
172+
173+
fn test_type_name_formatting(type_str: &str) {
174+
let ty: Type = parse_str(type_str).unwrap();
175+
assert_eq!(get_type_name(&ty), type_str)
176+
}
177+
178+
#[test]
179+
fn test_no_spaces() {
180+
test_type_name_formatting("u8");
181+
test_type_name_formatting("Option<u8>");
182+
test_type_name_formatting("[u8]");
183+
test_type_name_formatting("()");
184+
test_type_name_formatting("std::fmt::Formatter<'_>");
185+
}
186+
#[test]
187+
fn test_array() {
188+
test_type_name_formatting("[u8; 4]");
189+
}
190+
#[test]
191+
fn test_lifetime() {
192+
test_type_name_formatting("&'a u8");
193+
}
194+
#[test]
195+
fn test_function() {
196+
test_type_name_formatting("fn(u8) -> u8");
197+
}
198+
#[test]
199+
fn test_trait_object() {
200+
test_type_name_formatting("Box<dyn Send>");
201+
}
202+
#[test]
203+
fn test_tuple() {
204+
test_type_name_formatting("(Option<u8>, u8)");
205+
}
206+
}

0 commit comments

Comments
 (0)