1use serde_json::*;
2use std::*;
3use std::fs::File;
4use std::io::prelude::*;
5use std::path::Path;
6use std::collections::BTreeMap;
7
8pub fn vars<T>(args : &T)
9where
10 T: ?Sized + serde::ser::Serialize,
11{
12 if let Ok(inspect_vars_path) = env::var("INSPECT_VARS") {
13 if let Ok(json) = to_string(&args) {
14 let vars_path = inspect_vars_path.replace("\\","/");
15 if vars_path.contains("/") {
16 let dir = Path::new(&vars_path).parent().unwrap().to_str().unwrap();
17 let _ = fs::create_dir_all(dir);
18 }
19
20 let _ = match File::create(&vars_path) {
21 Ok(mut file) => file.write_all(json.as_bytes()),
22 Err(err) => Err(err),
23 };
24 }
25 }
26}
27
28pub fn dump<T>(args : &T) -> Option<String>
29where
30 T: ?Sized + serde::ser::Serialize,
31{
32 match to_string_pretty(&args) {
33 Ok(json) => {
34 Some(json.replace("\"",""))
35 },
36 _ => None
37 }
38}
39
40pub fn print_dump<T>(args : &T)
41where
42 T: ?Sized + serde::ser::Serialize,
43{
44 match dump(args) {
45 Some(s) => println!("{}", s),
46 _ => (),
47 }
48}
49
50pub fn to_list_map<T: Sized>(rows : &Vec<T>) -> Vec<Map<String,Value>>
51where
52 T: ?Sized + serde::ser::Serialize,
53{
54 let mut to: Vec<Map<String,Value>> = Vec::new();
55 for row in rows {
56 let map = from_str(&to_string(row).unwrap());
57 to.push(map.unwrap());
58 }
59 return to;
60}
61
62pub fn all_keys(rows : &Vec<Map<String,Value>>) -> Vec<String>
63{
64 let mut to: Vec<String> = Vec::new();
65 for row in rows {
66 for key in row.keys() {
67 if !to.contains(key) {
68 to.push(key.to_string());
69 }
70 }
71 }
72 return to;
73}
74
75pub fn dump_table<T: Sized>(rows : &Vec<T>) -> Option<String>
76where
77 T: ?Sized + serde::ser::Serialize,
78{
79 return dump_table_columns(rows, Vec::new());
80}
81
82pub fn dump_table_columns<T: Sized>(rows: &Vec<T>, columns: Vec<&str>) -> Option<String>
83where
84 T: ?Sized + serde::ser::Serialize,
85{
86 if rows.is_empty() {
87 return None;
88 }
89 let map_rows = to_list_map(rows);
90 let keys = if columns.is_empty() {
91 all_keys(&map_rows)
92 } else {
93 columns.iter().map(|x| x.to_string()).collect()
94 };
95 let mut col_sizes: BTreeMap<String,usize> = BTreeMap::new();
96
97 for k in &keys {
98 let mut max = k.len();
99 for row in &map_rows {
100 if let Some(col) = &row.get(k) {
101 max = cmp::max(format!("{}", col).len(), max);
102 }
103 }
104 col_sizes.insert(k.to_string(), max);
105 }
106
107 let col_sizes_length = col_sizes.len();
108 let row_width: usize = col_sizes.values().sum::<usize>()
109 + (col_sizes_length * 2)
110 + (col_sizes_length + 1);
111
112 let dashes = "-".repeat(row_width - 2);
113 let mut sb: Vec<String> = Vec::new();
114 sb.push(format!("+{}+", dashes));
115 let mut head = "|".to_string();
116 for k in &keys {
117 head = format!("{}{}|", &head, &align_center(k, col_sizes[k], " "));
118 }
119 sb.push(head.to_string());
120 sb.push(format!("|{}|", dashes));
121
122 for row in &map_rows {
123 let mut to = "|".to_string();
124 for k in &keys {
125 to = format!("{}{}!", &to, &align_auto(&row[k], col_sizes[k], " "));
126 }
127 sb.push(to);
128 }
129 sb.push(format!("+{}+", dashes));
130
131 return Some(sb.join("\n"));
132}
133
134pub fn print_dump_table<T: Sized>(rows : &Vec<T>)
135where
136 T: ?Sized + serde::ser::Serialize,
137{
138 match dump_table(rows) {
139 Some(s) => println!("{}", s),
140 _ => (),
141 }
142}
143
144pub fn print_dump_table_columns<T: Sized>(rows : &Vec<T>, columns: Vec<&str>)
145where
146 T: ?Sized + serde::ser::Serialize,
147{
148 match dump_table_columns(rows, columns) {
149 Some(s) => println!("{}", s),
150 _ => (),
151 }
152}
153
154pub fn align_left(str: &str, len: usize, pad: &str) -> String {
155 if len > 0 {
156 let a_len = len + 1 - str.len();
157 if a_len > 0 {
158 return format!("{}{}{}", pad, str, pad.repeat(a_len));
159 }
160 }
161 return "".into();
162}
163
164pub fn align_center(str: &str, len: usize, pad: &str) -> String {
165 if len > 0 {
166 let n_len = str.len();
167 let half = (len as f64 / 2.0 - n_len as f64 / 2.0).floor() as usize;
168 let odds = ((n_len % 2) as i64 - (len % 2) as i64).abs() as usize;
169 return format!("{}{}{}", pad.repeat(half + 1), str, pad.repeat(half + 1 + odds));
170 }
171 return "".into();
172}
173
174pub fn align_right(str: &str, len: usize, pad: &str) -> String {
175 if len > 0 {
176 let a_len = len + 1 - str.len();
177 if a_len > 0 {
178 return format!("{}{}{}", pad.repeat(a_len), str, pad);
179 }
180 }
181 return "".into();
182}
183
184pub fn align_auto(obj: &Value, len: usize, pad: &str) -> String {
185 let str = match obj {
186 Value::String(s) => format!("{}", s),
187 _ => format!("{}", obj),
188 };
189 if str.len() <= len {
190 return match obj {
191 Value::Number(_) => align_right(&str, len, &pad),
192 _ => align_left(&str, len, &pad),
193 }
194 }
195 return str;
196}
197
198
199#[cfg(test)]
200mod test {
201 use super::*;
202
203 #[test]
204 fn it_works() {
205 assert_eq!(2 + 2, 4);
206 }
207
208 fn args() -> Value {
209 return json!([
210 { "a": 1, "b":"foo" },
211 { "a": 2.1, "b":"barbar" },
212 { "a": 3.21, "b":"bazbazbaz" }
213 ]);
214 }
215
216 #[test]
217 fn inspect_vars() {
218 vars(&args());
219 }
220
221 #[test]
222 fn inspect_print_dump() {
223 print_dump(&args());
224 }
225
226 #[test]
227 fn inspect_print_dump_table() {
228 print_dump_table(&args().as_array().unwrap());
229 }
230
231 #[tokio::test]
232 async fn inspect_print_dump_table_json_api() {
233
234 let org_name = "rust-lang";
235
236 let res = reqwest::Client::new()
237 .get(&format!("https://api.github.com/orgs/{}/repos", org_name))
238 .header(reqwest::header::USER_AGENT, "gist.cafe")
239 .send()
240 .await.unwrap();
241
242 let json: Vec<Map<String,Value>> = res.json().await.unwrap();
243 let mut org_repos: Vec<Map<String,Value>> = Vec::new();
244 for x in json.iter() {
245 org_repos.push(json!({
246 "name": x["name"],
247 "description": x["description"],
248 "lang": x["language"],
249 "watchers": x["watchers"],
250 "forks": x["forks"],
251 }).as_object().unwrap().clone());
252 }
253 org_repos.sort_by(|a, b| b["watchers"].as_i64().cmp(&a["watchers"].as_i64()));
254
255 println!("Top 3 {} GitHub Repos:", org_name);
256 print_dump(&org_repos[1..=3]);
257
258 println!("\nTop 10 {} GitHub Repos:", org_name);
259 print_dump_table(&org_repos[1..=10].iter().map(|x| json!({
260 "name": x["name"],
261 "lang": x["lang"],
262 "watchers": x["watchers"],
263 "forks": x["forks"],
264 }).as_object().unwrap().clone()).collect());
265
266 println!("\nTop 10 {} GitHub Repos:", org_name);
267 print_dump_table_columns(&org_repos[1..=10].to_vec(),
268 vec!["name", "lang", "watchers", "forks"]);
269 }
270}