termo_gen/
lib.rs

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
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111


///! # termo_gen
///! code generator for termo terminal ui library
use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, DeriveInput};



///# termo::display
/// display trait for termo makes a custom display for the struct
/// with tree like structure and colors to make it easier to read
/// example:
/// ```
/// #[termo::display]
/// pub struct DedicatedCar {
///   pub date: DateTime<Utc>,
///   pub team: String,
///   pub passengers: String,
///   pub time: DateTime<Utc>,
///   pub route: String,
///   pub car_type: String,
///   pub comments: String,
///   pub daily_rate: f64,
///   pub additional: f64,
///   pub total_rate: f64,
///   pub booking_ref: Option<String>,
/// }
/// ```
/// will output something like this when printed
/// ```
/// ╭─ DedicatedCar ─────────
/// | date: 1970-01-01T00:00:00Z
/// | team: ""
/// | passengers: ""
/// | time: 1970-01-01T00:00:00Z
/// | route: ""
/// | car_type: ""
/// | comments: ""
/// | daily_rate: 0.0
/// | additional: 0.0
/// | total_rate: 0.0
/// | booking_ref: None
/// ╰────────────────────────
/// 

#[proc_macro_attribute]
pub fn display(_attr: TokenStream, item: TokenStream) -> TokenStream {
    let ast = parse_macro_input!(item as DeriveInput);
    let name = &ast.ident;
    let fields_names = match ast.data {
        syn::Data::Struct(ref data_struct) => match data_struct.fields {
            syn::Fields::Named(ref fields) => fields
                .named
                .iter()
                .map(|f| f.ident.as_ref().unwrap())
                .collect::<Vec<_>>(),
            _ => panic!("Only named fields are supported"),
        },
        _ => panic!("Only structs are supported"),
    };


    //generate original struct definition but add derive(Debug)
    let mut struct_def = quote! {
        #ast
    };


    //generate impl Debug for struct
    let mut dbg_impl = quote! {
        impl std::fmt::Debug for #name {
            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
                let mut s = String::new();
                s.push_str(&format!("{}╭─{} {}{}{} ─────────\n", termo::GRAY, termo::RESET, termo::MAGENTA, stringify!(#name), termo::GRAY));
                #(s.push_str(&format!("| {}{}{}{}: {}{:?}{}\n", termo::RESET, termo::YELLOW, stringify!(#fields_names), termo::RESET, termo::WHITE, self.#fields_names, termo::GRAY));)*
                s.push_str(format!("╰────────────────────────{}\n", termo::RESET).as_str());
                write!(f, "{}", s)
            }
        }
    };

    
    

    let output = quote! {
        #struct_def
        #dbg_impl
    };

    output.into()
}



// ╭─ {header} ──────────── __  
// |       ____ ___  ____  / /_____ 
// |      / __ `__ \/ __ \/ __/ __ \
// |     / / / / / / /_/ / /_/ /_/ /
// |    /_/ /_/ /_/\____/\__/\____/ 
// | 
// ╰─╮─[white] {subtitle} [/]─[yellow] {tail} ─"#;