@@ -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.
376416pub ( 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