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}