1use std::fmt::Display;
18use tabled::{
19 Table,
20 settings::{
21 Format, Modify, Panel, Remove,
22 object::{Rows, Segment},
23 style::Style,
24 },
25};
26
27pub use tabled::settings::Padding;
28
29#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
31#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
32pub enum TableStyle {
33 #[default]
35 Modern,
36 Borderless,
38 Markdown,
40 Sharp,
42 Ascii,
44 Psql,
46 Dots,
48}
49
50#[cfg(feature = "clap")]
51impl clap::ValueEnum for TableStyle {
52 fn value_variants<'a>() -> &'a [Self] {
53 &[
54 Self::Modern,
55 Self::Borderless,
56 Self::Markdown,
57 Self::Sharp,
58 Self::Ascii,
59 Self::Psql,
60 Self::Dots,
61 ]
62 }
63
64 fn to_possible_value(&self) -> Option<clap::builder::PossibleValue> {
65 Some(clap::builder::PossibleValue::new(match self {
66 Self::Modern => "modern",
67 Self::Borderless => "borderless",
68 Self::Markdown => "markdown",
69 Self::Sharp => "sharp",
70 Self::Ascii => "ascii",
71 Self::Psql => "psql",
72 Self::Dots => "dots",
73 }))
74 }
75}
76
77impl TableStyle {
78 pub fn apply(self, table: &mut Table) {
80 match self {
81 TableStyle::Modern => {
82 table.with(Style::rounded());
83 }
84 TableStyle::Borderless => {
85 table.with(Style::blank());
86 }
87 TableStyle::Markdown => {
88 table.with(Style::markdown());
89 }
90 TableStyle::Sharp => {
91 table.with(Style::sharp());
92 }
93 TableStyle::Ascii => {
94 table.with(Style::ascii());
95 }
96 TableStyle::Psql => {
97 table.with(Style::psql());
98 }
99 TableStyle::Dots => {
100 table.with(Style::dots());
101 }
102 }
103 }
104}
105
106pub struct StyledTable {
108 style: TableStyle,
109 header: Option<String>,
110 remove_header_row: bool,
111 padding: Option<Padding>,
112 newline_replacement: Option<String>,
113}
114
115impl Default for StyledTable {
116 fn default() -> Self {
117 Self::new()
118 }
119}
120
121impl StyledTable {
122 pub fn new() -> Self {
124 Self {
125 style: TableStyle::default(),
126 header: None,
127 remove_header_row: false,
128 padding: None,
129 newline_replacement: None,
130 }
131 }
132
133 pub fn style(mut self, style: TableStyle) -> Self {
135 self.style = style;
136 self
137 }
138
139 pub fn header(mut self, header: impl Into<String>) -> Self {
141 self.header = Some(header.into());
142 self
143 }
144
145 pub fn remove_header_row(mut self) -> Self {
149 self.remove_header_row = true;
150 self
151 }
152
153 pub fn padding(mut self, padding: Padding) -> Self {
157 self.padding = Some(padding);
158 self
159 }
160
161 pub fn replace_newlines(mut self, replacement: impl Into<String>) -> Self {
166 self.newline_replacement = Some(replacement.into());
167 self
168 }
169
170 pub fn build<T: tabled::Tabled>(self, data: Vec<T>) -> Table {
172 let mut table = Table::new(data);
173
174 self.style.apply(&mut table);
175
176 if let Some(padding) = self.padding {
177 table.with(padding);
178 }
179
180 if self.remove_header_row {
182 table.with(Remove::row(Rows::first()));
183 }
184
185 if let Some(header) = self.header {
186 table.with(Panel::header(header));
187 }
188
189 if let Some(replacement) = self.newline_replacement {
190 table.with(
191 Modify::new(Segment::all())
192 .with(Format::content(move |s| s.replace('\n', &replacement))),
193 );
194 }
195
196 table
197 }
198}
199
200pub fn display_option<T: Display>(opt: &Option<T>) -> String {
204 opt.as_ref().map_or_else(String::new, |val| val.to_string())
205}
206
207pub fn display_option_or<T: Display>(opt: &Option<T>, default: &str) -> String {
209 opt.as_ref()
210 .map_or_else(|| default.to_string(), |val| val.to_string())
211}