use proc_macro2::TokenStream;
use quote::quote;
use syn::File;
use crate::error::{Error, Result};
pub(crate) fn doc_tokens(documentation: Option<&str>) -> TokenStream {
let Some(doc) = documentation else {
return quote! {};
};
let doc = doc.trim();
if doc.is_empty() {
return quote! {};
}
let attrs: Vec<TokenStream> = doc
.lines()
.map(|line| {
let line = line.trim();
if line.is_empty() {
quote! { #[doc = ""] }
} else {
let spaced = format!(" {}", line);
quote! { #[doc = #spaced] }
}
})
.collect();
quote! { #(#attrs)* }
}
pub(crate) fn format_tokens(tokens: TokenStream) -> Result<String> {
let tokens_string = tokens.to_string();
let syntax_tree = syn::parse2::<File>(tokens).map_err(|source| Error::GeneratedParse {
tokens: tokens_string,
source,
})?;
Ok(prettyplease::unparse(&syntax_tree))
}
pub(crate) fn format_tokens_static(tokens: TokenStream) -> String {
format_tokens(tokens).expect("internal template tokens must always parse")
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn format_tokens_pretty_prints_valid_input() {
let out = format_tokens(quote! { pub fn answer() -> i32 { 42 } }).expect("valid tokens");
assert!(out.contains("pub fn answer"), "output: {out}");
}
#[test]
fn format_tokens_hard_fails_on_unparsable_tokens() {
let err = format_tokens(quote! { struct }).expect_err("expected a parse error");
match err {
Error::GeneratedParse { tokens, .. } => {
assert!(
tokens.contains("struct"),
"error should carry the offending token string: {tokens}"
);
}
other => panic!("expected GeneratedParse, got {other:?}"),
}
}
}