/rust/registry/src/index.crates.io-1949cf8c6b5b557f/tabled-0.20.0/src/tables/table.rs
Line | Count | Source |
1 | | //! This module contains a main table representation [`Table`]. |
2 | | |
3 | | use core::ops::DerefMut; |
4 | | use std::{borrow::Cow, fmt, iter::FromIterator}; |
5 | | |
6 | | use crate::{ |
7 | | builder::Builder, |
8 | | grid::{ |
9 | | colors::NoColors, |
10 | | config::{ |
11 | | AlignmentHorizontal, ColorMap, ColoredConfig, CompactConfig, Entity, Indent, Sides, |
12 | | SpannedConfig, |
13 | | }, |
14 | | dimension::{CompleteDimension, Dimension, Estimate, PeekableGridDimension}, |
15 | | records::{ |
16 | | vec_records::{Text, VecRecords}, |
17 | | ExactRecords, Records, |
18 | | }, |
19 | | PeekableGrid, |
20 | | }, |
21 | | settings::{object::Object, CellOption, Style, TableOption}, |
22 | | Tabled, |
23 | | }; |
24 | | |
25 | | /// The structure provides an interface for building a table for types that implements [`Tabled`]. |
26 | | /// |
27 | | /// To build a string representation of a table you must use a [`std::fmt::Display`]. |
28 | | /// Or simply call `.to_string()` method. |
29 | | /// |
30 | | /// The default table [`Style`] is [`Style::ascii`], |
31 | | /// with a single left and right [`Padding`]. |
32 | | /// |
33 | | /// ## Example |
34 | | /// |
35 | | /// ### Basic usage |
36 | | /// |
37 | | /// ```rust,no_run |
38 | | /// use tabled::Table; |
39 | | /// |
40 | | /// let table = Table::new(&["Year", "2021"]); |
41 | | /// ``` |
42 | | /// |
43 | | /// ### With settings |
44 | | /// |
45 | | /// ```rust,no_run |
46 | | /// use tabled::{Table, settings::{Style, Alignment}}; |
47 | | /// |
48 | | /// let data = vec!["Hello", "2021"]; |
49 | | /// let mut table = Table::new(&data); |
50 | | /// table |
51 | | /// .with(Style::psql()) |
52 | | /// .with(Alignment::left()); |
53 | | /// ``` |
54 | | /// |
55 | | /// ### With a [`Tabled`] trait. |
56 | | /// |
57 | | /// ```rust,no_run |
58 | | /// use tabled::{Table, Tabled}; |
59 | | /// |
60 | | /// #[derive(Tabled)] |
61 | | /// struct Character { |
62 | | /// good: f32, |
63 | | /// bad: f32, |
64 | | /// encouraging: f32, |
65 | | /// destructive: f32, |
66 | | /// } |
67 | | /// |
68 | | /// #[derive(Tabled)] |
69 | | /// struct Person<'a>( |
70 | | /// #[tabled(rename = "name")] &'a str, |
71 | | /// #[tabled(inline)] Character, |
72 | | /// ); |
73 | | /// |
74 | | /// let data = vec![ |
75 | | /// Person("007", Character { good: 0.8, bad: 0.2, encouraging: 0.8, destructive: 0.1}), |
76 | | /// Person("001", Character { good: 0.2, bad: 0.5, encouraging: 0.2, destructive: 0.1}), |
77 | | /// Person("006", Character { good: 0.4, bad: 0.1, encouraging: 0.5, destructive: 0.8}), |
78 | | /// ]; |
79 | | /// |
80 | | /// let table = Table::new(&data); |
81 | | /// ``` |
82 | | /// |
83 | | /// [`Padding`]: crate::settings::Padding |
84 | | /// [`Style`]: crate::settings::Style |
85 | | /// [`Style::ascii`]: crate::settings::Style::ascii |
86 | | #[derive(Debug, Clone, PartialEq, Eq)] |
87 | | pub struct Table { |
88 | | records: VecRecords<Text<String>>, |
89 | | config: ColoredConfig, |
90 | | dimension: CompleteDimension, |
91 | | } |
92 | | |
93 | | impl Table { |
94 | | /// Creates a Table instance, from a list of [`Tabled`] values. |
95 | | /// |
96 | | /// If you use a reference iterator you'd better use [`FromIterator`] instead. |
97 | | /// As it has a different lifetime constraints and make less copies therefore. |
98 | | /// |
99 | | /// # Examples |
100 | | /// |
101 | | /// ``` |
102 | | /// use tabled::{Table, Tabled, assert::assert_table}; |
103 | | /// |
104 | | /// #[derive(Tabled)] |
105 | | /// struct Relationship { |
106 | | /// love: bool |
107 | | /// } |
108 | | /// |
109 | | /// let list = vec![ |
110 | | /// Relationship { love: true }, |
111 | | /// Relationship { love: false }, |
112 | | /// ]; |
113 | | /// |
114 | | /// let table = Table::new(list); |
115 | | /// |
116 | | /// assert_table!( |
117 | | /// table, |
118 | | /// "+-------+" |
119 | | /// "| love |" |
120 | | /// "+-------+" |
121 | | /// "| true |" |
122 | | /// "+-------+" |
123 | | /// "| false |" |
124 | | /// "+-------+" |
125 | | /// ); |
126 | | /// ``` |
127 | | /// |
128 | | /// ## Don't hesitate to use iterators. |
129 | | /// |
130 | | /// ``` |
131 | | /// use tabled::{Table, Tabled, assert::assert_table}; |
132 | | /// |
133 | | /// #[derive(Tabled)] |
134 | | /// struct Relationship { |
135 | | /// person: String, |
136 | | /// love: bool |
137 | | /// } |
138 | | /// |
139 | | /// let list = vec![ |
140 | | /// Relationship { person: String::from("Clara"), love: true }, |
141 | | /// Relationship { person: String::from("Greg"), love: false }, |
142 | | /// ]; |
143 | | /// |
144 | | /// // Maybe don't love but don't hate :) |
145 | | /// let iter = list.into_iter() |
146 | | /// .map(|mut rel| { |
147 | | /// if !rel.love { |
148 | | /// rel.love = true; |
149 | | /// } |
150 | | /// |
151 | | /// rel |
152 | | /// }); |
153 | | /// |
154 | | /// let table = Table::new(iter); |
155 | | /// |
156 | | /// assert_table!( |
157 | | /// table, |
158 | | /// "+--------+------+" |
159 | | /// "| person | love |" |
160 | | /// "+--------+------+" |
161 | | /// "| Clara | true |" |
162 | | /// "+--------+------+" |
163 | | /// "| Greg | true |" |
164 | | /// "+--------+------+" |
165 | | /// ); |
166 | | /// ``` |
167 | | /// |
168 | | /// ## Notice that you can pass tuples. |
169 | | /// |
170 | | /// ``` |
171 | | /// use tabled::{Table, Tabled, assert::assert_table}; |
172 | | /// |
173 | | /// #[derive(Tabled)] |
174 | | /// struct Relationship { |
175 | | /// love: bool |
176 | | /// } |
177 | | /// |
178 | | /// let list = vec![ |
179 | | /// ("Kate", Relationship { love: true }), |
180 | | /// ("", Relationship { love: false }), |
181 | | /// ]; |
182 | | /// |
183 | | /// let table = Table::new(list); |
184 | | /// |
185 | | /// assert_table!( |
186 | | /// table, |
187 | | /// "+------+-------+" |
188 | | /// "| &str | love |" |
189 | | /// "+------+-------+" |
190 | | /// "| Kate | true |" |
191 | | /// "+------+-------+" |
192 | | /// "| | false |" |
193 | | /// "+------+-------+" |
194 | | /// ); |
195 | | /// ``` |
196 | | /// |
197 | | /// ## As a different way to create a [`Table`], you can use [`Table::from_iter`]. |
198 | | /// |
199 | | /// ``` |
200 | | /// use std::iter::FromIterator; |
201 | | /// use tabled::{Table, assert::assert_table}; |
202 | | /// |
203 | | /// let list = vec![ |
204 | | /// vec!["Kate", "+", "+", "+", "-"], |
205 | | /// vec!["", "-", "-", "-", "-"], |
206 | | /// ]; |
207 | | /// |
208 | | /// let table = Table::from_iter(list); |
209 | | /// |
210 | | /// assert_table!( |
211 | | /// table, |
212 | | /// "+------+---+---+---+---+" |
213 | | /// "| Kate | + | + | + | - |" |
214 | | /// "+------+---+---+---+---+" |
215 | | /// "| | - | - | - | - |" |
216 | | /// "+------+---+---+---+---+" |
217 | | /// ); |
218 | | /// ``` |
219 | 0 | pub fn new<I, T>(iter: I) -> Self |
220 | 0 | where |
221 | 0 | I: IntoIterator<Item = T>, |
222 | 0 | T: Tabled, |
223 | | { |
224 | 0 | let mut header = Vec::with_capacity(T::LENGTH); |
225 | 0 | for text in T::headers() { |
226 | 0 | let text = text.into_owned(); |
227 | 0 | let cell = Text::new(text); |
228 | 0 | header.push(cell); |
229 | 0 | } |
230 | | |
231 | 0 | let mut records = vec![header]; |
232 | 0 | for row in iter.into_iter() { |
233 | 0 | let mut list = Vec::with_capacity(T::LENGTH); |
234 | 0 | for text in row.fields().into_iter() { |
235 | 0 | let text = text.into_owned(); |
236 | 0 | let cell = Text::new(text); |
237 | 0 |
|
238 | 0 | list.push(cell); |
239 | 0 | } |
240 | | |
241 | 0 | records.push(list); |
242 | | } |
243 | | |
244 | 0 | let records = VecRecords::new(records); |
245 | 0 | let config = ColoredConfig::new(configure_grid()); |
246 | 0 | let dimension = CompleteDimension::default(); |
247 | | |
248 | 0 | Self { |
249 | 0 | records, |
250 | 0 | config, |
251 | 0 | dimension, |
252 | 0 | } |
253 | 0 | } Unexecuted instantiation: <tabled::tables::table::Table>::new::<alloc::vec::Vec<anise::naif::pretty_print::BpcRow>, anise::naif::pretty_print::BpcRow> Unexecuted instantiation: <tabled::tables::table::Table>::new::<alloc::vec::Vec<anise::naif::pretty_print::SpkRow>, anise::naif::pretty_print::SpkRow> Unexecuted instantiation: <tabled::tables::table::Table>::new::<alloc::vec::Vec<anise::almanac::planetary::PlanetaryRow>, anise::almanac::planetary::PlanetaryRow> Unexecuted instantiation: <tabled::tables::table::Table>::new::<alloc::vec::Vec<anise::structure::spacecraft::SpacecraftRow>, anise::structure::spacecraft::SpacecraftRow> Unexecuted instantiation: <tabled::tables::table::Table>::new::<alloc::vec::Vec<anise::structure::dataset::pretty_print::LocationRow>, anise::structure::dataset::pretty_print::LocationRow> Unexecuted instantiation: <tabled::tables::table::Table>::new::<alloc::vec::Vec<anise::structure::dataset::pretty_print::EulerParamRow>, anise::structure::dataset::pretty_print::EulerParamRow> Unexecuted instantiation: <tabled::tables::table::Table>::new::<_, _> |
254 | | |
255 | | /// Creates a Table instance, from a list of [`Tabled`] values. |
256 | | /// |
257 | | /// It's an optimized version of [`Table::new`]. |
258 | | /// |
259 | | /// ``` |
260 | | /// use tabled::{Table, Tabled, assert::assert_table}; |
261 | | /// |
262 | | /// #[derive(Tabled)] |
263 | | /// struct Relationship { |
264 | | /// person: String, |
265 | | /// love: bool |
266 | | /// } |
267 | | /// |
268 | | /// let list = vec![ |
269 | | /// Relationship { person: String::from("Clara"), love: true }, |
270 | | /// Relationship { person: String::from("Greg"), love: false }, |
271 | | /// ]; |
272 | | /// |
273 | | /// let table = Table::with_capacity(&list, list.len()); |
274 | | /// |
275 | | /// assert_table!( |
276 | | /// table, |
277 | | /// "+--------+-------+" |
278 | | /// "| person | love |" |
279 | | /// "+--------+-------+" |
280 | | /// "| Clara | true |" |
281 | | /// "+--------+-------+" |
282 | | /// "| Greg | false |" |
283 | | /// "+--------+-------+" |
284 | | /// ); |
285 | | /// ``` |
286 | 0 | pub fn with_capacity<I, T>(iter: I, count_rows: usize) -> Self |
287 | 0 | where |
288 | 0 | I: IntoIterator<Item = T>, |
289 | 0 | T: Tabled, |
290 | | { |
291 | 0 | let mut header = Vec::with_capacity(T::LENGTH); |
292 | 0 | for text in T::headers() { |
293 | 0 | let text = text.into_owned(); |
294 | 0 | let cell = Text::new(text); |
295 | 0 | header.push(cell); |
296 | 0 | } |
297 | | |
298 | 0 | let mut records = Vec::with_capacity(count_rows + 1); |
299 | 0 | records.push(header); |
300 | | |
301 | 0 | for row in iter.into_iter() { |
302 | 0 | let mut list = Vec::with_capacity(T::LENGTH); |
303 | 0 | for text in row.fields().into_iter() { |
304 | 0 | let text = text.into_owned(); |
305 | 0 | let cell = Text::new(text); |
306 | 0 |
|
307 | 0 | list.push(cell); |
308 | 0 | } |
309 | | |
310 | 0 | records.push(list); |
311 | | } |
312 | | |
313 | 0 | let records = VecRecords::new(records); |
314 | 0 | let config = ColoredConfig::new(configure_grid()); |
315 | 0 | let dimension = CompleteDimension::default(); |
316 | | |
317 | 0 | Self { |
318 | 0 | records, |
319 | 0 | config, |
320 | 0 | dimension, |
321 | 0 | } |
322 | 0 | } |
323 | | |
324 | | /// Creates a Table instance, from a list of [`Tabled`] values. |
325 | | /// |
326 | | /// Compared to [`Table::new`] it does not use a "header" (first line). |
327 | | /// |
328 | | /// If you use a reference iterator you'd better use [`FromIterator`] instead. |
329 | | /// As it has a different lifetime constraints and make less copies therefore. |
330 | | /// |
331 | | /// # Examples |
332 | | /// |
333 | | /// ``` |
334 | | /// use tabled::{Table, Tabled, assert::assert_table}; |
335 | | /// |
336 | | /// #[derive(Tabled)] |
337 | | /// struct Relationship { |
338 | | /// love: bool |
339 | | /// } |
340 | | /// |
341 | | /// let list = vec![ |
342 | | /// ("Kate", Relationship { love: true }), |
343 | | /// ("", Relationship { love: false }), |
344 | | /// ]; |
345 | | /// |
346 | | /// let table = Table::nohead(list); |
347 | | /// |
348 | | /// assert_table!( |
349 | | /// table, |
350 | | /// "+------+-------+" |
351 | | /// "| Kate | true |" |
352 | | /// "+------+-------+" |
353 | | /// "| | false |" |
354 | | /// "+------+-------+" |
355 | | /// ); |
356 | | /// ``` |
357 | 0 | pub fn nohead<I, T>(iter: I) -> Self |
358 | 0 | where |
359 | 0 | I: IntoIterator<Item = T>, |
360 | 0 | T: Tabled, |
361 | | { |
362 | 0 | let mut records = vec![]; |
363 | 0 | for row in iter.into_iter() { |
364 | 0 | let mut list = Vec::with_capacity(T::LENGTH); |
365 | 0 | for text in row.fields().into_iter() { |
366 | 0 | let text = text.into_owned(); |
367 | 0 | let cell = Text::new(text); |
368 | 0 |
|
369 | 0 | list.push(cell); |
370 | 0 | } |
371 | | |
372 | 0 | records.push(list); |
373 | | } |
374 | | |
375 | 0 | let records = VecRecords::new(records); |
376 | 0 | let config = ColoredConfig::new(configure_grid()); |
377 | 0 | let dimension = CompleteDimension::default(); |
378 | | |
379 | 0 | Self { |
380 | 0 | records, |
381 | 0 | config, |
382 | 0 | dimension, |
383 | 0 | } |
384 | 0 | } |
385 | | |
386 | | /// Creates a Key-Value [`Table`] instance, from a list of [`Tabled`] values. |
387 | | /// |
388 | | /// # Examples |
389 | | /// |
390 | | /// ``` |
391 | | /// use tabled::{Table, Tabled, assert::assert_table}; |
392 | | /// |
393 | | /// #[derive(Tabled)] |
394 | | /// #[tabled(rename_all = "PascalCase")] |
395 | | /// struct Swim { |
396 | | /// event: String, |
397 | | /// time: String, |
398 | | /// #[tabled(rename = "Pool Length")] |
399 | | /// pool: u8, |
400 | | /// } |
401 | | /// |
402 | | /// const POOL_25: u8 = 25; |
403 | | /// const POOL_50: u8 = 50; |
404 | | /// |
405 | | /// let list = vec![ |
406 | | /// Swim { event: String::from("Men 100 Freestyle"), time: String::from("47.77"), pool: POOL_25 }, |
407 | | /// Swim { event: String::from("Men 400 Freestyle"), time: String::from("03:59.16"), pool: POOL_25 }, |
408 | | /// Swim { event: String::from("Men 800 Freestyle"), time: String::from("08:06.70"), pool: POOL_25 }, |
409 | | /// Swim { event: String::from("Men 4x100 Medley Relay"), time: String::from("03:27.28"), pool: POOL_50 }, |
410 | | /// ]; |
411 | | /// |
412 | | /// let table = Table::kv(list); |
413 | | /// |
414 | | /// assert_table!( |
415 | | /// table, |
416 | | /// "+-------------+------------------------+" |
417 | | /// "| Event | Men 100 Freestyle |" |
418 | | /// "+-------------+------------------------+" |
419 | | /// "| Time | 47.77 |" |
420 | | /// "+-------------+------------------------+" |
421 | | /// "| Pool Length | 25 |" |
422 | | /// "+-------------+------------------------+" |
423 | | /// "| Event | Men 400 Freestyle |" |
424 | | /// "+-------------+------------------------+" |
425 | | /// "| Time | 03:59.16 |" |
426 | | /// "+-------------+------------------------+" |
427 | | /// "| Pool Length | 25 |" |
428 | | /// "+-------------+------------------------+" |
429 | | /// "| Event | Men 800 Freestyle |" |
430 | | /// "+-------------+------------------------+" |
431 | | /// "| Time | 08:06.70 |" |
432 | | /// "+-------------+------------------------+" |
433 | | /// "| Pool Length | 25 |" |
434 | | /// "+-------------+------------------------+" |
435 | | /// "| Event | Men 4x100 Medley Relay |" |
436 | | /// "+-------------+------------------------+" |
437 | | /// "| Time | 03:27.28 |" |
438 | | /// "+-------------+------------------------+" |
439 | | /// "| Pool Length | 50 |" |
440 | | /// "+-------------+------------------------+" |
441 | | /// ); |
442 | | /// ``` |
443 | | /// |
444 | | /// Next you'll find a more complex example with a subtle style. |
445 | | /// |
446 | | /// ``` |
447 | | /// use tabled::{Table, Tabled, settings::Style}; |
448 | | /// use tabled::settings::{style::HorizontalLine, Theme}; |
449 | | /// use tabled::assert::assert_table; |
450 | | /// |
451 | | /// #[derive(Tabled)] |
452 | | /// #[tabled(rename_all = "PascalCase")] |
453 | | /// struct Swim { |
454 | | /// event: String, |
455 | | /// time: String, |
456 | | /// #[tabled(rename = "Pool Length")] |
457 | | /// pool: u8, |
458 | | /// } |
459 | | /// |
460 | | /// const POOL_25: u8 = 25; |
461 | | /// const POOL_50: u8 = 50; |
462 | | /// |
463 | | /// let list = vec![ |
464 | | /// Swim { event: String::from("Men 100 Freestyle"), time: String::from("47.77"), pool: POOL_25 }, |
465 | | /// Swim { event: String::from("Men 400 Freestyle"), time: String::from("03:59.16"), pool: POOL_25 }, |
466 | | /// Swim { event: String::from("Men 800 Freestyle"), time: String::from("08:06.70"), pool: POOL_25 }, |
467 | | /// Swim { event: String::from("Men 4x100 Medley Relay"), time: String::from("03:27.28"), pool: POOL_50 }, |
468 | | /// ]; |
469 | | /// |
470 | | /// let mut table = Table::kv(list); |
471 | | /// |
472 | | /// let mut style = Theme::from_style(Style::rounded().remove_horizontals()); |
473 | | /// for entry in 1 .. table.count_rows() / Swim::LENGTH { |
474 | | /// style.insert_horizontal_line(entry * Swim::LENGTH, HorizontalLine::inherit(Style::modern())); |
475 | | /// } |
476 | | /// |
477 | | /// table.with(style); |
478 | | /// |
479 | | /// assert_table!( |
480 | | /// table, |
481 | | /// "â•─────────────┬────────────────────────╮" |
482 | | /// "│ Event │ Men 100 Freestyle │" |
483 | | /// "│ Time │ 47.77 │" |
484 | | /// "│ Pool Length │ 25 │" |
485 | | /// "├─────────────┼────────────────────────┤" |
486 | | /// "│ Event │ Men 400 Freestyle │" |
487 | | /// "│ Time │ 03:59.16 │" |
488 | | /// "│ Pool Length │ 25 │" |
489 | | /// "├─────────────┼────────────────────────┤" |
490 | | /// "│ Event │ Men 800 Freestyle │" |
491 | | /// "│ Time │ 08:06.70 │" |
492 | | /// "│ Pool Length │ 25 │" |
493 | | /// "├─────────────┼────────────────────────┤" |
494 | | /// "│ Event │ Men 4x100 Medley Relay │" |
495 | | /// "│ Time │ 03:27.28 │" |
496 | | /// "│ Pool Length │ 50 │" |
497 | | /// "╰─────────────┴────────────────────────╯" |
498 | | /// ); |
499 | | /// ``` |
500 | 0 | pub fn kv<I, T>(iter: I) -> Self |
501 | 0 | where |
502 | 0 | I: IntoIterator<Item = T>, |
503 | 0 | T: Tabled, |
504 | | { |
505 | 0 | let headers = T::headers(); |
506 | | |
507 | 0 | let mut records = Vec::new(); |
508 | 0 | for row in iter.into_iter() { |
509 | 0 | for (text, name) in row.fields().into_iter().zip(headers.iter()) { |
510 | 0 | let key = Text::new(name.clone().into_owned()); |
511 | 0 | let value = Text::new(text.into_owned()); |
512 | 0 |
|
513 | 0 | records.push(vec![key, value]); |
514 | 0 | } |
515 | | } |
516 | | |
517 | 0 | let records = VecRecords::new(records); |
518 | 0 | let config = ColoredConfig::new(configure_grid()); |
519 | 0 | let dimension = CompleteDimension::default(); |
520 | | |
521 | 0 | Self { |
522 | 0 | records, |
523 | 0 | config, |
524 | 0 | dimension, |
525 | 0 | } |
526 | 0 | } |
527 | | |
528 | | /// Creates a builder from a data set given. |
529 | | /// |
530 | | /// # Example |
531 | | /// |
532 | | #[cfg_attr(feature = "derive", doc = "```")] |
533 | | #[cfg_attr(not(feature = "derive"), doc = "```ignore")] |
534 | | /// use tabled::{ |
535 | | /// Table, Tabled, |
536 | | /// settings::{object::Segment, Modify, Alignment} |
537 | | /// }; |
538 | | /// |
539 | | /// #[derive(Tabled)] |
540 | | /// struct User { |
541 | | /// name: &'static str, |
542 | | /// #[tabled(inline("device::"))] |
543 | | /// device: Device, |
544 | | /// } |
545 | | /// |
546 | | /// #[derive(Tabled)] |
547 | | /// enum Device { |
548 | | /// PC, |
549 | | /// Mobile |
550 | | /// } |
551 | | /// |
552 | | /// let data = vec![ |
553 | | /// User { name: "Vlad", device: Device::Mobile }, |
554 | | /// User { name: "Dimitry", device: Device::PC }, |
555 | | /// User { name: "John", device: Device::PC }, |
556 | | /// ]; |
557 | | /// |
558 | | /// let mut table = Table::builder(data) |
559 | | /// .index() |
560 | | /// .column(0) |
561 | | /// .transpose() |
562 | | /// .build() |
563 | | /// .modify(Segment::new(1.., 1..), Alignment::center()) |
564 | | /// .to_string(); |
565 | | /// |
566 | | /// assert_eq!( |
567 | | /// table, |
568 | | /// "+----------------+------+---------+------+\n\ |
569 | | /// | name | Vlad | Dimitry | John |\n\ |
570 | | /// +----------------+------+---------+------+\n\ |
571 | | /// | device::PC | | + | + |\n\ |
572 | | /// +----------------+------+---------+------+\n\ |
573 | | /// | device::Mobile | + | | |\n\ |
574 | | /// +----------------+------+---------+------+" |
575 | | /// ) |
576 | | /// ``` |
577 | 0 | pub fn builder<I, T>(iter: I) -> Builder |
578 | 0 | where |
579 | 0 | T: Tabled, |
580 | 0 | I: IntoIterator<Item = T>, |
581 | | { |
582 | 0 | let mut builder = Builder::with_capacity(1, T::LENGTH); |
583 | 0 | builder.push_record(T::headers()); |
584 | | |
585 | 0 | for row in iter { |
586 | 0 | builder.push_record(row.fields().into_iter()); |
587 | 0 | } |
588 | | |
589 | 0 | builder |
590 | 0 | } |
591 | | |
592 | | /// It's a generic function which applies options to the [`Table`]. |
593 | | /// |
594 | | /// It applies settings immediately. |
595 | | /// |
596 | | /// ``` |
597 | | /// use tabled::{Table, settings::Style}; |
598 | | /// use tabled::assert::assert_table; |
599 | | /// |
600 | | /// let data = vec![ |
601 | | /// ("number", "name"), |
602 | | /// ("285-324-7322", "Rosalia"), |
603 | | /// ("564.549.6468", "Mary"), |
604 | | /// ]; |
605 | | /// |
606 | | /// let mut table = Table::new(data); |
607 | | /// table.with(Style::markdown()); |
608 | | /// |
609 | | /// assert_table!( |
610 | | /// table, |
611 | | /// "| &str | &str |" |
612 | | /// "|--------------|---------|" |
613 | | /// "| number | name |" |
614 | | /// "| 285-324-7322 | Rosalia |" |
615 | | /// "| 564.549.6468 | Mary |" |
616 | | /// ); |
617 | | /// ``` |
618 | 0 | pub fn with<O>(&mut self, option: O) -> &mut Self |
619 | 0 | where |
620 | 0 | O: TableOption<VecRecords<Text<String>>, ColoredConfig, CompleteDimension>, |
621 | | { |
622 | 0 | let reastimation_hint = option.hint_change(); |
623 | | |
624 | 0 | option.change(&mut self.records, &mut self.config, &mut self.dimension); |
625 | 0 | self.dimension.reastimate(reastimation_hint); |
626 | | |
627 | 0 | self |
628 | 0 | } Unexecuted instantiation: <tabled::tables::table::Table>::with::<tabled::settings::style::builder::Style<tabled::settings::style::builder::On, tabled::settings::style::builder::On, tabled::settings::style::builder::On, tabled::settings::style::builder::On, tabled::settings::style::builder::On, tabled::settings::style::builder::On, 0, 0>> Unexecuted instantiation: <tabled::tables::table::Table>::with::<tabled::settings::style::builder::Style<tabled::settings::style::builder::On, tabled::settings::style::builder::On, tabled::settings::style::builder::On, tabled::settings::style::builder::On, (), tabled::settings::style::builder::On, 1, 0>> Unexecuted instantiation: <tabled::tables::table::Table>::with::<tabled::settings::style::builder::Style<(), (), (), (), (), (), 0, 0>> Unexecuted instantiation: <tabled::tables::table::Table>::with::<tabled::settings::padding::Padding> |
629 | | |
630 | | /// It's a generic function which applies options to particular cells on the [`Table`]. |
631 | | /// Target cells using [`Object`]s such as [`Cell`], [`Rows`], [`Location`] and more. |
632 | | /// |
633 | | /// It applies settings immediately. |
634 | | /// |
635 | | /// ``` |
636 | | /// use tabled::{Table, settings::{object::Columns, Alignment}}; |
637 | | /// use tabled::assert::assert_table; |
638 | | /// |
639 | | /// let data = vec![ |
640 | | /// ("number", "name"), |
641 | | /// ("285-324-7322", "Rosalia"), |
642 | | /// ("564.549.6468", "Mary"), |
643 | | /// ]; |
644 | | /// |
645 | | /// let mut table = Table::new(data); |
646 | | /// table.modify(Columns::first(), Alignment::right()); |
647 | | /// table.modify(Columns::one(1), Alignment::center()); |
648 | | /// |
649 | | /// assert_table!( |
650 | | /// table, |
651 | | /// "+--------------+---------+" |
652 | | /// "| &str | &str |" |
653 | | /// "+--------------+---------+" |
654 | | /// "| number | name |" |
655 | | /// "+--------------+---------+" |
656 | | /// "| 285-324-7322 | Rosalia |" |
657 | | /// "+--------------+---------+" |
658 | | /// "| 564.549.6468 | Mary |" |
659 | | /// "+--------------+---------+" |
660 | | /// ); |
661 | | /// ``` |
662 | | /// |
663 | | /// [`Cell`]: crate::settings::object::Cell |
664 | | /// [`Rows`]: crate::settings::object::Rows |
665 | | /// [`Location`]: crate::settings::location::Locator |
666 | 0 | pub fn modify<T, O>(&mut self, target: T, option: O) -> &mut Self |
667 | 0 | where |
668 | 0 | T: Object<VecRecords<Text<String>>>, |
669 | 0 | O: CellOption<VecRecords<Text<String>>, ColoredConfig> + Clone, |
670 | | { |
671 | 0 | for entity in target.cells(&self.records) { |
672 | 0 | let opt = option.clone(); |
673 | 0 | opt.change(&mut self.records, &mut self.config, entity); |
674 | 0 | } |
675 | | |
676 | 0 | let hint = option.hint_change(); |
677 | 0 | self.dimension.reastimate(hint); |
678 | | |
679 | 0 | self |
680 | 0 | } |
681 | | |
682 | | /// Returns a table shape (count rows, count columns). |
683 | 0 | pub fn shape(&self) -> (usize, usize) { |
684 | 0 | (self.count_rows(), self.count_columns()) |
685 | 0 | } |
686 | | |
687 | | /// Returns an amount of rows in the table. |
688 | 0 | pub fn count_rows(&self) -> usize { |
689 | 0 | self.records.count_rows() |
690 | 0 | } |
691 | | |
692 | | /// Returns an amount of columns in the table. |
693 | 0 | pub fn count_columns(&self) -> usize { |
694 | 0 | self.records.count_columns() |
695 | 0 | } |
696 | | |
697 | | /// Returns a table shape (count rows, count columns). |
698 | 0 | pub fn is_empty(&self) -> bool { |
699 | 0 | let (count_rows, count_cols) = self.shape(); |
700 | 0 | count_rows == 0 || count_cols == 0 |
701 | 0 | } |
702 | | |
703 | | /// Returns total widths of a table, including margin and horizontal lines. |
704 | 0 | pub fn total_height(&self) -> usize { |
705 | 0 | let mut dims = self.dimension.clone(); |
706 | 0 | dims.estimate(&self.records, self.config.as_ref()); |
707 | | |
708 | 0 | let total = (0..self.count_rows()) |
709 | 0 | .map(|row| dims.get_height(row)) |
710 | 0 | .sum::<usize>(); |
711 | 0 | let counth = self.config.count_horizontal(self.count_rows()); |
712 | | |
713 | 0 | let margin = self.config.get_margin(); |
714 | | |
715 | 0 | total + counth + margin.top.size + margin.bottom.size |
716 | 0 | } |
717 | | |
718 | | /// Returns total widths of a table, including margin and vertical lines. |
719 | 0 | pub fn total_width(&self) -> usize { |
720 | 0 | let mut dims = self.dimension.clone(); |
721 | 0 | dims.estimate(&self.records, self.config.as_ref()); |
722 | | |
723 | 0 | let total = (0..self.count_columns()) |
724 | 0 | .map(|col| dims.get_width(col)) |
725 | 0 | .sum::<usize>(); |
726 | 0 | let countv = self.config.count_vertical(self.count_columns()); |
727 | | |
728 | 0 | let margin = self.config.get_margin(); |
729 | | |
730 | 0 | total + countv + margin.left.size + margin.right.size |
731 | 0 | } |
732 | | |
733 | | /// Returns a table config. |
734 | 0 | pub fn get_config(&self) -> &ColoredConfig { |
735 | 0 | &self.config |
736 | 0 | } |
737 | | |
738 | | /// Returns a table config. |
739 | 0 | pub fn get_config_mut(&mut self) -> &mut ColoredConfig { |
740 | 0 | &mut self.config |
741 | 0 | } |
742 | | |
743 | | /// Returns a used records. |
744 | 0 | pub fn get_records(&self) -> &VecRecords<Text<String>> { |
745 | 0 | &self.records |
746 | 0 | } |
747 | | |
748 | | /// Returns a used records. |
749 | 0 | pub fn get_records_mut(&mut self) -> &mut VecRecords<Text<String>> { |
750 | 0 | &mut self.records |
751 | 0 | } |
752 | | |
753 | | /// Returns a dimension. |
754 | 0 | pub fn get_dimension(&self) -> &CompleteDimension { |
755 | 0 | &self.dimension |
756 | 0 | } |
757 | | |
758 | | /// Returns a dimension. |
759 | 0 | pub fn get_dimension_mut(&mut self) -> &mut CompleteDimension { |
760 | 0 | &mut self.dimension |
761 | 0 | } |
762 | | } |
763 | | |
764 | | impl Default for Table { |
765 | 0 | fn default() -> Self { |
766 | 0 | Self { |
767 | 0 | records: VecRecords::default(), |
768 | 0 | config: ColoredConfig::new(configure_grid()), |
769 | 0 | dimension: CompleteDimension::default(), |
770 | 0 | } |
771 | 0 | } |
772 | | } |
773 | | |
774 | | impl fmt::Display for Table { |
775 | 0 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
776 | 0 | if self.is_empty() { |
777 | 0 | return Ok(()); |
778 | 0 | } |
779 | | |
780 | 0 | let config = use_format_configuration(f, self); |
781 | 0 | let colors = self.config.get_colors(); |
782 | | |
783 | 0 | if !self.dimension.is_empty() { |
784 | 0 | let mut dims = self.dimension.clone(); |
785 | 0 | dims.estimate(&self.records, config.as_ref()); |
786 | | |
787 | 0 | print_grid(f, &self.records, &config, &dims, colors) |
788 | | } else { |
789 | 0 | let mut dims = PeekableGridDimension::default(); |
790 | 0 | dims.estimate(&self.records, &config); |
791 | | |
792 | 0 | print_grid(f, &self.records, &config, &dims, colors) |
793 | | } |
794 | 0 | } |
795 | | } |
796 | | |
797 | | impl<T> FromIterator<T> for Table |
798 | | where |
799 | | T: IntoIterator, |
800 | | T::Item: Into<String>, |
801 | | { |
802 | 0 | fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self { |
803 | 0 | Builder::from_iter(iter.into_iter().map(|i| i.into_iter().map(|s| s.into()))).build() |
804 | 0 | } |
805 | | } |
806 | | |
807 | | impl From<Builder> for Table { |
808 | 0 | fn from(builder: Builder) -> Self { |
809 | 0 | let data = builder.into(); |
810 | 0 | let records = VecRecords::new(data); |
811 | 0 | let config = ColoredConfig::new(configure_grid()); |
812 | 0 | let dimension = CompleteDimension::default(); |
813 | | |
814 | 0 | Self { |
815 | 0 | records, |
816 | 0 | config, |
817 | 0 | dimension, |
818 | 0 | } |
819 | 0 | } |
820 | | } |
821 | | |
822 | | impl From<Table> for Builder { |
823 | 0 | fn from(val: Table) -> Self { |
824 | 0 | let data = val.records.into(); |
825 | 0 | Builder::from_vec(data) |
826 | 0 | } |
827 | | } |
828 | | |
829 | | impl<R, D> TableOption<R, ColoredConfig, D> for CompactConfig { |
830 | 0 | fn change(self, _: &mut R, cfg: &mut ColoredConfig, _: &mut D) { |
831 | 0 | *cfg.deref_mut() = self.into(); |
832 | 0 | } |
833 | | } |
834 | | |
835 | | impl<R, D> TableOption<R, ColoredConfig, D> for ColoredConfig { |
836 | 0 | fn change(self, _: &mut R, cfg: &mut ColoredConfig, _: &mut D) { |
837 | 0 | *cfg = self; |
838 | 0 | } |
839 | | } |
840 | | |
841 | | impl<R, D> TableOption<R, ColoredConfig, D> for SpannedConfig { |
842 | 0 | fn change(self, _: &mut R, cfg: &mut ColoredConfig, _: &mut D) { |
843 | 0 | *cfg.deref_mut() = self; |
844 | 0 | } |
845 | | } |
846 | | |
847 | | impl<R, D> TableOption<R, ColoredConfig, D> for &SpannedConfig { |
848 | 0 | fn change(self, _: &mut R, cfg: &mut ColoredConfig, _: &mut D) { |
849 | 0 | *cfg.deref_mut() = self.clone(); |
850 | 0 | } |
851 | | } |
852 | | |
853 | 0 | fn convert_fmt_alignment(alignment: fmt::Alignment) -> AlignmentHorizontal { |
854 | 0 | match alignment { |
855 | 0 | fmt::Alignment::Left => AlignmentHorizontal::Left, |
856 | 0 | fmt::Alignment::Right => AlignmentHorizontal::Right, |
857 | 0 | fmt::Alignment::Center => AlignmentHorizontal::Center, |
858 | | } |
859 | 0 | } |
860 | | |
861 | 0 | fn table_padding(alignment: fmt::Alignment, available: usize) -> (usize, usize) { |
862 | 0 | match alignment { |
863 | 0 | fmt::Alignment::Left => (available, 0), |
864 | 0 | fmt::Alignment::Right => (0, available), |
865 | | fmt::Alignment::Center => { |
866 | 0 | let left = available / 2; |
867 | 0 | let right = available - left; |
868 | 0 | (left, right) |
869 | | } |
870 | | } |
871 | 0 | } |
872 | | |
873 | 0 | fn configure_grid() -> SpannedConfig { |
874 | 0 | let mut cfg = SpannedConfig::default(); |
875 | 0 | cfg.set_padding( |
876 | 0 | Entity::Global, |
877 | 0 | Sides::new( |
878 | 0 | Indent::spaced(1), |
879 | 0 | Indent::spaced(1), |
880 | 0 | Indent::default(), |
881 | 0 | Indent::default(), |
882 | | ), |
883 | | ); |
884 | 0 | cfg.set_alignment_horizontal(Entity::Global, AlignmentHorizontal::Left); |
885 | 0 | cfg.set_line_alignment(Entity::Global, false); |
886 | 0 | cfg.set_trim_horizontal(Entity::Global, false); |
887 | 0 | cfg.set_trim_vertical(Entity::Global, false); |
888 | 0 | cfg.set_borders(Style::ascii().get_borders()); |
889 | | |
890 | 0 | cfg |
891 | 0 | } |
892 | | |
893 | 0 | fn use_format_configuration<'a>( |
894 | 0 | f: &mut fmt::Formatter<'_>, |
895 | 0 | table: &'a Table, |
896 | 0 | ) -> Cow<'a, SpannedConfig> { |
897 | 0 | if f.align().is_some() || f.width().is_some() { |
898 | 0 | let mut cfg = table.config.as_ref().clone(); |
899 | | |
900 | 0 | set_align_table(f, &mut cfg); |
901 | 0 | set_width_table(f, &mut cfg, table); |
902 | | |
903 | 0 | Cow::Owned(cfg) |
904 | | } else { |
905 | 0 | Cow::Borrowed(table.config.as_ref()) |
906 | | } |
907 | 0 | } |
908 | | |
909 | 0 | fn set_align_table(f: &fmt::Formatter<'_>, cfg: &mut SpannedConfig) { |
910 | 0 | if let Some(alignment) = f.align() { |
911 | 0 | let alignment = convert_fmt_alignment(alignment); |
912 | 0 | cfg.set_alignment_horizontal(Entity::Global, alignment); |
913 | 0 | } |
914 | 0 | } |
915 | | |
916 | 0 | fn set_width_table(f: &fmt::Formatter<'_>, cfg: &mut SpannedConfig, table: &Table) { |
917 | 0 | if let Some(width) = f.width() { |
918 | 0 | let total_width = table.total_width(); |
919 | 0 | if total_width >= width { |
920 | 0 | return; |
921 | 0 | } |
922 | | |
923 | 0 | let mut fill = f.fill(); |
924 | 0 | if fill == char::default() { |
925 | 0 | fill = ' '; |
926 | 0 | } |
927 | | |
928 | 0 | let available = width - total_width; |
929 | 0 | let alignment = f.align().unwrap_or(fmt::Alignment::Left); |
930 | 0 | let (left, right) = table_padding(alignment, available); |
931 | | |
932 | 0 | let mut margin = cfg.get_margin(); |
933 | 0 | margin.left.size += left; |
934 | 0 | margin.right.size += right; |
935 | | |
936 | 0 | if (margin.left.size > 0 && margin.left.fill == char::default()) || fill != char::default() |
937 | 0 | { |
938 | 0 | margin.left.fill = fill; |
939 | 0 | } |
940 | | |
941 | 0 | if (margin.right.size > 0 && margin.right.fill == char::default()) |
942 | 0 | || fill != char::default() |
943 | 0 | { |
944 | 0 | margin.right.fill = fill; |
945 | 0 | } |
946 | | |
947 | 0 | cfg.set_margin(margin); |
948 | 0 | } |
949 | 0 | } |
950 | | |
951 | 0 | fn print_grid<F: fmt::Write, D: Dimension>( |
952 | 0 | f: &mut F, |
953 | 0 | records: &VecRecords<Text<String>>, |
954 | 0 | cfg: &SpannedConfig, |
955 | 0 | dims: D, |
956 | 0 | colors: &ColorMap, |
957 | 0 | ) -> fmt::Result { |
958 | 0 | if !colors.is_empty() { |
959 | 0 | PeekableGrid::new(records, cfg, &dims, colors).build(f) |
960 | | } else { |
961 | 0 | PeekableGrid::new(records, cfg, &dims, NoColors).build(f) |
962 | | } |
963 | 0 | } Unexecuted instantiation: tabled::tables::table::print_grid::<core::fmt::Formatter, &papergrid::dimension::peekable::PeekableGridDimension> Unexecuted instantiation: tabled::tables::table::print_grid::<core::fmt::Formatter, &tabled::grid::dimension::complete_dimension::CompleteDimension> |