tabled/
lib.rs

1//! An easy to use library for pretty print tables of Rust `struct`s and `enum`s.
2//!
3//! The library is based on a [`Tabled`] trait which is used to actually build tables.
4//! It also provides an variate of dynamic settings for customization of a [`Table`].
5//!
6//! [`Table`] can be build from vast majority of Rust's standard types.
7//!
8//! ## Usage
9//!
10//! If you want to build a table for your custom type.
11//! A starting point is to a anotate your type with `#[derive(Tabled)]`.
12//!
13//! Then one of ways to create a table is to call [`Table::new`] to create a table.
14//!
15#![cfg_attr(feature = "derive", doc = "```")]
16#![cfg_attr(not(feature = "derive"), doc = "```ignore")]
17//! use tabled::{Tabled, Table};
18//!
19//! #[derive(Tabled)]
20//! struct Language {
21//!     name: &'static str,
22//!     designed_by: &'static str,
23//!     invented_year: usize,
24//! }
25//!
26//! let languages = vec![
27//!     Language{
28//!         name: "C",
29//!         designed_by: "Dennis Ritchie",
30//!         invented_year: 1972
31//!     },
32//!     Language{
33//!         name: "Rust",
34//!         designed_by: "Graydon Hoare",
35//!         invented_year: 2010
36//!     },
37//!     Language{
38//!         name: "Go",
39//!         designed_by: "Rob Pike",
40//!         invented_year: 2009
41//!     },
42//! ];
43//!
44//! let table = Table::new(languages).to_string();
45//!
46//! let expected = "+------+----------------+---------------+\n\
47//!                 | name | designed_by    | invented_year |\n\
48//!                 +------+----------------+---------------+\n\
49//!                 | C    | Dennis Ritchie | 1972          |\n\
50//!                 +------+----------------+---------------+\n\
51//!                 | Rust | Graydon Hoare  | 2010          |\n\
52//!                 +------+----------------+---------------+\n\
53//!                 | Go   | Rob Pike       | 2009          |\n\
54//!                 +------+----------------+---------------+";
55//!
56//! assert_eq!(table, expected);
57//! ```
58//!
59//! You can also create a table by using [`TableIteratorExt`].
60//!
61//! ```rust,no_run
62//! # let languages = [""];
63//! use tabled::TableIteratorExt;
64//! let table = languages.table();
65//! ```
66//!
67//! Not all types can derive [`Tabled`] trait though.
68//! The example below can't be compiled.
69//!
70//! ```rust,compile_fail
71//!   # use tabled::Tabled;
72//!     #[derive(Tabled)]
73//!     struct SomeType {
74//!         field1: SomeOtherType,
75//!     }
76//!
77//!     struct SomeOtherType;
78//! ```
79//!
80//! We must know what we're up to print as a field. Because of this
81//! each field must implement [`std::fmt::Display`].
82//!
83//! ### Default implementations
84//!
85//! As I've already mentioned most of the default types implements the trait out of the box.
86//!
87//! This allows you to run the following code.
88//!
89//! ```rust
90//! use tabled::{Tabled, Table};
91//! let table = Table::new(&[1, 2, 3]);
92//! # let expected = "+-----+\n\
93//! #                 | i32 |\n\
94//! #                 +-----+\n\
95//! #                 | 1   |\n\
96//! #                 +-----+\n\
97//! #                 | 2   |\n\
98//! #                 +-----+\n\
99//! #                 | 3   |\n\
100//! #                 +-----+";
101//! # assert_eq!(table.to_string(), expected);
102//! ```
103//!
104//! ### Combination of types via tuples
105//!
106//! Personally I consider this a feature which drives the library to shine.
107//! You can combine any types that implements [`Tabled`] trait into one table.
108//!
109//! You can also see in this example a `#[header("name")]` usage which configures a header
110//! of a table which will be printed.
111//! You could change it dynamically as well.
112//!
113#![cfg_attr(feature = "derive", doc = "```")]
114#![cfg_attr(not(feature = "derive"), doc = "```ignore")]
115//! use tabled::{Tabled, Table, Style, Alignment, ModifyObject, object::{Rows, Columns, Object}};
116//!
117//! #[derive(Tabled)]
118//! enum Domain {
119//!     Security,
120//!     Embeded,
121//!     Frontend,
122//!     Unknown,
123//! }
124//!
125//! #[derive(Tabled)]
126//! struct Developer(#[tabled(rename = "name")] &'static str);
127//!     
128//! let data = vec![
129//!     (Developer("Terri Kshlerin"), Domain::Embeded),
130//!     (Developer("Catalina Dicki"), Domain::Security),
131//!     (Developer("Jennie Schmeler"), Domain::Frontend),
132//!     (Developer("Maxim Zhiburt"), Domain::Unknown),
133//! ];
134//!     
135//! let table = Table::new(data)
136//!     .with(Style::psql())
137//!     .with(Rows::new(1..).not(Columns::first()).modify().with(Alignment::center()))
138//!     .to_string();
139//!
140//! assert_eq!(
141//!     table,
142//!     concat!(
143//!         " name            | Security | Embeded | Frontend | Unknown \n",
144//!         "-----------------+----------+---------+----------+---------\n",
145//!         " Terri Kshlerin  |          |    +    |          |         \n",
146//!         " Catalina Dicki  |    +     |         |          |         \n",
147//!         " Jennie Schmeler |          |         |    +     |         \n",
148//!         " Maxim Zhiburt   |          |         |          |    +    "
149//!     )
150//! );
151//! ```
152//!
153//! ### Dynamic table
154//!
155//! When you data sheme is not known at compile time.
156//! You mostlikely will not able to use [`Tabled`] trait.
157//! But you could build table from scratch.
158//!
159//! ```
160//! use tabled::{builder::Builder, ModifyObject, object::Rows, Alignment, Style};
161//!
162//! let mut builder = Builder::default();
163//!
164//! for i in 0..3 {
165//!     let mut row = vec![];
166//!     row.push(i.to_string());
167//!     for j in 0..10 {
168//!         row.push((i*j).to_string());
169//!     }
170//!
171//!     builder.add_record(row);
172//! }
173//!
174//! builder.set_columns(std::iter::once(String::from("i")).chain((0..10).map(|i| i.to_string())));
175//!
176//! let table = builder.build()
177//!     .with(Style::rounded())
178//!     .with(Rows::new(1..).modify().with(Alignment::left()))
179//!     .to_string();
180//!
181//! assert_eq!(
182//!     table,
183//!     concat!(
184//!         "╭───┬───┬───┬───┬───┬───┬────┬────┬────┬────┬────╮\n",
185//!         "│ i │ 0 │ 1 │ 2 │ 3 │ 4 │ 5  │ 6  │ 7  │ 8  │ 9  │\n",
186//!         "├───┼───┼───┼───┼───┼───┼────┼────┼────┼────┼────┤\n",
187//!         "│ 0 │ 0 │ 0 │ 0 │ 0 │ 0 │ 0  │ 0  │ 0  │ 0  │ 0  │\n",
188//!         "│ 1 │ 0 │ 1 │ 2 │ 3 │ 4 │ 5  │ 6  │ 7  │ 8  │ 9  │\n",
189//!         "│ 2 │ 0 │ 2 │ 4 │ 6 │ 8 │ 10 │ 12 │ 14 │ 16 │ 18 │\n",
190//!         "╰───┴───┴───┴───┴───┴───┴────┴────┴────┴────┴────╯",
191//!     )
192//! );
193//! ```
194//!
195//! ### Build table using [`row`] and [`col`].
196//!
197#![cfg_attr(feature = "macros", doc = "```")]
198#![cfg_attr(not(feature = "macros"), doc = "```ignore")]
199//! use tabled::{row, col};
200//!
201//! let table = row![
202//!     col!["Hello", "World", "!"],
203//!     col!["Hello"; 3],
204//!     col!["World"; 3],
205//! ].to_string();
206//!
207//! assert_eq!(
208//!     table,
209//!     concat!(
210//!         "+-----------+-----------+-----------+\n",
211//!         "| +-------+ | +-------+ | +-------+ |\n",
212//!         "| | Hello | | | Hello | | | World | |\n",
213//!         "| +-------+ | +-------+ | +-------+ |\n",
214//!         "| | World | | | Hello | | | World | |\n",
215//!         "| +-------+ | +-------+ | +-------+ |\n",
216//!         "| | !     | | | Hello | | | World | |\n",
217//!         "| +-------+ | +-------+ | +-------+ |\n",
218//!         "+-----------+-----------+-----------+",
219//!     )
220//! );
221//! ```
222//!
223//! ## Settings
224//!
225//! You can find more examples of settings and attributes in
226//! [README.md](https://github.com/zhiburt/tabled/blob/master/README.md)
227
228#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]
229#![doc(
230    html_logo_url = "https://raw.githubusercontent.com/zhiburt/tabled/86ac146e532ce9f7626608d7fd05072123603a2e/assets/tabled-gear.svg"
231)]
232#![warn(
233    missing_docs,
234    rust_2018_idioms,
235    missing_debug_implementations,
236    unreachable_pub
237)]
238#![deny(unused_must_use)]
239
240mod features;
241mod modify;
242mod table;
243mod table_iterator_ext;
244mod tabled;
245
246pub mod builder;
247pub mod display;
248pub mod object;
249
250#[cfg(feature = "macros")]
251#[cfg_attr(docsrs, doc(cfg(feature = "macros")))]
252pub mod macros;
253
254pub use papergrid;
255
256pub use crate::{
257    features::{
258        alignment::{self, Alignment},
259        concat::Concat,
260        disable::Disable,
261        extract::Extract,
262        format, formatting,
263        height::{self, Height},
264        highlight::Highlight,
265        locator,
266        margin::Margin,
267        measurment, merge,
268        padding::Padding,
269        panel::{Footer, Header, Panel},
270        peaker,
271        rotate::Rotate,
272        shadow,
273        span::Span,
274        style::{self, Border, BorderText, Style},
275        width::{self, Width},
276    },
277    modify::{CellSettingsList, Modify, ModifyList, ModifyObject},
278    table::{CellOption, Table, TableOption},
279    table_iterator_ext::TableIteratorExt,
280    tabled::Tabled,
281};
282
283#[cfg(feature = "color")]
284#[cfg_attr(docsrs, doc(cfg(feature = "color")))]
285pub use crate::features::{color, highlight, margin_color, padding_color};
286
287/// A derive to implement a [`Tabled`] trait.
288///
289/// The macros available only when `derive` feature in turned on (and it is by default).
290///
291/// To be able to use the derive each field must implement `std::fmt::Display`.
292/// The following example will cause a error because of that.
293///
294/// ```rust,compile_fail
295/// use tabled::Tabled;
296/// #[derive(Tabled)]
297/// struct SomeType {
298///     field1: SomeOtherType,
299/// }
300///
301/// struct SomeOtherType;
302/// ```
303///
304/// Bellow you'll find available options for it.
305///
306/// ### Override a column name
307///
308/// You can use a `#[tabled(rename = "")]` attribute to override a column name.
309///
310/// ```rust,no_run
311/// use tabled::Tabled;
312///
313/// #[derive(Tabled)]
314/// struct Person {
315///     #[tabled(rename = "Name")]
316///     first_name: &'static str,
317///     #[tabled(rename = "Surname")]
318///     last_name: &'static str,
319/// }
320/// ```
321///
322/// ### Hide a column
323///
324/// You can mark filds as hidden in which case they fill be ignored and not be present on a sheet.
325///
326/// A similar affect could be achieved by the means of a `Disable` setting.
327///
328/// ```rust,no_run
329/// use tabled::Tabled;
330///
331/// #[derive(Tabled)]
332/// struct Person {
333///    id: u8,
334///    #[tabled(skip)]
335///    number: &'static str,
336///    name: &'static str,
337/// }
338/// ```
339///
340/// ### Set column order
341///
342/// You can change the order in which they will be displayed in table.
343///
344/// ```rust,no_run
345/// use tabled::Tabled;
346///
347/// #[derive(Tabled)]
348/// struct Person {
349///    id: u8,
350///    #[tabled(order = 0)]
351///    number: &'static str,
352///    #[tabled(order = 1)]
353///    name: &'static str,
354/// }
355/// ```
356///
357/// ### Format fields
358///
359/// As was said already, using `#[derive(Tabled)]` is possible only when all fields implement a `Display` trait.
360/// However, this may be often not the case for example when a field uses the `Option` type. There's 2 common ways how to solve this:
361///
362/// - Implement `Tabled` trait manually for a type.
363/// - Wrap `Option` to something like `DisplayedOption<T>(Option<T>)` and implement a Display trait for it.
364///
365/// Alternatively, you can use the `#[tabled(display_with = "func")]` attribute for the field to specify a display function.
366///
367/// ```rust,no_run
368/// use tabled::Tabled;
369///
370/// #[derive(Tabled)]
371/// pub struct MyRecord {
372///     pub id: i64,
373///     #[tabled(display_with = "display_option")]
374///     pub valid: Option<bool>
375/// }
376///
377/// fn display_option(o: &Option<bool>) -> String {
378///     match o {
379///         Some(s) => format!("is valid thing = {}", s),
380///         None => format!("is not valid"),
381///     }
382/// }
383/// ```
384///
385/// It's also possible to change function argument to be `&self`,
386/// using `#[tabled(display_with("some_function", args))]`
387///
388/// ```rust,no_run
389/// use tabled::Tabled;
390///
391/// #[derive(Tabled)]
392/// pub struct MyRecord {
393///     pub id: i64,
394///     #[tabled(display_with("Self::display_valid", args))]
395///     pub valid: Option<bool>
396/// }
397///
398/// impl MyRecord {
399///     fn display_valid(&self) -> String {
400///         match self.valid {
401///             Some(s) => format!("is valid thing = {}", s),
402///             None => format!("is not valid"),
403///         }
404///     }
405/// }
406/// ```
407///
408/// ### Format headers
409///
410/// Beside `#[tabled(rename = "")]` you can change a format of a column name using
411/// `#[tabled(rename_all = "UPPERCASE")]`.
412///
413/// ```rust,no_run
414/// use tabled::Tabled;
415///
416/// #[derive(Tabled)]
417/// #[tabled(rename_all = "CamelCase")]
418/// struct Person {
419///     id: u8,
420///     number: &'static str,
421///     name: &'static str,
422///     #[tabled(rename_all = "snake_case")]
423///     middle_name: &'static str,
424/// }
425/// ```
426///
427/// ### Inline
428///
429/// It's possible to inline internal data if it implements the `Tabled` trait using `#[tabled(inline)]`.
430/// You can also set a prefix which will be used for all inlined elements by `#[tabled(inline("prefix>>"))]`.
431///
432/// ```rust,no_run
433/// use tabled::Tabled;
434///
435/// #[derive(Tabled)]
436/// struct Person {
437///     id: u8,
438///     name: &'static str,
439///     #[tabled(inline)]
440///     ed: Education,
441/// }
442///
443/// #[derive(Tabled)]
444/// struct Education {
445///     uni: &'static str,
446///     graduated: bool,
447/// }
448/// ```
449///
450/// And it works for enums as well.
451///
452/// ```rust,no_run
453/// use tabled::Tabled;
454///
455/// #[derive(Tabled)]
456/// enum Vehicle {
457///     #[tabled(inline("Auto::"))]
458///     Auto {
459///         model: &'static str,
460///         engine: &'static str,
461///     },
462///     #[tabled(inline)]
463///     Bikecycle(
464///         &'static str,
465///         #[tabled(inline)] Bike,
466///     ),
467/// }
468///
469/// #[derive(Tabled)]
470/// struct Bike {
471///     brand: &'static str,
472///     price: f32,
473/// }
474/// ```
475// @todo: Move the comment to tabled_derive
476#[cfg(feature = "derive")]
477#[cfg_attr(docsrs, doc(cfg(feature = "derive")))]
478pub use tabled_derive::Tabled;