mod private
{
use core ::fmt ::Write;
use error_tools ::untyped ::Result;
#[ derive( Debug ) ]
pub struct Table( Vec< Row > );
impl< T, R > From< T > for Table
where
T: IntoIterator< Item = R >,
R: Into< Row >,
{
fn from( value: T ) -> Self
{
Self( value.into_iter().map( Into ::into ).collect() )
}
}
impl Table
{
pub fn validate( &self ) -> bool
{
let mut row_iter = self.0.iter();
let Some( first_row ) = row_iter.next() else { return true };
let first_row_length = first_row.0.len();
for row in row_iter
{
if row.0.len() != first_row_length
{
return false;
}
}
true
}
}
#[ derive( Debug ) ]
pub struct Row( Vec< String > );
impl< R, V > From< R > for Row
where
R: IntoIterator< Item = V >,
V: Into< String >,
{
fn from( value: R ) -> Self
{
Self( value.into_iter().map( Into ::into ).collect() )
}
}
fn max_column_lengths( table: &Table ) -> Vec< usize >
{
let num_columns = table.0.first().map_or( 0, | row | row.0.len() );
( 0 .. num_columns )
.map( | column_index |
{
table.0.iter()
.map( | row | row.0[ column_index ].len() )
.max()
.unwrap_or( 0 )
})
.collect()
}
#[ derive( Debug, error_tools ::typed ::Error ) ]
#[ error( "Invalid table" ) ]
pub struct FormatTableError;
pub fn format_table< IntoTable >( table: IntoTable ) -> Result< String, FormatTableError >
where
IntoTable: Into< Table >,
{
let table = table.into();
if !table.validate()
{
return Err( FormatTableError );
}
let max_lengths = max_column_lengths( &table );
let mut formatted_table = String ::new();
for row in table.0
{
for ( i, cell ) in row.0.iter().enumerate()
{
write!( formatted_table, "{:width$}", cell, width = max_lengths[ i ] ).expect( "Writing to String shouldn't fail" );
formatted_table.push( ' ' );
}
formatted_table.pop(); formatted_table.push( '\n' );
}
formatted_table.pop();
Ok( formatted_table )
}
}
crate ::mod_interface!
{
own use format_table;
}