use quote::ToTokens;
pub(super) fn render_type(ty: &syn::Type) -> String {
let mut tokens = proc_macro2::TokenStream::new();
ty.to_tokens(&mut tokens);
normalise_rendering(&tokens.to_string())
}
fn normalise_rendering(s: &str) -> String {
let mut out = String::with_capacity(s.len());
let mut chars = s.chars().peekable();
while let Some(c) = chars.next() {
dispatch_char(c, &mut chars, &mut out);
}
out
}
fn dispatch_char(c: char, chars: &mut std::iter::Peekable<std::str::Chars<'_>>, out: &mut String) {
match c {
' ' => emit_space(chars, out),
'<' | '>' => emit_angle(c, chars, out),
':' => emit_colon(chars, out),
other => out.push(other),
}
}
fn emit_space(chars: &mut std::iter::Peekable<std::str::Chars<'_>>, out: &mut String) {
let keep = match chars.peek() {
Some('<' | '>' | ',') => false,
Some(':') => !next_two_are_colons(chars),
_ => true,
};
if keep {
out.push(' ');
}
}
fn emit_angle(c: char, chars: &mut std::iter::Peekable<std::str::Chars<'_>>, out: &mut String) {
out.push(c);
eat_spaces(chars);
}
fn emit_colon(chars: &mut std::iter::Peekable<std::str::Chars<'_>>, out: &mut String) {
if chars.peek() == Some(&':') {
chars.next();
out.push_str("::");
eat_spaces(chars);
} else {
out.push(':');
}
}
fn next_two_are_colons(chars: &std::iter::Peekable<std::str::Chars<'_>>) -> bool {
let mut la = chars.clone();
la.next() == Some(':') && la.next() == Some(':')
}
fn eat_spaces(chars: &mut std::iter::Peekable<std::str::Chars<'_>>) {
while chars.peek() == Some(&' ') {
chars.next();
}
}
pub(super) fn render_type_param_bound(bound: &syn::TypeParamBound) -> String {
let mut tokens = proc_macro2::TokenStream::new();
bound.to_tokens(&mut tokens);
tokens.to_string()
}
pub(super) fn receiver_kind(sig: &syn::Signature) -> Option<&'static str> {
let recv = sig.inputs.iter().find_map(|arg| match arg {
syn::FnArg::Receiver(r) => Some(r),
_ => None,
})?;
if recv.reference.is_some() {
if recv.mutability.is_some() {
Some("mut_ref")
} else {
Some("shared_ref")
}
} else {
Some("owned")
}
}