1//! An easy to use library for pretty print tables of Rust `struct`s and `enum`s.
2//!
3//! The library supports different approaches of table building.
4//! You can use [`Tabled`] trait if the data type is known.
5//! Or you can use [`Builder`] to construct the table from scratch.
6//!
7//! ## Usage
8//!
9//! If you want to build a table for your custom type.
10//! A starting point is to a anotate your type with `#[derive(Tabled)]`.
11//!
12//! Then to provide your collection to [`Table::new`] and you will be set to render table.
13//!
14#![cfg_attr(all(feature = "derive", feature = "std"), doc = "```")]
15#![cfg_attr(not(all(feature = "derive", feature = "std")), doc = "```ignore")]
16//! use tabled::{Tabled, Table};
17//!
18//! #[derive(Tabled)]
19//! struct Language {
20//! name: &'static str,
21//! designed_by: &'static str,
22//! invented_year: usize,
23//! }
24//!
25//! let languages = vec![
26//! Language{
27//! name: "C",
28//! designed_by: "Dennis Ritchie",
29//! invented_year: 1972
30//! },
31//! Language{
32//! name: "Rust",
33//! designed_by: "Graydon Hoare",
34//! invented_year: 2010
35//! },
36//! Language{
37//! name: "Go",
38//! designed_by: "Rob Pike",
39//! invented_year: 2009
40//! },
41//! ];
42//!
43//! let table = Table::new(languages).to_string();
44//!
45//! let expected = "+------+----------------+---------------+\n\
46//! | name | designed_by | invented_year |\n\
47//! +------+----------------+---------------+\n\
48//! | C | Dennis Ritchie | 1972 |\n\
49//! +------+----------------+---------------+\n\
50//! | Rust | Graydon Hoare | 2010 |\n\
51//! +------+----------------+---------------+\n\
52//! | Go | Rob Pike | 2009 |\n\
53//! +------+----------------+---------------+";
54//!
55//! assert_eq!(table, expected);
56//! ```
57//!
58//! Not all types can derive [`Tabled`] trait though.
59//! The example below can't be compiled.
60//!
61//! ```rust,compile_fail
62//! # use tabled::Tabled;
63//! #[derive(Tabled)]
64//! struct SomeType {
65//! field1: SomeOtherType,
66//! }
67//!
68//! struct SomeOtherType;
69//! ```
70//!
71//! Because `tabled` must know what we're up to print as a field, so
72//! each (almoust) field must implement [`std::fmt::Display`].
73//!
74//! ### Default implementations
75//!
76//! [`Table`] can be build from vast majority of Rust's standard types.
77//! This allows you to run the following code.
78//!
79#![cfg_attr(feature = "std", doc = "```")]
80#![cfg_attr(not(feature = "std"), doc = "```ignore")]
81//! use tabled::{Tabled, Table};
82//! let table = Table::new(&[1, 2, 3]);
83//! # let expected = "+-----+\n\
84//! # | i32 |\n\
85//! # +-----+\n\
86//! # | 1 |\n\
87//! # +-----+\n\
88//! # | 2 |\n\
89//! # +-----+\n\
90//! # | 3 |\n\
91//! # +-----+";
92//! # assert_eq!(table.to_string(), expected);
93//! ```
94//!
95//! ### Dynamic table
96//!
97//! When you data scheme is not known at compile time.
98//! You most likely will not able to relay on [`Tabled`] trait.
99//!
100//! So one option would be is to use [`Builder`].
101//!
102#![cfg_attr(feature = "std", doc = "```")]
103#![cfg_attr(not(feature = "std"), doc = "```ignore")]
104//! use std::iter;
105//!
106//! use tabled::{
107//! builder::Builder,
108//! settings::{Modify, object::Rows, Alignment, Style}
109//! };
110//!
111//! let (x, y) = (3, 10);
112//!
113//! let mut builder = Builder::default();
114//!
115//! let header = iter::once(String::from("i"))
116//! .chain((0..y)
117//! .map(|i| i.to_string()));
118//! builder.set_header(header);
119//!
120//! for i in 0..x {
121//! let row = iter::once(i)
122//! .chain((0..y).map(|j| i * j))
123//! .map(|i| i.to_string());
124//! builder.push_record(row);
125//! }
126//!
127//! let table = builder.build()
128//! .with(Style::rounded())
129//! .with(Modify::new(Rows::new(1..)).with(Alignment::left()))
130//! .to_string();
131//!
132//! assert_eq!(
133//! table,
134//! concat!(
135//! "╭───┬───┬───┬───┬───┬───┬────┬────┬────┬────┬────╮\n",
136//! "│ i │ 0 │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │\n",
137//! "├───┼───┼───┼───┼───┼───┼────┼────┼────┼────┼────┤\n",
138//! "│ 0 │ 0 │ 0 │ 0 │ 0 │ 0 │ 0 │ 0 │ 0 │ 0 │ 0 │\n",
139//! "│ 1 │ 0 │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │\n",
140//! "│ 2 │ 0 │ 2 │ 4 │ 6 │ 8 │ 10 │ 12 │ 14 │ 16 │ 18 │\n",
141//! "╰───┴───┴───┴───┴───┴───┴────┴────┴────┴────┴────╯",
142//! )
143//! );
144//! ```
145//!
146//! ### Build table using [`row!`] and [`col!`] macros.
147//!
148#![cfg_attr(all(feature = "macros", feature = "std"), doc = "```")]
149#![cfg_attr(not(all(feature = "macros", feature = "std")), doc = "```ignore")]
150//! use tabled::{row, col};
151//!
152//! let table = row![
153//! col!["Hello", "World", "!"],
154//! col!["Hello"; 3],
155//! col!["World"; 3],
156//! ];
157//!
158//! assert_eq!(
159//! table.to_string(),
160//! concat!(
161//! "+-----------+-----------+-----------+\n",
162//! "| +-------+ | +-------+ | +-------+ |\n",
163//! "| | Hello | | | Hello | | | World | |\n",
164//! "| +-------+ | +-------+ | +-------+ |\n",
165//! "| | World | | | Hello | | | World | |\n",
166//! "| +-------+ | +-------+ | +-------+ |\n",
167//! "| | ! | | | Hello | | | World | |\n",
168//! "| +-------+ | +-------+ | +-------+ |\n",
169//! "+-----------+-----------+-----------+",
170//! )
171//! );
172//! ```
173//!
174//! ### Settings
175//!
176//! You can use many settings which is found in [`tabled::settings`] module.
177//!
178//! # Advanced
179//!
180//! ## Alloc
181//!
182//! [`Table`] keeps data buffered, which sometimes not ideal choise.
183//! For such reason there is [`IterTable`] and [`CompactTable`].
184//!
185//! ### Less allocations
186//!
187//! [`IterTable`] stands on a middle ground between [`Table`] and [`CompactTable`].
188//!
189//! It does allocate memory but in a much smaller chunks that a [`Table`] does.
190//! The benefit is that it can be used interchangebly with [`Table`].
191//!
192#![cfg_attr(feature = "std", doc = "```")]
193#![cfg_attr(not(feature = "std"), doc = "```ignore")]
194//! use tabled::tables::IterTable;
195//!
196//! let iterator = (0..3).map(|row| (0..4).map(move |col| format!("{}-{}", row, col)));
197//!
198//! let table = IterTable::new(iterator).to_string();
199//!
200//! assert_eq!(
201//! table,
202//! "+-----+-----+-----+-----+\n\
203//! | 0-0 | 0-1 | 0-2 | 0-3 |\n\
204//! +-----+-----+-----+-----+\n\
205//! | 1-0 | 1-1 | 1-2 | 1-3 |\n\
206//! +-----+-----+-----+-----+\n\
207//! | 2-0 | 2-1 | 2-2 | 2-3 |\n\
208//! +-----+-----+-----+-----+",
209//! );
210//! ```
211//!
212//! ## Alloc free (`#nostd`)
213//!
214//! [`CompactTable`] can be configured ('1) to not make any allocations.
215//! But the price is that the set of settings which can be applied to it is limited.
216//!
217//! It also can be printed directly to [`fmt::Write`] to not have any intermidiaries.
218//!
219//! '1. It does not make any allocations in case you provide it with `width` and `count_rows`.
220//!
221//! ```
222//! use tabled::{settings::Style, tables::CompactTable};
223//! use core::fmt::{Write, Result};
224//!
225//! struct StubWriter;
226//!
227//! impl Write for StubWriter {
228//! fn write_str(&mut self, _: &str) -> Result {
229//! Ok(())
230//! }
231//! }
232//!
233//! let data = [
234//! ["FreeBSD", "1993", "William and Lynne Jolitz", "?"],
235//! ["OpenBSD", "1995", "Theo de Raadt", ""],
236//! ["HardenedBSD", "2014", "Oliver Pinter and Shawn Webb", ""],
237//! ];
238//!
239//! let table = CompactTable::from(data).with(Style::psql());
240//!
241//! table.fmt(StubWriter);
242//! ```
243//!
244//! ## More information
245//!
246//! You can find more examples of settings and attributes in
247//! [README.md](https://github.com/zhiburt/tabled/blob/master/README.md)
248//!
249//! [`Builder`]: crate::builder::Builder
250//! [`IterTable`]: crate::tables::IterTable
251//! [`CompactTable`]: crate::tables::CompactTable
252//! [`fmt::Write`]: core::fmt::Write
253//! [`row!`]: crate::row
254//! [`col!`]: crate::col
255
256#![cfg_attr(not(any(feature = "std", test)), no_std)]
257#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]
258#![doc(
259 html_logo_url = "https://raw.githubusercontent.com/zhiburt/tabled/86ac146e532ce9f7626608d7fd05072123603a2e/assets/tabled-gear.svg"
260)]
261#![deny(unused_must_use)]
262#![warn(
263 missing_docs,
264 rust_2018_idioms,
265 rust_2018_compatibility,
266 missing_debug_implementations,
267 unreachable_pub,
268 future_incompatible,
269 single_use_lifetimes,
270 trivial_casts,
271 trivial_numeric_casts,
272 unused_extern_crates,
273 unused_import_braces,
274 unused_qualifications,
275 unused_results,
276 unused_variables,
277 variant_size_differences
278)]
279#![allow(clippy::uninlined_format_args)]
280
281#[cfg(feature = "std")]
282mod tabled;
283
284#[cfg(feature = "std")]
285#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
286pub mod builder;
287pub mod settings;
288pub mod tables;
289
290#[cfg(feature = "macros")]
291#[cfg_attr(docsrs, doc(cfg(feature = "macros")))]
292pub mod macros;
293
294pub mod grid;
295
296#[cfg(feature = "std")]
297#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
298pub use crate::{tabled::Tabled, tables::Table};
299
300/// A derive to implement a [`Tabled`] trait.
301///
302/// The macros available only when `derive` feature in turned on (and it is by default).
303///
304/// To be able to use the derive each field must implement `std::fmt::Display`.
305/// The following example will cause a error because of that.
306///
307/// ```rust,compile_fail
308/// use tabled::Tabled;
309/// #[derive(Tabled)]
310/// struct SomeType {
311/// field1: SomeOtherType,
312/// }
313///
314/// struct SomeOtherType;
315/// ```
316///
317/// Bellow you'll find available options for it.
318///
319/// ### Override a column name
320///
321/// You can use a `#[tabled(rename = "")]` attribute to override a column name.
322///
323/// ```rust,no_run
324/// use tabled::Tabled;
325///
326/// #[derive(Tabled)]
327/// struct Person {
328/// #[tabled(rename = "Name")]
329/// first_name: &'static str,
330/// #[tabled(rename = "Surname")]
331/// last_name: &'static str,
332/// }
333/// ```
334///
335/// ### Hide a column
336///
337/// You can mark fields as hidden in which case they fill be ignored and not be present on a sheet.
338///
339/// A similar affect could be achieved by the means of a `Disable` setting.
340///
341/// ```rust,no_run
342/// use tabled::Tabled;
343///
344/// #[derive(Tabled)]
345/// struct Person {
346/// id: u8,
347/// #[tabled(skip)]
348/// number: &'static str,
349/// name: &'static str,
350/// }
351/// ```
352///
353/// ### Set column order
354///
355/// You can change the order in which they will be displayed in table.
356///
357/// ```rust,no_run
358/// use tabled::Tabled;
359///
360/// #[derive(Tabled)]
361/// struct Person {
362/// id: u8,
363/// #[tabled(order = 0)]
364/// number: &'static str,
365/// #[tabled(order = 1)]
366/// name: &'static str,
367/// }
368/// ```
369///
370/// ### Format fields
371///
372/// As was said already, using `#[derive(Tabled)]` is possible only when all fields implement a `Display` trait.
373/// 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:
374///
375/// - Implement `Tabled` trait manually for a type.
376/// - Wrap `Option` to something like `DisplayedOption<T>(Option<T>)` and implement a Display trait for it.
377///
378/// Alternatively, you can use the `#[tabled(display_with = "func")]` attribute for the field to specify a display function.
379///
380/// ```rust,no_run
381/// use tabled::Tabled;
382///
383/// #[derive(Tabled)]
384/// pub struct MyRecord {
385/// pub id: i64,
386/// #[tabled(display_with = "display_option")]
387/// pub valid: Option<bool>
388/// }
389///
390/// fn display_option(o: &Option<bool>) -> String {
391/// match o {
392/// Some(s) => format!("is valid thing = {}", s),
393/// None => format!("is not valid"),
394/// }
395/// }
396/// ```
397///
398/// It's also possible to change function argument to be `&self`,
399/// using `#[tabled(display_with("some_function", self))]`
400///
401/// ```rust,no_run
402/// use tabled::Tabled;
403///
404/// #[derive(Tabled)]
405/// pub struct MyRecord {
406/// pub id: i64,
407/// #[tabled(display_with("Self::display_valid", self))]
408/// pub valid: Option<bool>
409/// }
410///
411/// impl MyRecord {
412/// fn display_valid(&self) -> String {
413/// match self.valid {
414/// Some(s) => format!("is valid thing = {}", s),
415/// None => format!("is not valid"),
416/// }
417/// }
418/// }
419/// ```
420///
421/// ### Format headers
422///
423/// Beside `#[tabled(rename = "")]` you can change a format of a column name using
424/// `#[tabled(rename_all = "UPPERCASE")]`.
425///
426/// ```rust,no_run
427/// use tabled::Tabled;
428///
429/// #[derive(Tabled)]
430/// #[tabled(rename_all = "CamelCase")]
431/// struct Person {
432/// id: u8,
433/// number: &'static str,
434/// name: &'static str,
435/// #[tabled(rename_all = "snake_case")]
436/// middle_name: &'static str,
437/// }
438/// ```
439///
440/// ### Inline
441///
442/// It's possible to inline internal data if it implements the `Tabled` trait using `#[tabled(inline)]`.
443/// You can also set a prefix which will be used for all inlined elements by `#[tabled(inline("prefix>>"))]`.
444///
445/// ```rust,no_run
446/// use tabled::Tabled;
447///
448/// #[derive(Tabled)]
449/// struct Person {
450/// id: u8,
451/// name: &'static str,
452/// #[tabled(inline)]
453/// ed: Education,
454/// }
455///
456/// #[derive(Tabled)]
457/// struct Education {
458/// uni: &'static str,
459/// graduated: bool,
460/// }
461/// ```
462///
463/// And it works for enums as well.
464///
465/// ```rust,no_run
466/// use tabled::Tabled;
467///
468/// #[derive(Tabled)]
469/// enum Vehicle {
470/// #[tabled(inline("Auto::"))]
471/// Auto {
472/// model: &'static str,
473/// engine: &'static str,
474/// },
475/// #[tabled(inline)]
476/// Bikecycle(
477/// &'static str,
478/// #[tabled(inline)] Bike,
479/// ),
480/// }
481///
482/// #[derive(Tabled)]
483/// struct Bike {
484/// brand: &'static str,
485/// price: f32,
486/// }
487/// ```
488#[cfg(feature = "derive")]
489#[cfg_attr(docsrs, doc(cfg(feature = "derive")))]
490pub use tabled_derive::Tabled;
491