1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
//!
//! Text wrapping function.
//!
/// Define a private namespace for all its items.
mod private
{
use std::borrow::Cow;
use crate::*;
/// Struct that represents a wrapped tabular data. It is similar to `InputExtract`,
/// but we cannot use it as it does not wrap the text and it contains wrong column
/// widthes and heights (as they are dependent on wrapping too).
#[ derive( Debug ) ]
pub struct WrappedInputExtract< 'data >
{
/// Tabular data of rows and columns.
/// Note: these cells does not represent the actual information cells in the
/// original table. These cells are wrapped and used only for displaying. This also
/// means that one row in original table can be represented here with one or more
/// rows.
pub data: Vec< Vec< WrappedCell< 'data > > >,
/// New widthes of columns that include wrapping.
pub column_widthes : Vec< usize >,
/// Size of the first row of the table.
/// This parameter is used in case header of the table should be displayed.
pub first_row_height : usize,
}
/// Struct that represents a content of a wrapped cell.
/// It contains the slice of the cell as well as its original width.
///
/// Parameter `wrap_width` is needed as text in `output_format::Table` is centered.
/// However it is centered according to whole cell size and not the size of wrapped
/// text slice.
///
/// Example that depicts the importance of `wrap_width` parameter:
///
/// 1) | [ | 2) | [ |
/// | line1, | | line1, |
/// | line2 | | line2 |
/// | ] | | ] |
///
/// The first case seems to be properly formatted, while the second case took centering
/// too literally. That is why `wrap_width` is introduced, and additional spaces to the
/// right side should be included by the output formatter.
#[ derive( Debug ) ]
pub struct WrappedCell< 'data >
{
/// Width of the cell. In calculations use this width instead of slice length in order
/// to properly center the text. See example in the doc string of the parent struct.
pub wrap_width : usize,
/// Actual content of the cell.
pub content : Cow< 'data, str >
}
/// Wrap table cells.
///
/// `InputExtract` contains cells with full content, so it represents the logical
/// structure of the table. `WrappedInputExtract` wraps original cells to smaller
/// cells. The resulting data is more low-level and corresponds to the table that
/// will be actually printed to the console (or other output type).
///
/// `InputExtract` is not directly passed to this function, as it made to be general.
/// Instead you pass table cells in `data` argument and pass a vector of column widthes
/// in `columns_width_list` generated by `InputExtract`.
///
/// `columns_width_list` is a slice, this is more effective and general than just a `Vec`.
/// In table style, there could be many columns, but in records style there will be
/// always 2 columns - this number is known at compile time, so we can use a slice object.
///
/// Notice:
/// 1. Data passed to this function should contain only visible rows and columns.
/// It does not perform additional filtering.
/// 2. `data` parameters is **vector of rows of columns** (like and ordinary table).
/// This means that in styles like `Records` where headers and rows turned into columns
/// You have to transpose your data before passing it to this function.
///
/// Wrapping is controlled by `columns_max_width` and `columns_nowrap_width` parameters.
///
/// - `columns_max_width` is the size that is allowed to be occupied by columns.
/// It equals to maximum table width minus lengthes of visual elements (prefixes,
/// postfixes, separators, etc.).
///
/// - `columns_nowrap_width` is the sum of column widthes of cells without wrapping (basically,
/// the sum of widthes of column descriptors in `InputExtract`).
///
/// The function will perform wrapping and shrink the columns so that they occupy not
/// more than `columns_max_width`.
///
/// If `columns_max_width` is equal to 0, then no wrapping will be performed.
pub fn text_wrap< 'data >
(
data : impl Iterator< Item = &'data Vec< ( Cow< 'data, str >, [ usize; 2 ] ) > >,
columns_width_list : impl AsRef< [ usize ] >,
columns_max_width : usize,
columns_nowrap_width : usize,
)
-> WrappedInputExtract< 'data >
{
let columns_width_list = columns_width_list.as_ref();
let mut first_row_height = 0;
let mut new_data = Vec::new();
let mut column_widthes = Vec::new();
if columns_max_width == 0 || columns_max_width >= columns_nowrap_width
{
column_widthes.extend( columns_width_list.iter() );
}
else
{
let shrink_factor : f32 = ( columns_max_width as f32 ) / ( columns_nowrap_width as f32 );
for ( icol, col_width ) in columns_width_list.iter().enumerate()
{
let col_limit_float = ( *col_width as f32 ) * shrink_factor;
let col_limit = col_limit_float.floor() as usize;
let col_width_to_put = if icol == columns_width_list.len() - 1
{
columns_max_width - column_widthes.iter().sum::<usize>()
}
else
{
col_limit.max(1)
};
column_widthes.push( col_width_to_put );
}
}
for ( irow, row ) in data.enumerate()
{
let mut wrapped_rows : Vec< Vec< Cow< 'data, str > > > = vec![];
for ( icol, col ) in row.iter().enumerate()
{
let col_limit = column_widthes[ icol ];
let wrapped_col = string::lines_with_limit( col.0.as_ref(), col_limit ).map( Cow::from ).collect();
wrapped_rows.push( wrapped_col );
}
let max_rows = wrapped_rows.iter().map( Vec::len ).max().unwrap_or(0);
let mut transposed : Vec< Vec< WrappedCell< 'data > > > = Vec::new();
if max_rows == 0
{
transposed.push( vec![] );
}
for i in 0..max_rows
{
let mut row_vec : Vec< WrappedCell< 'data > > = Vec::new();
for col_lines in &wrapped_rows
{
if col_lines.len() > i
{
let wrap_width = col_lines.iter().map( |c| c.len() ).max().unwrap_or(0);
row_vec.push( WrappedCell { wrap_width , content : col_lines[ i ].clone() } );
}
else
{
row_vec.push( WrappedCell { wrap_width : 0, content : Cow::from( "" ) } );
}
}
transposed.push( row_vec );
}
if irow == 0
{
first_row_height += transposed.len();
}
new_data.extend( transposed );
}
WrappedInputExtract
{
data: new_data,
first_row_height,
column_widthes
}
}
/// Calculate width of the column without wrapping.
pub fn width_calculate< 'data >
(
column : &'data Vec< ( Cow< 'data, str >, [ usize; 2 ] ) >
)
-> usize
{
column.iter().map( |k|
{
string::lines( k.0.as_ref() ).map( |l| l.chars().count() ).max().unwrap_or( 0 )
} ).max().unwrap_or( 0 )
}
}
#[ allow( unused_imports ) ]
pub use own::*;
/// Own namespace of the module.
#[ allow( unused_imports ) ]
pub mod own
{
use super::*;
#[ doc( inline ) ]
pub use orphan::*;
#[ doc( inline ) ]
pub use
{
};
#[ doc( inline ) ]
pub use private::
{
text_wrap,
width_calculate,
};
}
/// Orphan namespace of the module.
#[ allow( unused_imports ) ]
pub mod orphan
{
use super::*;
#[ doc( inline ) ]
pub use exposed::*;
}
/// Exposed namespace of the module.
#[ allow( unused_imports ) ]
pub mod exposed
{
use super::*;
pub use super::super::output_format;
}
/// Prelude to use essentials: `use my_module::prelude::*`.
#[ allow( unused_imports ) ]
pub mod prelude
{
use super::*;
}