bonfida_autodoc/
utils.rs

1use std::fmt::Write;
2
3use proc_macro2::{TokenStream, TokenTree};
4use syn::{
5    punctuated::Punctuated, token::Comma, Attribute, Field, Fields, FieldsNamed, Item, ItemStruct,
6};
7
8pub type TableRow = Vec<String>;
9
10pub fn generate_table(columns: &[String], data: &[TableRow]) -> Vec<String> {
11    let mut lines = Vec::with_capacity(data.len() + 2);
12    let mut column_widths = Vec::with_capacity(columns.len());
13    let mut current_line = "".to_owned();
14    for (c_idx, c) in columns.iter().enumerate() {
15        let mut current_width = c.len();
16        for d in data {
17            current_width = std::cmp::max(current_width, d[c_idx].len());
18        }
19        column_widths.push(current_width);
20        current_line
21            .write_str(&format!(
22                "| {value:width$} ",
23                value = c,
24                width = current_width
25            ))
26            .unwrap();
27    }
28    current_line.write_str("|").unwrap();
29    let total_width = current_line.len();
30    lines.push(current_line);
31
32    lines.push(format!(
33        "| {val:-<width$} |",
34        val = "",
35        width = total_width - 4
36    ));
37    for t in data {
38        current_line = "".to_owned();
39        for (value, column_width) in t.iter().zip(column_widths.iter()) {
40            current_line
41                .write_str(&format!("| {:1$} ", value, column_width))
42                .unwrap();
43        }
44        current_line.write_str("|").unwrap();
45        lines.push(current_line)
46    }
47    lines
48}
49
50pub fn find_struct(ident_str: &str, file_ast: &syn::File) -> Item {
51    file_ast
52        .items
53        .iter()
54        .find(|a| {
55            if let Item::Struct(ItemStruct {
56                ident,
57                attrs: _,
58                vis: _,
59                struct_token: _,
60                generics: _,
61                fields: _,
62                semi_token: _,
63            }) = a
64            {
65                *ident == ident_str
66            } else {
67                false
68            }
69        })
70        .unwrap()
71        .clone()
72}
73
74pub fn get_struct_fields(s: Item) -> Punctuated<Field, Comma> {
75    if let Item::Struct(ItemStruct {
76        ident: _,
77        attrs: _,
78        vis: _,
79        struct_token: _,
80        generics: _,
81        fields:
82            Fields::Named(FieldsNamed {
83                named,
84                brace_token: _,
85            }),
86        semi_token: _,
87    }) = s
88    {
89        named
90    } else {
91        unreachable!()
92    }
93}
94
95pub fn get_constraints_and_doc(attrs: &[Attribute]) -> (bool, bool, Vec<String>) {
96    let mut writable = false;
97    let mut signer = false;
98    let mut doc = vec![];
99    for a in attrs {
100        if a.path.is_ident("cons") {
101            let t = if let TokenTree::Group(g) = a.tokens.clone().into_iter().next().unwrap() {
102                g.stream()
103            } else {
104                panic!()
105            };
106
107            for constraint in t.into_iter() {
108                match constraint {
109                    TokenTree::Ident(i) => {
110                        if &i.to_string() == "writable" {
111                            writable = true;
112                        }
113                        if &i.to_string() == "signer" {
114                            signer = true;
115                        }
116                    }
117                    TokenTree::Punct(p) if p.as_char() == ',' => {}
118                    _ => {}
119                }
120            }
121        } else if a.path.is_ident("doc") {
122            let _t = if let TokenTree::Literal(l) = a.tokens.clone().into_iter().nth(1).unwrap() {
123                l
124            } else {
125                panic!()
126            };
127            let parsed_l: syn::LitStr = syn::parse2(TokenStream::from(
128                a.tokens.clone().into_iter().nth(1).unwrap(),
129            ))
130            .unwrap();
131            doc.push(parsed_l.value().trim().to_owned());
132        }
133    }
134    (writable, signer, doc)
135}
136
137pub fn strip_docs(attrs: &[Attribute]) -> Vec<Attribute> {
138    let mut attributes = Vec::with_capacity(attrs.len());
139    for a in attrs {
140        if !a.path.is_ident("doc") {
141            attributes.push(a.clone());
142        }
143    }
144    attributes
145}
146
147pub fn boolean_to_emoji(b: bool) -> char {
148    match b {
149        true => '✅',
150        false => '❌',
151    }
152}