Skip to content

Commit

Permalink
READY : format_tools: limit table width (#1494)
Browse files Browse the repository at this point in the history
* Remove slices

* Start moving

* Tests are passing

* Not ideal

* Fix test

* Fix algorithm and add tests

* Fix for Records

* Fix from the feedback

* Rework documentation and naming

* Fix docs

* Move out `text_wrap` from `table`

* Make `Records` use general `text_wrap`
  • Loading branch information
InAnYan authored Nov 22, 2024
1 parent d3bf267 commit ea86324
Show file tree
Hide file tree
Showing 9 changed files with 806 additions and 185 deletions.
4 changes: 4 additions & 0 deletions module/core/format_tools/src/format.rs
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,7 @@ pub mod string;
pub mod table;
pub mod to_string;
pub mod to_string_with_fallback;
pub mod text_wrap;

/// A strucutre for diagnostic and demonstration purpose.
#[ doc( hidden ) ]
Expand Down Expand Up @@ -317,6 +318,7 @@ pub mod own
table::orphan::*,
to_string::orphan::*,
to_string_with_fallback::orphan::*,
text_wrap::orphan::*,
};

}
Expand Down Expand Up @@ -369,6 +371,7 @@ pub mod exposed
table::exposed::*,
to_string::exposed::*,
to_string_with_fallback::exposed::*,
text_wrap::exposed::*,
};

}
Expand All @@ -391,6 +394,7 @@ pub mod prelude
table::prelude::*,
to_string::prelude::*,
to_string_with_fallback::prelude::*,
text_wrap::prelude::*,
};

}
122 changes: 82 additions & 40 deletions module/core/format_tools/src/format/output_format/records.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,12 @@
//!
use crate::*;
use md_math::MdOffset;
use print::
{
InputExtract,
Context,
};
use std::borrow::Cow;
use core::
{
fmt,
Expand Down Expand Up @@ -59,6 +59,8 @@ pub struct Records
pub cell_postfix : String,
/// Separator used between table columns.
pub cell_separator : String,
/// Limit table width. If the value is zero, then no limitation.
pub max_width: usize,
// /// Horizontal line character.
// pub h : char,
// /// Vertical line character.
Expand Down Expand Up @@ -91,6 +93,25 @@ impl Records
static INSTANCE : OnceLock< Records > = OnceLock::new();
INSTANCE.get_or_init( || Records::default() )
}

/// Calculate how much space is minimally needed in order to generate an output with this output formatter.
/// It will be impossible to render tables smaller than the result of `min_width()`.
///
/// This function is similar to `output_format::Table::min_width`, but it does not contain a
/// `column_count` as it always equal to 2, and it aslo uses the `output_format::Records`
/// style parameters.
pub fn min_width
(
&self,
) -> usize
{
// 2 is used here, because `Records` displays 2 columns: keys and values.
self.row_prefix.chars().count()
+ self.row_postfix.chars().count()
+ 2 * ( self.cell_postfix.chars().count() + self.cell_prefix.chars().count() )
+ self.cell_separator.chars().count()
+ 2
}
}

impl Default for Records
Expand All @@ -108,6 +129,8 @@ impl Default for Records
let table_postfix = "".to_string();
let table_separator = "\n".to_string();

let max_width = 0;

// let h = '─';
// let v = '|';
// let t_l = '├';
Expand All @@ -131,6 +154,7 @@ impl Default for Records
cell_prefix,
cell_postfix,
cell_separator,
max_width,
// h,
// v,
// t_l,
Expand All @@ -155,70 +179,88 @@ impl TableOutputFormat for Records
c : & mut Context< 'buf >,
) -> fmt::Result
{
use format::text_wrap::{ text_wrap, width_calculate };

if self.max_width != 0 && self.max_width < self.min_width()
{
return Err( fmt::Error );
}

// 2 because there are only 2 columns: key and value.
let columns_max_width = if self.max_width == 0 { 0 } else { self.max_width - self.min_width() + 2 };

let label_width = x.header().fold( 0, | acc, cell | acc.max( cell.1[ 0 ] ) );
let keys : Vec< ( Cow< 'data, str >, [ usize; 2 ] ) > = x.header().collect();
let keys_width = width_calculate( &keys );

write!( c.buf, "{}", self.table_prefix )?;

let mut first = true;
// Write each record
for ( irow, row ) in x.rows()
{
let mut printed_tables_count = 0;

if !row.vis
for ( itable_descriptor, table_descriptor ) in x.row_descriptors.iter().enumerate()
{
if !table_descriptor.vis || ( x.has_header && itable_descriptor == 0 )
{
continue;
}

if first
{
first = false;
}
else
if printed_tables_count > 0
{
write!( c.buf, "{}", self.table_separator )?;
}

let slice_width = x.data[ irow ].iter().fold( 0, | acc, cell | acc.max( cell.1[ 0 ] ) );
printed_tables_count += 1;

writeln!( c.buf, " = {}", table_descriptor.irow )?;

writeln!( c.buf, " = {}", irow )?;
let values = &x.data[ itable_descriptor ];
let values_width = width_calculate( &values );

for ( icol, _col ) in x.col_descriptors.iter().enumerate()
let table_for_wrapping : Vec< Vec< ( Cow< 'data, str >, [ usize; 2] ) > > =
keys.iter().enumerate().map( | ( ikey, key ) |
{
let cell = &x.data[ irow ][ icol ];
let height = cell.1[ 1 ];
vec![ key.clone(), values[ ikey ].clone() ]
}).collect();

for islice in 0..height
let wrapped_text = text_wrap
(
table_for_wrapping.iter(),
&[ keys_width, values_width ],
columns_max_width,
keys_width + values_width,
);

for ( irow, cols ) in wrapped_text.data.into_iter().enumerate()
{
if irow != 0
{
let label = x.header_slice( islice, icol );
let md_index = [ islice, icol, irow ];
let slice = x.slices[ x.slices_dim.md_offset( md_index ) ];

if icol > 0 || islice > 0
{
write!( c.buf, "{}", self.row_separator )?;
}

write!( c.buf, "{}", self.row_prefix )?;

write!( c.buf, "{}", self.cell_prefix )?;
write!( c.buf, "{:<label_width$}", label )?;
write!( c.buf, "{}", self.cell_postfix )?;
write!( c.buf, "{}", self.cell_separator )?;
write!( c.buf, "{}", self.cell_prefix )?;
write!( c.buf, "{:<slice_width$}", slice )?;
write!( c.buf, "{}", self.cell_postfix )?;

write!( c.buf, "{}", self.row_postfix )?;
write!( c.buf, "{}", self.row_separator )?;
}

}
let key = &cols[ 0 ];
let value = &cols[ 1 ];

let key_width = wrapped_text.column_widthes[ 0 ];
let value_width = wrapped_text.column_widthes[ 1 ];

write!( c.buf, "{}", self.row_prefix )?;

write!( c.buf, "{}", self.cell_prefix )?;
write!( c.buf, "{:<key_width$}", key.content )?;
write!( c.buf, "{}", self.cell_postfix )?;
write!( c.buf, "{}", self.cell_separator )?;
write!( c.buf, "{}", self.cell_prefix )?;
// No need to use `wrap_width` of `WrappedCell`, as `output_format::Records`
// does not center values in cells (they are always left aligned).
write!( c.buf, "{:<value_width$}", value.content )?;
write!( c.buf, "{}", self.cell_postfix )?;

write!( c.buf, "{}", self.row_postfix )?;
}
}

write!( c.buf, "{}", self.table_postfix )?;

Ok(())
Ok( () )
}

}
Loading

0 comments on commit ea86324

Please sign in to comment.