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))]
32#[cfg_attr(feature = "clap", derive(clap::ValueEnum))]
33#[cfg_attr(feature = "clap", clap(rename_all = "kebab-case"))]
34pub enum TableStyle {
35 #[default]
37 Modern,
38 Borderless,
40 Markdown,
42 Sharp,
44 Ascii,
46 Psql,
48 Dots,
50}
51
52impl TableStyle {
53 pub fn apply(self, table: &mut Table) {
55 match self {
56 TableStyle::Modern => {
57 table.with(Style::rounded());
58 }
59 TableStyle::Borderless => {
60 table.with(Style::blank());
61 }
62 TableStyle::Markdown => {
63 table.with(Style::markdown());
64 }
65 TableStyle::Sharp => {
66 table.with(Style::sharp());
67 }
68 TableStyle::Ascii => {
69 table.with(Style::ascii());
70 }
71 TableStyle::Psql => {
72 table.with(Style::psql());
73 }
74 TableStyle::Dots => {
75 table.with(Style::dots());
76 }
77 }
78 }
79}
80
81pub struct StyledTable {
83 style: TableStyle,
84 header: Option<String>,
85 remove_header_row: bool,
86 padding: Option<Padding>,
87 newline_replacement: Option<String>,
88}
89
90impl Default for StyledTable {
91 fn default() -> Self {
92 Self::new()
93 }
94}
95
96impl StyledTable {
97 pub fn new() -> Self {
99 Self {
100 style: TableStyle::default(),
101 header: None,
102 remove_header_row: false,
103 padding: None,
104 newline_replacement: None,
105 }
106 }
107
108 pub fn style(mut self, style: TableStyle) -> Self {
110 self.style = style;
111 self
112 }
113
114 pub fn header(mut self, header: impl Into<String>) -> Self {
116 self.header = Some(header.into());
117 self
118 }
119
120 pub fn remove_header_row(mut self) -> Self {
124 self.remove_header_row = true;
125 self
126 }
127
128 pub fn padding(mut self, padding: Padding) -> Self {
132 self.padding = Some(padding);
133 self
134 }
135
136 pub fn replace_newlines(mut self, replacement: impl Into<String>) -> Self {
141 self.newline_replacement = Some(replacement.into());
142 self
143 }
144
145 pub fn build<T: tabled::Tabled>(self, data: Vec<T>) -> Table {
147 let mut table = Table::new(data);
148
149 self.style.apply(&mut table);
150
151 if let Some(padding) = self.padding {
152 table.with(padding);
153 }
154
155 if self.remove_header_row {
157 table.with(Remove::row(Rows::first()));
158 }
159
160 if let Some(header) = self.header {
161 table.with(Panel::header(header));
162 }
163
164 if let Some(replacement) = self.newline_replacement {
165 table.with(
166 Modify::new(Segment::all())
167 .with(Format::content(move |s| s.replace('\n', &replacement))),
168 );
169 }
170
171 table
172 }
173}
174
175pub fn display_option<T: Display>(opt: &Option<T>) -> String {
179 opt.as_ref().map_or_else(String::new, |val| val.to_string())
180}
181
182pub fn display_option_or<T: Display>(opt: &Option<T>, default: &str) -> String {
184 opt.as_ref()
185 .map_or_else(|| default.to_string(), |val| val.to_string())
186}