1use crate::{alignment::Alignment, format::TableFormat};
2
3#[derive(Clone, Debug, Default)]
5pub enum Headers {
6 #[default]
8 None,
9 FirstRow,
11 Keys,
13 Explicit(Vec<String>),
15 Mapping(Vec<(String, String)>),
17}
18
19#[derive(Clone, Debug, Default)]
21pub enum FormatSpec {
22 #[default]
24 Default,
25 Fixed(String),
27 PerColumn(Vec<String>),
29}
30
31#[derive(Clone, Copy, Debug, PartialEq, Eq)]
33pub enum RowAlignment {
34 Top,
36 Center,
38 Bottom,
40}
41
42#[derive(Clone, Copy, Debug, PartialEq, Eq)]
44pub enum HeaderAlignment {
45 Align(Alignment),
47 SameAsColumn,
49}
50
51impl From<Alignment> for HeaderAlignment {
52 fn from(value: Alignment) -> Self {
53 HeaderAlignment::Align(value)
54 }
55}
56
57#[derive(Clone, Debug)]
59pub enum MissingValues {
60 Single(String),
62 PerColumn(Vec<String>),
64}
65
66impl Default for MissingValues {
67 fn default() -> Self {
68 Self::Single(String::new())
69 }
70}
71
72#[derive(Clone, Debug, Default)]
74pub enum ShowIndex {
75 #[default]
78 Default,
79 Always,
81 Never,
83 Values(Vec<String>),
85}
86
87#[derive(Clone, Debug)]
89pub struct TabulateOptions {
90 pub headers: Headers,
92 pub table_format: TableFormatChoice,
94 pub float_format: FormatSpec,
96 pub int_format: FormatSpec,
98 pub num_align: Option<Alignment>,
100 pub str_align: Option<Alignment>,
102 pub missing_values: MissingValues,
104 pub show_index: ShowIndex,
106 pub disable_numparse: bool,
108 pub disable_numparse_columns: Option<Vec<usize>>,
110 pub preserve_whitespace: bool,
112 pub enable_widechars: bool,
114 pub max_col_widths: Option<Vec<Option<usize>>>,
116 pub max_header_col_widths: Option<Vec<Option<usize>>>,
118 pub col_global_align: Option<Alignment>,
120 pub col_align: Vec<Option<Alignment>>,
122 pub headers_global_align: Option<Alignment>,
124 pub headers_align: Vec<Option<HeaderAlignment>>,
126 pub row_align: Vec<Option<RowAlignment>>,
128 pub row_global_align: Option<RowAlignment>,
130 pub break_long_words: bool,
132 pub break_on_hyphens: bool,
134}
135
136impl Default for TabulateOptions {
137 fn default() -> Self {
138 Self {
139 headers: Headers::default(),
140 table_format: TableFormatChoice::Name("simple".to_string()),
141 float_format: FormatSpec::Default,
142 int_format: FormatSpec::Default,
143 num_align: None,
144 str_align: None,
145 missing_values: MissingValues::default(),
146 show_index: ShowIndex::default(),
147 disable_numparse: false,
148 disable_numparse_columns: None,
149 preserve_whitespace: false,
150 enable_widechars: false,
151 max_col_widths: None,
152 max_header_col_widths: None,
153 col_global_align: None,
154 col_align: Vec::new(),
155 headers_global_align: None,
156 headers_align: Vec::new(),
157 row_align: Vec::new(),
158 row_global_align: None,
159 break_long_words: true,
160 break_on_hyphens: true,
161 }
162 }
163}
164
165impl TabulateOptions {
166 pub fn new() -> Self {
168 Self::default()
169 }
170
171 pub fn headers(mut self, headers: Headers) -> Self {
173 self.headers = headers;
174 self
175 }
176
177 pub fn table_format<S: Into<String>>(mut self, format: S) -> Self {
179 self.table_format = TableFormatChoice::Name(format.into());
180 self
181 }
182
183 pub fn table_format_custom(mut self, format: TableFormat) -> Self {
185 self.table_format = TableFormatChoice::Custom(Box::new(format));
186 self
187 }
188
189 pub fn float_format(mut self, format: FormatSpec) -> Self {
191 self.float_format = format;
192 self
193 }
194
195 pub fn int_format(mut self, format: FormatSpec) -> Self {
197 self.int_format = format;
198 self
199 }
200
201 pub fn num_align(mut self, align: Alignment) -> Self {
203 self.num_align = Some(align);
204 self
205 }
206
207 pub fn col_global_align(mut self, align: Alignment) -> Self {
209 self.col_global_align = Some(align);
210 self
211 }
212
213 pub fn str_align(mut self, align: Alignment) -> Self {
215 self.str_align = Some(align);
216 self
217 }
218
219 pub fn missing_value<S: Into<String>>(mut self, value: S) -> Self {
221 self.missing_values = MissingValues::Single(value.into());
222 self
223 }
224
225 pub fn missing_values<I, S>(mut self, values: I) -> Self
227 where
228 I: IntoIterator<Item = S>,
229 S: Into<String>,
230 {
231 let collected = values.into_iter().map(Into::into).collect();
232 self.missing_values = MissingValues::PerColumn(collected);
233 self
234 }
235
236 pub fn disable_numparse(mut self, disable: bool) -> Self {
238 self.disable_numparse = disable;
239 self
240 }
241
242 pub fn disable_numparse_columns<I>(mut self, columns: I) -> Self
244 where
245 I: IntoIterator<Item = usize>,
246 {
247 let collected = columns.into_iter().collect::<Vec<_>>();
248 self.disable_numparse_columns = Some(collected);
249 self
250 }
251
252 pub fn is_numparse_disabled(&self, column: Option<usize>) -> bool {
254 if self.disable_numparse {
255 return true;
256 }
257 match (column, &self.disable_numparse_columns) {
258 (Some(idx), Some(columns)) => columns.contains(&idx),
259 _ => false,
260 }
261 }
262
263 pub fn preserve_whitespace(mut self, preserve: bool) -> Self {
265 self.preserve_whitespace = preserve;
266 self
267 }
268
269 pub fn enable_widechars(mut self, enable: bool) -> Self {
271 self.enable_widechars = enable;
272 self
273 }
274
275 pub fn show_index(mut self, show_index: ShowIndex) -> Self {
277 self.show_index = show_index;
278 self
279 }
280
281 pub fn max_col_widths(mut self, widths: Vec<Option<usize>>) -> Self {
283 self.max_col_widths = Some(widths);
284 self
285 }
286
287 pub fn max_col_width(mut self, width: usize) -> Self {
289 self.max_col_widths = Some(vec![Some(width)]);
290 self
291 }
292
293 pub fn max_header_col_widths(mut self, widths: Vec<Option<usize>>) -> Self {
295 self.max_header_col_widths = Some(widths);
296 self
297 }
298
299 pub fn max_header_col_width(mut self, width: usize) -> Self {
301 self.max_header_col_widths = Some(vec![Some(width)]);
302 self
303 }
304
305 pub fn headers_mapping<I, K, V>(mut self, mapping: I) -> Self
307 where
308 I: IntoIterator<Item = (K, V)>,
309 K: Into<String>,
310 V: Into<String>,
311 {
312 let collected = mapping
313 .into_iter()
314 .map(|(key, value)| (key.into(), value.into()))
315 .collect();
316 self.headers = Headers::Mapping(collected);
317 self
318 }
319
320 pub fn col_alignments<I>(mut self, aligns: I) -> Self
322 where
323 I: IntoIterator<Item = Option<Alignment>>,
324 {
325 self.col_align = aligns.into_iter().collect();
326 self
327 }
328
329 pub fn headers_global_align(mut self, align: Alignment) -> Self {
331 self.headers_global_align = Some(align);
332 self
333 }
334
335 pub fn headers_alignments<I>(mut self, aligns: I) -> Self
337 where
338 I: IntoIterator<Item = Option<HeaderAlignment>>,
339 {
340 self.headers_align = aligns.into_iter().collect();
341 self
342 }
343
344 pub fn row_global_align(mut self, align: RowAlignment) -> Self {
346 self.row_global_align = Some(align);
347 self
348 }
349
350 pub fn row_alignments<I>(mut self, aligns: I) -> Self
352 where
353 I: IntoIterator<Item = Option<RowAlignment>>,
354 {
355 self.row_align = aligns.into_iter().collect();
356 self
357 }
358
359 pub(crate) fn table_format_name(&self) -> Option<&str> {
360 match &self.table_format {
361 TableFormatChoice::Name(name) => Some(name.as_str()),
362 TableFormatChoice::Custom(_) => None,
363 }
364 }
365}
366
367#[derive(Clone, Debug)]
369pub enum TableFormatChoice {
370 Name(String),
372 Custom(Box<TableFormat>),
374}