tabled/features/width/mod.rs
1//! This module contains object which can be used to limit a cell to a given width:
2//!
3//! - [`Truncate`] cuts a cell content to limit width.
4//! - [`Wrap`] split the content via new lines in order to fit max width.
5//! - [`Justify`] sets columns width to the same value.
6//!
7//! To set a a table width, a combination of [`Width::truncate`] or [`Width::wrap`] and [`Width::increase`] can be used.
8//!
9//! ## Example
10//!
11//! ```
12//! use tabled::{Width, Table};
13//!
14//! let table = Table::new(&["Hello World!"])
15//! .with(Width::wrap(7))
16//! .with(Width::increase(7))
17//! .to_string();
18//!
19//! assert_eq!(
20//! table,
21//! concat!(
22//! "+-----+\n",
23//! "| &st |\n",
24//! "| r |\n",
25//! "+-----+\n",
26//! "| Hel |\n",
27//! "| lo |\n",
28//! "| Wor |\n",
29//! "| ld! |\n",
30//! "+-----+",
31//! )
32//! );
33//! ```
34
35mod justify;
36mod min_width;
37mod truncate;
38mod width_list;
39mod wrap;
40
41use crate::measurment::Measurment;
42
43pub use self::{
44 justify::Justify,
45 min_width::MinWidth,
46 truncate::{SuffixLimit, Truncate},
47 width_list::WidthList,
48 wrap::Wrap,
49};
50
51use papergrid::{records::Records, width::WidthEstimator, Estimate, GridConfig};
52
53pub(crate) use wrap::wrap_text;
54
55/// Width allows you to set a min and max width of an object on a [`Table`]
56/// using different strategies.
57///
58/// It also allows you to set a min and max width for a whole table.
59///
60/// You can apply a min and max strategy at the same time with the same value,
61/// the value will be a total table width.
62///
63/// It is an abstract factory.
64///
65/// Beware that borders are not removed when you set a size value to very small.
66/// For example if you set size to 0 the table still be rendered but with all content removed.
67///
68/// Also be aware that it doesn't changes [`Padding`] settings nor it considers them.
69///
70/// The function is color aware if a `color` feature is on.
71///
72/// ## Examples
73///
74/// ### Cell change
75///
76/// ```
77/// use tabled::{object::Segment, Width, Modify, Style, Table};
78///
79/// let data = ["Hello", "World", "!"];
80///
81/// let table = Table::new(&data)
82/// .with(Style::markdown())
83/// .with(Modify::new(Segment::all()).with(Width::truncate(3).suffix("...")));
84/// ```
85///
86/// ### Table change
87///
88/// ```
89/// use tabled::{Width, Table};
90///
91/// let table = Table::new(&["Hello World!"]).with(Width::wrap(5));
92/// ```
93///
94/// ### Total width
95///
96/// ```
97/// use tabled::{Width, Table};
98///
99/// let table = Table::new(&["Hello World!"])
100/// .with(Width::wrap(5))
101/// .with(Width::increase(5));
102/// ```
103///
104/// [`Padding`]: crate::Padding
105/// [`Table`]: crate::Table
106#[derive(Debug)]
107pub struct Width;
108
109impl Width {
110 /// Returns a [`Wrap`] structure.
111 pub fn wrap<W>(width: W) -> Wrap<W>
112 where
113 W: Measurment<Width>,
114 {
115 Wrap::new(width)
116 }
117
118 /// Returns a [`Truncate`] structure.
119 pub fn truncate<W>(width: W) -> Truncate<'static, W>
120 where
121 W: Measurment<Width>,
122 {
123 Truncate::new(width)
124 }
125
126 /// Returns a [`MinWidth`] structure.
127 pub fn increase<W>(width: W) -> MinWidth<W>
128 where
129 W: Measurment<Width>,
130 {
131 MinWidth::new(width)
132 }
133
134 /// Returns a [`Justify`] structure.
135 pub fn justify<W>(width: W) -> Justify<W>
136 where
137 W: Measurment<Width>,
138 {
139 Justify::new(width)
140 }
141
142 /// Create [`WidthList`] to set a table width to a constant list of column widths.
143 ///
144 /// Notice if you provide a list with `.len()` smaller than `Table::count_columns` then it will have no affect.
145 ///
146 /// Also notice that you must provide values bigger than or equal to a real content width, otherwise it may panic.
147 ///
148 /// # Example
149 ///
150 /// ```
151 /// use tabled::{Table, Width};
152 ///
153 /// let data = vec![
154 /// ("Some\ndata", "here", "and here"),
155 /// ("Some\ndata on a next", "line", "right here"),
156 /// ];
157 ///
158 /// let table = Table::new(data)
159 /// .with(Width::list([20, 10, 12]))
160 /// .to_string();
161 ///
162 /// assert_eq!(
163 /// table,
164 /// "+--------------------+----------+------------+\n\
165 /// | &str | &str | &str |\n\
166 /// +--------------------+----------+------------+\n\
167 /// | Some | here | and here |\n\
168 /// | data | | |\n\
169 /// +--------------------+----------+------------+\n\
170 /// | Some | line | right here |\n\
171 /// | data on a next | | |\n\
172 /// +--------------------+----------+------------+"
173 /// )
174 /// ```
175 pub fn list<I>(rows: I) -> WidthList
176 where
177 I: IntoIterator<Item = usize>,
178 {
179 WidthList::new(rows.into_iter().collect())
180 }
181}
182
183pub(crate) fn get_table_widths<R>(records: R, cfg: &GridConfig) -> Vec<usize>
184where
185 R: Records,
186{
187 let mut evaluator = WidthEstimator::default();
188 evaluator.estimate(records, cfg);
189 evaluator.into()
190}
191
192pub(crate) fn count_borders(
193 cfg: &GridConfig,
194 start: usize,
195 end: usize,
196 count_columns: usize,
197) -> usize {
198 (start..end)
199 .skip(1)
200 .filter(|&i| cfg.has_vertical(i, count_columns))
201 .count()
202}
203
204pub(crate) fn get_table_total_width<W, R>(records: R, cfg: &GridConfig, ctrl: &W) -> usize
205where
206 W: Estimate<R>,
207 R: Records,
208{
209 ctrl.total()
210 + cfg.count_vertical(records.count_columns())
211 + cfg.get_margin().left.size
212 + cfg.get_margin().right.size
213}
214
215pub(crate) fn get_table_widths_with_total<R>(records: R, cfg: &GridConfig) -> (Vec<usize>, usize)
216where
217 R: Records,
218{
219 let mut evaluator = WidthEstimator::default();
220 evaluator.estimate(&records, cfg);
221 let total_width = get_table_total_width(&records, cfg, &evaluator);
222 let widths = evaluator.into();
223 (widths, total_width)
224}