forc_wallet/
format.rs

1use std::{cmp::max, collections::HashMap, fmt::Display};
2
3use anyhow::Result;
4
5#[derive(PartialEq, Eq)]
6enum Value {
7    Seperator,
8    NewLine,
9    Entry(String, String),
10}
11
12/// Simple helper to print key-value entries where the keys are all alligned.
13///
14/// Here is an example of how it looks:
15///
16/// --------------------------------------------------------------------------
17/// account 0: 0x0008faCa0e0280192dbA692156280C5410043f63a48d49C6a1d901de8A29a4aa
18/// Asset ID : 0000000000000000000000000000000000000000000000000000000000000000
19/// Amount   : 499999800
20///
21/// Asset ID : 0000000000000000000000000000000000000000000000000000000000000001
22/// Amount   : 359989610
23/// --------------------------------------------------------------------------
24/// account 1: 0x002D7487aeFb0238D2e12c63eaB99B545876e2F2E5cdA90aDFD2dC40BD6B49ff
25/// Asset ID : 0000000000000000000000000000000000000000000000000000000000000000
26/// Amount   : 268983615
27#[derive(Default)]
28pub struct List(Vec<Value>);
29
30impl List {
31    pub fn add(&mut self, title: impl ToString, value: impl ToString) {
32        self.0
33            .push(Value::Entry(title.to_string(), value.to_string()));
34    }
35
36    pub fn add_newline(&mut self) {
37        self.0.push(Value::NewLine);
38    }
39
40    pub fn add_seperator(&mut self) {
41        if self.0.last() == Some(&Value::Seperator) {
42            return;
43        }
44        self.0.push(Value::Seperator);
45    }
46
47    pub fn longest_title(&self) -> usize {
48        self.0
49            .iter()
50            .map(|value| match value {
51                Value::Seperator => 0,
52                Value::NewLine => 0,
53                Value::Entry(title, _) => title.len(),
54            })
55            .max()
56            .unwrap_or(0)
57    }
58}
59
60impl Display for List {
61    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
62        let longest_key = self.longest_title();
63        let entries = self
64            .0
65            .iter()
66            .map(|entry| match entry {
67                Value::Seperator => None,
68                Value::NewLine => Some("".to_owned()),
69                Value::Entry(title, value) => {
70                    let padding = " ".repeat(longest_key - title.len());
71                    Some(format!("{}{}: {}", title, padding, value))
72                }
73            })
74            .collect::<Vec<_>>();
75
76        let longest_entry = entries
77            .iter()
78            .map(|entry| entry.as_ref().map(|s| s.len()).unwrap_or(0))
79            .max()
80            .unwrap_or(0);
81
82        let seperator = "-".repeat(longest_entry);
83
84        let formatted = entries
85            .into_iter()
86            .map(|entry| entry.map(|s| s.to_string()).unwrap_or(seperator.clone()))
87            .collect::<Vec<_>>()
88            .join("\n");
89
90        write!(f, "{formatted}")
91    }
92}
93
94#[derive(Default)]
95pub struct Table {
96    headers: Vec<String>,
97    rows: Vec<Vec<String>>,
98}
99
100impl Table {
101    pub fn add_header(&mut self, header: impl ToString) {
102        self.headers.push(header.to_string());
103    }
104
105    pub fn add_row(&mut self, row: Vec<impl ToString>) -> Result<()> {
106        if self.headers.len() != row.len() {
107            anyhow::bail!("Row length does not match header length");
108        }
109        self.rows
110            .push(row.into_iter().map(|x| x.to_string()).collect());
111        Ok(())
112    }
113}
114
115impl Display for Table {
116    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
117        let mut longest_columns = self
118            .headers
119            .iter()
120            .enumerate()
121            .map(|(column_id, x)| (column_id, x.len()))
122            .collect::<HashMap<_, _>>();
123
124        for row in self.rows.iter() {
125            for (column_id, value) in row.iter().enumerate() {
126                longest_columns
127                    .entry(column_id)
128                    .and_modify(|x| *x = max(*x, value.len()));
129            }
130        }
131        let separator = self
132            .headers
133            .iter()
134            .enumerate()
135            .map(|(column_id, _)| "-".repeat(longest_columns[&column_id]))
136            .collect::<Vec<_>>()
137            .join("-|-");
138
139        let mut table = vec![
140            self.headers
141                .iter()
142                .enumerate()
143                .map(|(column_id, header)| {
144                    let padding = " ".repeat(longest_columns[&column_id] - header.len());
145                    format!("{}{}", header, padding)
146                })
147                .collect::<Vec<_>>()
148                .join(" | "),
149            separator.clone(),
150        ];
151
152        for row in &self.rows {
153            table.push(
154                row.iter()
155                    .enumerate()
156                    .map(|(column_id, value)| {
157                        let padding = " ".repeat(longest_columns[&column_id] - value.len());
158                        format!("{}{}", value, padding)
159                    })
160                    .collect::<Vec<_>>()
161                    .join(" | "),
162            );
163            table.push(separator.clone());
164        }
165
166        let formatted = table.join("\n");
167
168        write!(f, "{formatted}")
169    }
170}