Skip to content

Commit 2b34f08

Browse files
committed
df: table: Collect byte slices for Cell element
Also pre-record the cell width as getting it back in the printing function would require some conversion back to a String.
1 parent 418bf93 commit 2b34f08

File tree

1 file changed

+111
-50
lines changed

1 file changed

+111
-50
lines changed

src/uu/df/src/table.rs

Lines changed: 111 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,43 @@ impl From<Filesystem> for Row {
192192
}
193193
}
194194

195+
/// A `Cell` in the table. We store raw `bytes` as the data (e.g. directory name
196+
/// may be non-Unicode). We also record the printed `width` for alignment purpose,
197+
/// as it is easier to compute on the original string.
198+
struct Cell {
199+
bytes: Vec<u8>,
200+
width: usize,
201+
}
202+
203+
impl Cell {
204+
/// Create a cell, knowing that s contains only 1-length chars
205+
fn from_ascii_string<T: AsRef<str>>(s: T) -> Cell {
206+
let s = s.as_ref();
207+
Cell {
208+
bytes: s.as_bytes().into(),
209+
width: s.len(),
210+
}
211+
}
212+
213+
/// Create a cell from an unknown origin string that may contain
214+
/// wide characters.
215+
fn from_string<T: AsRef<str>>(s: T) -> Cell {
216+
let s = s.as_ref();
217+
Cell {
218+
bytes: s.as_bytes().into(),
219+
width: UnicodeWidthStr::width(s),
220+
}
221+
}
222+
223+
/// Create a cell from an `OsString`
224+
fn from_os_string(os: &OsString) -> Cell {
225+
Cell {
226+
bytes: uucore::os_str_as_bytes(os).unwrap().to_vec(),
227+
width: UnicodeWidthStr::width(os.to_string_lossy().as_ref()),
228+
}
229+
}
230+
}
231+
195232
/// A formatter for [`Row`].
196233
///
197234
/// The `options` control how the information in the row gets formatted.
@@ -225,47 +262,50 @@ impl<'a> RowFormatter<'a> {
225262
/// Get a string giving the scaled version of the input number.
226263
///
227264
/// The scaling factor is defined in the `options` field.
228-
fn scaled_bytes(&self, size: u64) -> String {
229-
if let Some(h) = self.options.human_readable {
265+
fn scaled_bytes(&self, size: u64) -> Cell {
266+
let s = if let Some(h) = self.options.human_readable {
230267
to_magnitude_and_suffix(size.into(), SuffixType::HumanReadable(h))
231268
} else {
232269
let BlockSize::Bytes(d) = self.options.block_size;
233270
(size as f64 / d as f64).ceil().to_string()
234-
}
271+
};
272+
Cell::from_ascii_string(s)
235273
}
236274

237275
/// Get a string giving the scaled version of the input number.
238276
///
239277
/// The scaling factor is defined in the `options` field.
240-
fn scaled_inodes(&self, size: u128) -> String {
241-
if let Some(h) = self.options.human_readable {
278+
fn scaled_inodes(&self, size: u128) -> Cell {
279+
let s = if let Some(h) = self.options.human_readable {
242280
to_magnitude_and_suffix(size, SuffixType::HumanReadable(h))
243281
} else {
244282
size.to_string()
245-
}
283+
};
284+
Cell::from_ascii_string(s)
246285
}
247286

248287
/// Convert a float between 0 and 1 into a percentage string.
249288
///
250289
/// If `None`, return the string `"-"` instead.
251-
fn percentage(fraction: Option<f64>) -> String {
252-
match fraction {
290+
fn percentage(fraction: Option<f64>) -> Cell {
291+
let s = match fraction {
253292
None => "-".to_string(),
254293
Some(x) => format!("{:.0}%", (100.0 * x).ceil()),
255-
}
294+
};
295+
Cell::from_ascii_string(s)
256296
}
257297

258298
/// Returns formatted row data.
259-
fn get_values(&self) -> Vec<String> {
260-
let mut strings = Vec::new();
299+
fn get_cells(&self) -> Vec<Cell> {
300+
let mut cells = Vec::new();
261301

262302
for column in &self.options.columns {
263-
let string = match column {
303+
let cell = match column {
264304
Column::Source => {
265305
if self.is_total_row {
266-
get_message("df-total")
306+
Cell::from_string(get_message("df-total"))
267307
} else {
268-
self.row.fs_device.to_string()
308+
Cell::from_string(&self.row.fs_device)
269309
}
270310
}
271311
Column::Size => self.scaled_bytes(self.row.bytes),
@@ -275,9 +315,9 @@ impl<'a> RowFormatter<'a> {
275315

276316
Column::Target => {
277317
if self.is_total_row && !self.options.columns.contains(&Column::Source) {
278-
get_message("df-total")
318+
Cell::from_string(get_message("df-total"))
279319
} else {
280-
self.row.fs_mount.to_string_lossy().into_owned()
320+
Cell::from_os_string(&self.row.fs_mount)
281321
}
282322
}
283323
Column::Itotal => self.scaled_inodes(self.row.inodes),
@@ -288,17 +328,17 @@ impl<'a> RowFormatter<'a> {
288328
.row
289329
.file
290330
.as_ref()
291-
.map_or("-".into(), |s| s.to_string_lossy().into_owned()),
331+
.map_or(Cell::from_ascii_string("-"), Cell::from_os_string),
292332

293-
Column::Fstype => self.row.fs_type.to_string(),
333+
Column::Fstype => Cell::from_string(&self.row.fs_type),
294334
#[cfg(target_os = "macos")]
295335
Column::Capacity => Self::percentage(self.row.bytes_capacity),
296336
};
297337

298-
strings.push(string);
338+
cells.push(cell);
299339
}
300340

301-
strings
341+
cells
302342
}
303343
}
304344

@@ -375,7 +415,7 @@ impl Header {
375415
/// The output table.
376416
pub(crate) struct Table {
377417
alignments: Vec<Alignment>,
378-
rows: Vec<Vec<String>>,
418+
rows: Vec<Vec<Cell>>,
379419
widths: Vec<usize>,
380420
}
381421

@@ -389,7 +429,7 @@ impl Table {
389429
.map(|(i, col)| Column::min_width(col).max(headers[i].len()))
390430
.collect();
391431

392-
let mut rows = vec![headers];
432+
let mut rows = vec![headers.iter().map(Cell::from_string).collect()];
393433

394434
// The running total of filesystem sizes and usage.
395435
//
@@ -404,7 +444,7 @@ impl Table {
404444
if options.show_all_fs || filesystem.usage.blocks > 0 {
405445
let row = Row::from(filesystem);
406446
let fmt = RowFormatter::new(&row, options, false);
407-
let values = fmt.get_values();
447+
let values = fmt.get_cells();
408448
total += row;
409449

410450
rows.push(values);
@@ -413,15 +453,15 @@ impl Table {
413453

414454
if options.show_total {
415455
let total_row = RowFormatter::new(&total, options, true);
416-
rows.push(total_row.get_values());
456+
rows.push(total_row.get_cells());
417457
}
418458

419459
// extend the column widths (in chars) for long values in rows
420460
// do it here, after total row was added to the list of rows
421461
for row in &rows {
422462
for (i, value) in row.iter().enumerate() {
423-
if UnicodeWidthStr::width(value.as_str()) > widths[i] {
424-
widths[i] = UnicodeWidthStr::width(value.as_str());
463+
if value.width > widths[i] {
464+
widths[i] = value.width;
425465
}
426466
}
427467
}
@@ -450,6 +490,8 @@ impl fmt::Display for Table {
450490
while let Some(row) = row_iter.next() {
451491
let mut col_iter = row.iter().enumerate().peekable();
452492
while let Some((i, elem)) = col_iter.next() {
493+
// TODO: Fix this, and print the bytes directly.
494+
let elem = String::from_utf8(elem.bytes.clone()).unwrap_or("meh?".to_string());
453495
let is_last_col = col_iter.peek().is_none();
454496

455497
match self.alignments.get(i) {
@@ -490,7 +532,7 @@ mod tests {
490532

491533
use crate::blocks::HumanReadable;
492534
use crate::columns::Column;
493-
use crate::table::{Header, HeaderMode, Row, RowFormatter, Table};
535+
use crate::table::{Cell, Header, HeaderMode, Row, RowFormatter, Table};
494536
use crate::{BlockSize, Options};
495537

496538
fn init() {
@@ -674,6 +716,13 @@ mod tests {
674716
);
675717
}
676718

719+
fn compare_cell_content(cells: Vec<Cell>, expected: Vec<&str>) -> bool {
720+
cells
721+
.into_iter()
722+
.zip(expected)
723+
.all(|(c, s)| c.bytes == s.as_bytes())
724+
}
725+
677726
#[test]
678727
fn test_row_formatter() {
679728
init();
@@ -693,10 +742,10 @@ mod tests {
693742
..Default::default()
694743
};
695744
let fmt = RowFormatter::new(&row, &options, false);
696-
assert_eq!(
697-
fmt.get_values(),
745+
assert!(compare_cell_content(
746+
fmt.get_cells(),
698747
vec!("my_device", "100", "25", "75", "25%", "my_mount")
699-
);
748+
));
700749
}
701750

702751
#[test]
@@ -720,10 +769,10 @@ mod tests {
720769
..Default::default()
721770
};
722771
let fmt = RowFormatter::new(&row, &options, false);
723-
assert_eq!(
724-
fmt.get_values(),
772+
assert!(compare_cell_content(
773+
fmt.get_cells(),
725774
vec!("my_device", "my_type", "100", "25", "75", "25%", "my_mount")
726-
);
775+
));
727776
}
728777

729778
#[test]
@@ -746,10 +795,10 @@ mod tests {
746795
..Default::default()
747796
};
748797
let fmt = RowFormatter::new(&row, &options, false);
749-
assert_eq!(
750-
fmt.get_values(),
798+
assert!(compare_cell_content(
799+
fmt.get_cells(),
751800
vec!("my_device", "10", "2", "8", "20%", "my_mount")
752-
);
801+
));
753802
}
754803

755804
#[test]
@@ -766,7 +815,7 @@ mod tests {
766815
..Default::default()
767816
};
768817
let fmt = RowFormatter::new(&row, &options, false);
769-
assert_eq!(fmt.get_values(), vec!("1", "10"));
818+
assert!(compare_cell_content(fmt.get_cells(), vec!("1", "10")));
770819
}
771820

772821
#[test]
@@ -790,10 +839,10 @@ mod tests {
790839
..Default::default()
791840
};
792841
let fmt = RowFormatter::new(&row, &options, false);
793-
assert_eq!(
794-
fmt.get_values(),
842+
assert!(compare_cell_content(
843+
fmt.get_cells(),
795844
vec!("my_device", "my_type", "4k", "1k", "3k", "25%", "my_mount")
796-
);
845+
));
797846
}
798847

799848
#[test]
@@ -817,10 +866,10 @@ mod tests {
817866
..Default::default()
818867
};
819868
let fmt = RowFormatter::new(&row, &options, false);
820-
assert_eq!(
821-
fmt.get_values(),
869+
assert!(compare_cell_content(
870+
fmt.get_cells(),
822871
vec!("my_device", "my_type", "4K", "1K", "3K", "25%", "my_mount")
823-
);
872+
));
824873
}
825874

826875
#[test]
@@ -835,13 +884,13 @@ mod tests {
835884
..Default::default()
836885
};
837886
let fmt = RowFormatter::new(&row, &options, false);
838-
assert_eq!(fmt.get_values(), vec!("26%"));
887+
assert!(compare_cell_content(fmt.get_cells(), vec!("26%")));
839888
}
840889

841890
#[test]
842891
fn test_row_formatter_with_round_up_byte_values() {
843892
init();
844-
fn get_formatted_values(bytes: u64, bytes_used: u64, bytes_avail: u64) -> Vec<String> {
893+
fn get_formatted_values(bytes: u64, bytes_used: u64, bytes_avail: u64) -> Vec<Cell> {
845894
let options = Options {
846895
block_size: BlockSize::Bytes(1000),
847896
columns: vec![Column::Size, Column::Used, Column::Avail],
@@ -854,13 +903,25 @@ mod tests {
854903
bytes_avail,
855904
..Default::default()
856905
};
857-
RowFormatter::new(&row, &options, false).get_values()
906+
RowFormatter::new(&row, &options, false).get_cells()
858907
}
859908

860-
assert_eq!(get_formatted_values(100, 100, 0), vec!("1", "1", "0"));
861-
assert_eq!(get_formatted_values(100, 99, 1), vec!("1", "1", "1"));
862-
assert_eq!(get_formatted_values(1000, 1000, 0), vec!("1", "1", "0"));
863-
assert_eq!(get_formatted_values(1001, 1000, 1), vec!("2", "1", "1"));
909+
assert!(compare_cell_content(
910+
get_formatted_values(100, 100, 0),
911+
vec!("1", "1", "0")
912+
));
913+
assert!(compare_cell_content(
914+
get_formatted_values(100, 99, 1),
915+
vec!("1", "1", "1")
916+
));
917+
assert!(compare_cell_content(
918+
get_formatted_values(1000, 1000, 0),
919+
vec!("1", "1", "0")
920+
));
921+
assert!(compare_cell_content(
922+
get_formatted_values(1001, 1000, 1),
923+
vec!("2", "1", "1")
924+
));
864925
}
865926

866927
#[test]

0 commit comments

Comments
 (0)