1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
//! This crate provides a series of traits, macros, functions, and utilities that make writing
//! and debugging proc macros easier

pub use proc_macro2::TokenStream as TokenStream2;
pub use proc_util_macros::*;
use quote::ToTokens;

/// Bring this trait into scope for a blanket `to_pretty()` implementation for all
/// `syn`-compatible types as well as `TokenStream2`.
///
/// Calling [`to_pretty`](`ToPretty::to_pretty`) on any such objects will result in a
/// pretty-formatted string representation of the (parsed) source code of that item.
pub trait ToPretty {
    /// Produces a pretty-formatted [`String`] (formatted by [`prettyplease`]) representation
    /// of the underlying token stream.
    ///
    /// Can be used on anything that implements [`ToTokens`] if [`ToPretty`] is brought into
    /// scope.
    ///
    /// ## Example
    ///
    /// Using `to_pretty` directly on a [`TokenStream2`]:
    /// ```
    /// use proc_utils::ToPretty;
    ///
    /// let struct_tokens = quote::quote!(struct MyStruct;);
    /// assert_eq!(struct_tokens.to_pretty(), "struct MyStruct;\n");
    /// ```
    ///
    /// Using `to_pretty` on a [`syn::ItemStruct`]:
    /// ```
    /// use proc_utils::ToPretty;
    ///
    /// let struct_tokens = quote::quote!(struct MyStruct { field1: usize, field2: u32, field3: bool });
    /// let item_struct = syn::parse2::<syn::ItemStruct>(struct_tokens).unwrap();
    /// assert_eq!(item_struct.to_pretty(),
    ///     "struct MyStruct {\n    \
    ///          field1: usize,\n    \
    ///          field2: u32,\n    \
    ///          field3: bool,\n\
    ///     }\n"
    /// );
    /// ```
    fn to_pretty(&self) -> String;
}

impl<T: ToTokens> ToPretty for T {
    fn to_pretty(&self) -> String {
        let tokens: TokenStream2 = self.to_token_stream();
        let file = syn::parse_file(&tokens.to_string()).unwrap();
        prettyplease::unparse(&file)
    }
}

/// Like [`ToPretty`] but prints to the console instead.
pub trait PrettyPrint {
    /// Like [`ToPretty::to_pretty`] but prints to the console instead.
    fn pretty_print(&self);
}

impl<T: ToTokens> PrettyPrint for T {
    fn pretty_print(&self) {
        let tokens: TokenStream2 = self.to_token_stream();
        let file = syn::parse_file(&tokens.to_string()).unwrap();
        println!("{}", prettyplease::unparse(&file));
    }
}

#[test]
fn test_pretty_print() {
    quote::quote!(
        type Blah = usize;
    )
    .pretty_print();
}