From bceee5c74d8db871b56bf4fb15566188762f38dd Mon Sep 17 00:00:00 2001 From: InAnYan Date: Mon, 11 Nov 2024 16:58:16 +0200 Subject: [PATCH 1/9] First step --- .../src/format/output_format/table.rs | 23 ++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/module/core/format_tools/src/format/output_format/table.rs b/module/core/format_tools/src/format/output_format/table.rs index e96af36f20..59e69cfc3b 100644 --- a/module/core/format_tools/src/format/output_format/table.rs +++ b/module/core/format_tools/src/format/output_format/table.rs @@ -75,6 +75,8 @@ pub struct Table pub corner_lb : char, /// Bottom-right corner character. pub corner_rb : char, + /// Limit table size (0 - no limit). + pub max_width : usize, } impl Default for Table @@ -103,6 +105,8 @@ impl Default for Table let corner_lb = '└'; let corner_rb = '┘'; + let max_width = 50; + Self { delimitting_header, @@ -123,6 +127,7 @@ impl Default for Table corner_rt, corner_lb, corner_rb, + max_width, } } } @@ -192,6 +197,17 @@ impl TableOutputFormat for Table // dbg!( x.row_descriptors.len() ); + let table_width: usize = x.col_descriptors.iter().map( |col| col.width ).sum(); + + let shrink_factor = if self.max_width == 0 || table_width <= self.max_width + { + 1.0 + } + else + { + ( self.max_width as f32 ) / ( table_width as f32 ) + }; + for ( irow, row ) in x.row_descriptors.iter().enumerate() { let height = row.height; @@ -203,7 +219,8 @@ impl TableOutputFormat for Table if prev_typ == LineType::Header && row.typ == LineType::Regular { write!( c.buf, "{}", row_separator )?; - write!( c.buf, "{}", h.repeat( row_width ) )?; + let new_row_width = ( ( row_width as f32 ) * shrink_factor ).floor() as usize; + write!( c.buf, "{}", h.repeat( new_row_width ) )?; delimitting_header = false } } @@ -234,7 +251,7 @@ impl TableOutputFormat for Table { let col = &x.col_descriptors[ icol ]; let cell_width = x.data[ irow ][ icol ].1[0]; - let width = col.width; + let width = ( ( col.width as f32 ) * shrink_factor ).floor() as usize; let md_index = [ islice, icol, irow as usize ]; let slice = x.slices[ x.slices_dim.md_offset( md_index ) ]; @@ -247,7 +264,7 @@ impl TableOutputFormat for Table write!( c.buf, "{}", cell_prefix )?; - println!( "icol : {icol} | irow : {irow} | width : {width} | cell_width : {cell_width} | slice.len() : {}", slice.len() ); + // println!( "icol : {icol} | irow : {irow} | width : {width} | cell_width : {cell_width} | slice.len() : {}", slice.len() ); let lspaces = if cell_width > width { 0 From fe0e74eb95bf5d2fe1872a5c7184c03380bdf9b6 Mon Sep 17 00:00:00 2001 From: InAnYan Date: Wed, 13 Nov 2024 13:15:18 +0200 Subject: [PATCH 2/9] Refactor InputExtract --- module/core/format_tools/src/format/filter.rs | 58 +--- .../src/format/output_format/keys.rs | 14 +- .../src/format/output_format/records.rs | 64 ++--- .../src/format/output_format/table.rs | 147 ++++------ module/core/format_tools/src/format/print.rs | 272 ++---------------- .../tests/inc/format_records_test.rs | 4 +- .../tests/inc/format_table_test.rs | 4 +- .../tests/inc/to_string_with_fallback_test.rs | 2 +- module/move/assistant/src/bin/main.rs | 2 +- 9 files changed, 120 insertions(+), 447 deletions(-) diff --git a/module/core/format_tools/src/format/filter.rs b/module/core/format_tools/src/format/filter.rs index 1551721570..6430ee9f07 100644 --- a/module/core/format_tools/src/format/filter.rs +++ b/module/core/format_tools/src/format/filter.rs @@ -13,21 +13,6 @@ mod private borrow::Cow, }; - /// Represents a line type in a table, either a header or a regular row. - /// - /// `LineType` is used to distinguish between different types of lines - /// in a table structure, aiding in formatting and processing. - /// - #[ derive( Debug, Default, PartialEq, Eq, Copy, Clone ) ] - pub enum LineType - { - /// Represents a regular row of data in the table. - #[ default ] - Regular, - /// Represents a header line in the table. - Header, - } - // = filters /// Filter passing all elements. @@ -45,11 +30,6 @@ mod private { /// Filter columns of a table to print it only partially. fn filter_col( &self, key : &str ) -> bool; - /// Determine is arguments needed for the filter or it can give answer even without arguments. Useful for optimization. - fn need_args( &self ) -> bool - { - true - } } impl Default for &'static dyn FilterCol @@ -78,11 +58,6 @@ mod private { true } - #[ inline( always ) ] - fn need_args( &self ) -> bool - { - false - } } impl None @@ -102,11 +77,6 @@ mod private { false } - #[ inline( always ) ] - fn need_args( &self ) -> bool - { - false - } } impl< F : Fn( &str ) -> bool > FilterCol for F @@ -124,12 +94,7 @@ mod private pub trait FilterRow { /// Filter rows of a table to print it only partially. - fn filter_row( &self, typ : LineType, irow : usize, row : &[ ( Cow< '_, str >, [ usize ; 2 ] ) ] ) -> bool; - /// Determine is arguments needed for the filter or it can give answer even without arguments. Useful for optimization. - fn need_args( &self ) -> bool - { - true - } + fn filter_row( &self, row : &[ Cow< '_, str > ] ) -> bool; } impl Default for &'static dyn FilterRow @@ -144,15 +109,10 @@ mod private impl FilterRow for All { #[ inline( always ) ] - fn filter_row( &self, _typ : LineType, _irow : usize, _row : &[ ( Cow< '_, str >, [ usize ; 2 ] ) ] ) -> bool + fn filter_row( &self, _row : &[ Cow< '_, str > ] ) -> bool { true } - #[ inline( always ) ] - fn need_args( &self ) -> bool - { - false - } } impl All @@ -168,12 +128,7 @@ mod private impl FilterRow for None { #[ inline( always ) ] - fn filter_row( &self, _typ : LineType, _irow : usize, _row : &[ ( Cow< '_, str >, [ usize ; 2 ] ) ] ) -> bool - { - false - } - #[ inline( always ) ] - fn need_args( &self ) -> bool + fn filter_row( &self, _row : &[ Cow< '_, str > ] ) -> bool { false } @@ -189,12 +144,12 @@ mod private } } - impl< F : Fn( LineType, usize, &[ ( Cow< '_, str >, [ usize ; 2 ] ) ] ) -> bool > FilterRow for F + impl< F : Fn( &[ Cow< '_, str > ] ) -> bool > FilterRow for F { #[ inline( always ) ] - fn filter_row( &self, typ : LineType, irow : usize, row : &[ ( Cow< '_, str >, [ usize ; 2 ] ) ] ) -> bool + fn filter_row( &self, row : &[ Cow< '_, str > ] ) -> bool { - self( typ, irow, row ) + self( row ) } } @@ -231,7 +186,6 @@ pub mod orphan #[ doc( inline ) ] pub use private:: { - LineType, FilterCol, FilterRow, }; diff --git a/module/core/format_tools/src/format/output_format/keys.rs b/module/core/format_tools/src/format/output_format/keys.rs index 55ee27b023..c88f8526b4 100644 --- a/module/core/format_tools/src/format/output_format/keys.rs +++ b/module/core/format_tools/src/format/output_format/keys.rs @@ -92,14 +92,18 @@ impl TableOutputFormat for Keys ) -> fmt::Result { - // dbg!( &x ); - - for col in &x.col_descriptors + if x.has_header && x.data.len() != 0 { - write!( c.buf, " - {}\n", col.label )?; + for col in &x.data[0] + { + write!( c.buf, " - {}\n", col )?; + } } - write!( c.buf, " {} fields\n", x.col_descriptors.len() )?; + if x.data.len() != 0 + { + write!( c.buf, " {} fields\n", x.data[0].len() )?; + } Ok(()) } diff --git a/module/core/format_tools/src/format/output_format/records.rs b/module/core/format_tools/src/format/output_format/records.rs index 45a1206e41..efc944c5d8 100644 --- a/module/core/format_tools/src/format/output_format/records.rs +++ b/module/core/format_tools/src/format/output_format/records.rs @@ -22,12 +22,12 @@ //! use crate::*; -use md_math::MdOffset; use print:: { InputExtract, Context, }; +use std::borrow::{ Cow, Borrow }; use core:: { fmt, @@ -156,20 +156,15 @@ impl TableOutputFormat for Records ) -> fmt::Result { - let label_width = x.header().fold( 0, | acc, cell | acc.max( cell.1[ 0 ] ) ); + let col_names : Vec< Cow< 'data, str > > = x.header().collect(); + let key_width = x.header().fold( 0, | acc, cell | acc.max( cell.len() ) ); write!( c.buf, "{}", self.table_prefix )?; let mut first = true; - // Write each record - for ( irow, row ) in x.rows() - { - - if !row.vis - { - continue; - } + for ( irow, row ) in x.rows().enumerate() + { if first { first = false; @@ -179,46 +174,31 @@ impl TableOutputFormat for Records write!( c.buf, "{}", self.table_separator )?; } - let slice_width = x.data[ irow ].iter().fold( 0, | acc, cell | acc.max( cell.1[ 0 ] ) ); + writeln!( c.buf, " = {}", irow + 1 )?; - writeln!( c.buf, " = {}", irow )?; + let value_width = row.iter().fold( 0, | acc, cell | acc.max( cell.len() ) ); - for ( icol, _col ) in x.col_descriptors.iter().enumerate() + for ( icol, col ) in row.iter().enumerate() { - let cell = &x.data[ irow ][ icol ]; - let height = cell.1[ 1 ]; - - for islice in 0..height - { - 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, "{:( &self, x : &InputExtract< 'data >, c : &mut Context< 'buf > ) -> fmt::Result { - use md_math::MdOffset; - let cell_prefix = &self.cell_prefix; let cell_postfix = &self.cell_postfix; let cell_separator = &self.cell_separator; @@ -178,124 +176,85 @@ impl TableOutputFormat for Table let row_separator = &self.row_separator; let h = self.h.to_string(); - let mut delimitting_header = self.delimitting_header; - let row_width = if delimitting_header + let column_count = x.header().count(); + + let mut col_width : Vec< usize > = vec![ 0; column_count ]; + + for ( _, row ) in x.data.iter().enumerate() { - let mut grid_width = x.mcells_vis[ 0 ] * ( cell_prefix.chars().count() + cell_postfix.chars().count() ); - grid_width += row_prefix.chars().count() + row_postfix.chars().count(); - if x.mcells_vis[ 0 ] > 0 + for ( icol, col ) in row.iter().enumerate() { - grid_width += ( x.mcells_vis[ 0 ] - 1 ) * ( cell_separator.chars().count() ); + col_width[ icol ] = col_width[ icol ].max( col.chars().count() ); } - x.mchars[ 0 ] + grid_width } - else - { - 0 - }; - let mut prev_typ : Option< LineType > = None; - // dbg!( x.row_descriptors.len() ); - - let table_width: usize = x.col_descriptors.iter().map( |col| col.width ).sum(); - - let shrink_factor = if self.max_width == 0 || table_width <= self.max_width - { - 1.0 - } - else - { - ( self.max_width as f32 ) / ( table_width as f32 ) - }; + let max_row_width = col_width.iter().sum::() + + self.row_prefix.chars().count() + + self.row_postfix.chars().count() + + column_count * ( self.cell_postfix.chars().count() + self.cell_prefix.chars().count() ) + + if column_count == 0 { 0 } else { ( column_count - 1 ) * self.cell_separator.chars().count() }; - for ( irow, row ) in x.row_descriptors.iter().enumerate() + for ( irow, row ) in x.data.iter().enumerate() { - let height = row.height; - - if delimitting_header + if irow == 1 && x.has_header && self.delimitting_header { - if let Some( prev_typ ) = prev_typ - { - if prev_typ == LineType::Header && row.typ == LineType::Regular - { - write!( c.buf, "{}", row_separator )?; - let new_row_width = ( ( row_width as f32 ) * shrink_factor ).floor() as usize; - write!( c.buf, "{}", h.repeat( new_row_width ) )?; - delimitting_header = false - } - } - if row.vis - { - prev_typ = Some( row.typ ); - } + write!( c.buf, "{}", row_separator )?; + write!( c.buf, "{}", h.repeat( max_row_width ) )?; } - - if !row.vis + + if irow > 0 { - continue; + write!( c.buf, "{}", row_separator )?; } - // dbg!( row.height ); + write!( c.buf, "{}", row_prefix )?; - for islice in 0..height + for ( icol, col ) in row.iter().enumerate() { + let cell_width = col_width[ icol ]; + let width = col.len(); - if irow > 0 + if icol > 0 { - write!( c.buf, "{}", row_separator )?; + write!( c.buf, "{}", cell_separator )?; } - write!( c.buf, "{}", row_prefix )?; - - for icol in 0 .. x.col_descriptors.len() + write!( c.buf, "{}", cell_prefix )?; + + let lspaces = if cell_width >= width { - let col = &x.col_descriptors[ icol ]; - let cell_width = x.data[ irow ][ icol ].1[0]; - let width = ( ( col.width as f32 ) * shrink_factor ).floor() as usize; - let md_index = [ islice, icol, irow as usize ]; - let slice = x.slices[ x.slices_dim.md_offset( md_index ) ]; - - // println!( "md_index : {md_index:?} | md_offset : {} | slice : {slice}", x.slices_dim.md_offset( md_index ) ); - - if icol > 0 - { - write!( c.buf, "{}", cell_separator )?; - } - - write!( c.buf, "{}", cell_prefix )?; - - // println!( "icol : {icol} | irow : {irow} | width : {width} | cell_width : {cell_width} | slice.len() : {}", slice.len() ); + ( cell_width - width ) / 2 + } + else + { + 0 + }; - let lspaces = if cell_width > width { - 0 - } else { - ( width - cell_width ) / 2 - }; - - let rspaces = if (cell_width > width) || (slice.len() > cell_width) { - 0 - } else { - ( width - cell_width + 1 ) / 2 + cell_width - slice.len() - }; - - // println!( "icol : {icol} | irow : {irow} | width : {width} | cell_width : {cell_width} | lspaces : {lspaces} | rspaces : {rspaces}" ); + let rspaces = if cell_width >= width + { + ( ( cell_width - width ) as f32 / 2 as f32 ).round() as usize + } + else + { + 0 + }; - if lspaces > 0 - { - write!( c.buf, "{: 0 - { - write!( c.buf, "{:>width$}", " ", width = rspaces )?; - } + if lspaces > 0 + { + write!( c.buf, "{: 0 + { + write!( c.buf, "{:>width$}", " ", width = rspaces )?; } - write!( c.buf, "{}", row_postfix )?; + write!( c.buf, "{}", cell_postfix )?; } + write!( c.buf, "{}", row_postfix )?; } Ok(()) diff --git a/module/core/format_tools/src/format/print.rs b/module/core/format_tools/src/format/print.rs index 78ac91b294..ef0b879787 100644 --- a/module/core/format_tools/src/format/print.rs +++ b/module/core/format_tools/src/format/print.rs @@ -7,12 +7,7 @@ mod private { use crate::*; - use md_math::MdOffset; - use std:: - { - borrow::Cow, - collections::HashMap, - }; + use std::borrow::Cow; use core:: { fmt, @@ -225,27 +220,6 @@ mod private } - /// A struct for extracting and organizing row of table data for formatting. - - #[ derive( Debug, Default ) ] - pub struct RowDescriptor - { - pub irow : usize, - pub height : usize, - pub typ : LineType, - pub vis : bool, - } - - /// A struct for extracting and organizing row of table data for formatting. - - #[ derive( Debug, Default ) ] - pub struct ColDescriptor< 'label > - { - pub icol : usize, - pub width : usize, - pub label : &'label str, - } - /// A struct for extracting and organizing table data for formatting. /// /// `InputExtract` holds metadata and content necessary for formatting tables, @@ -259,33 +233,12 @@ mod private pub struct InputExtract< 'data > { - /// Multidimensional size in number of columns per table and number of rows per table. - pub mcells : [ usize ; 2 ], - - /// Multidimensional size in number of visible columns per table and number of visible rows per table. - pub mcells_vis : [ usize ; 2 ], - - /// Multidimensional size in number of character without taking into account grids. - pub mchars : [ usize ; 2 ], - /// Indicates if the table has a header. pub has_header : bool, - /// Descriptors for each column, including optional title, width, and index. - // width, index - pub col_descriptors : Vec< ColDescriptor< 'data > >, - - /// Descriptors for each row, including height. - pub row_descriptors : Vec< RowDescriptor >, - - /// Extracted data for each cell, including string content and size. - // string, size, - pub data : Vec< Vec< ( Cow< 'data, str >, [ usize ; 2 ] ) > >, // xxx : use maybe flat vector - - /// Dimensions of slices for retrieving data from multi-matrix. - pub slices_dim : [ usize ; 3 ], - /// Extracted slices or strings for further processing. - pub slices : Vec< &'data str >, + /// Extracted data for each cell. + /// If the table has a header, then the first row is treated as a header row with column names. + pub data : Vec< Vec< Cow< 'data, str > > >, } @@ -299,71 +252,30 @@ mod private /// This function provides an iterator that yields each row descriptor along with its index. /// If the table has a header, the first row is skipped, ensuring that iteration starts from /// the first data row. - /// - /// # Returns - /// - /// An iterator over tuples containing: - /// - `usize`: The index of the row. - /// - `&RowDescriptor`: A reference to the row descriptor. - /// - pub fn rows( & self ) -> impl _IteratorTrait< Item = ( usize, &RowDescriptor ) > + pub fn rows( & self ) -> impl _IteratorTrait< Item = &Vec< Cow< 'data, str > > > { - self.row_descriptors + self.data .iter() - .enumerate() .skip( if self.has_header { 1 } else { 0 } ) } /// Returns an iterator over the header cells, or a default value if no header is present. /// /// This function provides an iterator that yields each cell in the header row. If the table - /// does not have a header, it returns an iterator over default values, which are empty strings - /// with a size of `[0, 1]`. - /// - /// # Returns - /// - /// A boxed iterator yielding tuples containing: - /// - `Cow<'data, str>`: A clone-on-write string representing the cell content. - /// - `[usize; 2]`: An array representing the size of the cell. - /// - pub fn header( & self ) -> Box< dyn Iterator< Item = ( Cow< 'data, str >, [ usize ; 2 ] ) > + '_ > + /// does not have a header, it returns an iterator over default values, which are empty strings. + pub fn header( & self ) -> Box< dyn Iterator< Item = Cow< 'data, str > > + '_ > { - if self.has_header + if self.has_header && self.data.len() != 0 { Box::new( self.data[ 0 ].iter().cloned() ) } else { - Box::new( std::iter::repeat( ( Cow::Borrowed( "" ), [ 0, 1 ] ) ).take( self.mcells[ 0 ] ) ) + let col_count = if self.data.len() == 0 { 0 } else { self.data[0].len() }; + Box::new( std::iter::repeat( Cow::Borrowed( "" ) ).take( col_count ) ) } } - /// Returns a slice from the header, or an empty string if no header is present. - /// - /// This function retrieves a specific slice from the header row based on the provided indices. - /// If the table does not have a header, it returns an empty string. - /// - /// # Arguments - /// - /// - `islice`: The slice index within the header cell. - /// - `icol`: The column index within the header row. - /// - /// # Returns - /// - /// A string slice representing the header content at the specified indices. - /// - pub fn header_slice( & self, islice : usize, icol : usize ) -> & str - { - if self.has_header - { - let md_index = [ islice, icol, 0 ]; - self.slices[ self.slices_dim.md_offset( md_index ) ] - } - else - { - "" - } - } /// Extract input data from and collect it in a format consumable by output formatter. pub fn extract< 't, 'context, Table, RowKey, Row, CellKey> ( @@ -379,128 +291,52 @@ mod private Table : TableRows< RowKey = RowKey, Row = Row, CellKey = CellKey >, Table : TableHeader< CellKey = CellKey >, RowKey : table::RowKey, - Row : Cells< CellKey> + 'data, + Row : Cells< CellKey > + 'data, CellKey : table::CellKey + ?Sized + 'data, // CellRepr : table::CellRepr, { - use md_math::MdOffset; - - // let mcells = table.mcells(); - let mut mcells_vis = [ 0 ; 2 ]; - let mut mcells = [ 0 ; 2 ]; - let mut mchars = [ 0 ; 2 ]; - - // key width, index - let mut key_to_ikey : HashMap< &'t CellKey, usize > = HashMap::new(); - - let mut col_descriptors : Vec< ColDescriptor< '_ > > = Vec::with_capacity( mcells[ 0 ] ); - let mut row_descriptors : Vec< RowDescriptor > = Vec::with_capacity( mcells[ 1 ] ); let mut has_header = false; - let mut data : Vec< Vec< ( Cow< 't, str >, [ usize ; 2 ] ) > > = Vec::new(); + let mut data : Vec< Vec< Cow< 't, str > > > = Vec::new(); let rows = table.rows(); - let mut irow : usize = 0; - let filter_col_need_args = filter_col.need_args(); - // let filter_row_need_args = filter_row.need_args(); - let mut row_add = | row_iter : &'_ mut dyn _IteratorTrait< Item = ( &'t CellKey, Cow< 't, str > ) >, typ : LineType | + let mut row_add = | row_iter : &'_ mut dyn _IteratorTrait< Item = ( &'t CellKey, Cow< 't, str > ) > | { - - irow = row_descriptors.len(); - let vis = true; - let height = 1; - let mut row = RowDescriptor { height, typ, vis, irow }; - let mut ncol = 0; - let mut ncol_vis = 0; - - let fields : Vec< ( Cow< 't, str >, [ usize ; 2 ] ) > = row_iter + let fields : Vec< Cow< 't, str > > = row_iter .filter_map ( | ( key, val ) | { - let l = col_descriptors.len(); - - ncol += 1; - - if filter_col_need_args + if !filter_col.filter_col( key.borrow() ) { - if !filter_col.filter_col( key.borrow() ) - { - return None; - } - } - else - { - if !filter_col.filter_col( "" ) - { - return None; - } + return None; } - ncol_vis += 1; - - let sz = string::size( &val ); - - key_to_ikey - .entry( key ) - .and_modify( | icol | - { - let col = &mut col_descriptors[ *icol ]; - col.width = col.width.max( sz[ 0 ] ); - col.label = ""; - }) - .or_insert_with( || - { - let icol = l; - let width = sz[ 0 ]; - let col = ColDescriptor { width, icol, label : "" }; - col_descriptors.push( col ); - icol - }); - - row.height = row.height.max( sz[ 1 ] ); - return Some( ( val, sz ) ); + return Some( val ); } ) .collect(); - mcells[ 0 ] = mcells[ 0 ].max( ncol ); - mcells_vis[ 0 ] = mcells_vis[ 0 ].max( ncol_vis ); - - row.vis = filter_row.filter_row( typ, irow, &fields ); - if row.vis + if filter_row.filter_row( &fields ) { - mcells_vis[ 1 ] += 1; + data.push( fields ); } - mcells[ 1 ] += 1; - - row_descriptors.push( row ); - data.push( fields ); - }; - // process header first - if let Some( header ) = table.header() { - rows.len().checked_add( 1 ).expect( "Table has too many rows" ); - // assert!( header.len() <= usize::MAX, "Header of a table has too many cells" ); has_header = true; - let mut row2 = header.map( | ( key, title ) | + let mut row2 = header.map( | ( key, title ) | { ( key, Cow::Borrowed( title ) ) }); - row_add( &mut row2, LineType::Header ); + row_add( &mut row2 ); } - // Collect rows - // key, string, size, for row in rows { - // assert!( row.cells().len() <= usize::MAX, "Row of a table has too many cells" ); - let mut row2 = row .cells() .map @@ -524,75 +360,15 @@ mod private } ); - row_add( &mut row2, LineType::Regular ); + row_add( &mut row2 ); } - // calculate size in chars - - mchars[ 0 ] = col_descriptors.iter().fold( 0, | acc, col | acc + col.width ); - mchars[ 1 ] = row_descriptors.iter().fold( 0, | acc, row | acc + if row.vis { row.height } else { 0 } ); - - // cook slices multi-matrix - - let mut slices_dim = [ 1, mcells[ 0 ], mcells[ 1 ] ]; - slices_dim[ 0 ] = row_descriptors - .iter() - .fold( 0, | acc : usize, row | acc.max( row.height ) ) - ; - - let slices_len = slices_dim[ 0 ] * slices_dim[ 1 ] * slices_dim[ 2 ]; - let slices : Vec< &str > = vec![ "" ; slices_len ]; - - // assert_eq!( mcells, mcells, r#"Incorrect multidimensional size of table - // mcells <> mcells - // {mcells:?} <> {mcells:?}"# ); - // println!( "mcells : {mcells:?} | mcells : {mcells:?} | mcells_vis : {mcells_vis:?}" ); - - let mut x = InputExtract::< '_ > + let x = InputExtract::< '_ > { - mcells, - mcells_vis, - mchars, - col_descriptors, - row_descriptors, data, has_header, - slices_dim, - slices, }; - // extract slices - - let mut slices : Vec< &str > = vec![]; - std::mem::swap( &mut x.slices, &mut slices ); - - let mut irow : isize = -1; - for row_data in x.data.iter() - { - - irow += 1; - - for icol in 0 .. x.col_descriptors.len() - { - let cell = &row_data[ icol ]; - string::lines( cell.0.as_ref() ) - .enumerate() - .for_each( | ( layer, s ) | - { - let md_index = [ layer, icol, irow as usize ]; - slices[ x.slices_dim.md_offset( md_index ) ] = s; - }) - ; - if irow == 0 - { - x.col_descriptors[ icol ].label = cell.0.as_ref(); - } - } - - } - - std::mem::swap( &mut x.slices, &mut slices ); - return callback( &x ); } diff --git a/module/core/format_tools/tests/inc/format_records_test.rs b/module/core/format_tools/tests/inc/format_records_test.rs index 72f23a5ff5..d6e2b16e70 100644 --- a/module/core/format_tools/tests/inc/format_records_test.rs +++ b/module/core/format_tools/tests/inc/format_records_test.rs @@ -281,9 +281,9 @@ fn filter_row_callback() format.row_separator = "\n".into(); let mut printer = print::Printer::with_format( &format ); - printer.filter_row = &| _typ, irow, _row : &[ ( Cow< '_, str >, [ usize ; 2 ] ) ] | + printer.filter_row = &| row : &[ Cow< '_, str > ] | { - irow != 1 + row[0] != "1" }; let as_table = AsTable::new( &test_objects ); diff --git a/module/core/format_tools/tests/inc/format_table_test.rs b/module/core/format_tools/tests/inc/format_table_test.rs index eb8a3b17dd..4cb82a3286 100644 --- a/module/core/format_tools/tests/inc/format_table_test.rs +++ b/module/core/format_tools/tests/inc/format_table_test.rs @@ -295,9 +295,9 @@ fn filter_row_callback() format.row_separator = "\n".into(); let mut printer = print::Printer::with_format( &format ); - printer.filter_row = &| _typ, irow, _row : &[ ( Cow< '_, str >, [ usize ; 2 ] ) ] | + printer.filter_row = &| row : &[ Cow< '_, str > ] | { - irow != 1 + row[0] != "1" }; let as_table = AsTable::new( &test_objects ); diff --git a/module/core/format_tools/tests/inc/to_string_with_fallback_test.rs b/module/core/format_tools/tests/inc/to_string_with_fallback_test.rs index bd9947cd71..9491237b6d 100644 --- a/module/core/format_tools/tests/inc/to_string_with_fallback_test.rs +++ b/module/core/format_tools/tests/inc/to_string_with_fallback_test.rs @@ -14,7 +14,7 @@ use the_module:: use std:: { - // fmt, + fmt, // collections::HashMap, borrow::Cow, }; diff --git a/module/move/assistant/src/bin/main.rs b/module/move/assistant/src/bin/main.rs index adeeaf150f..419030d03b 100644 --- a/module/move/assistant/src/bin/main.rs +++ b/module/move/assistant/src/bin/main.rs @@ -26,7 +26,7 @@ async fn main() -> Result< (), Box< dyn Error > > let secret = Secret::load()?; - let client = client::client( &secret )?; + let client = client( &secret )?; let cli = Cli::parse(); From 23bead1a2fe296208d775b99bcffc909d5895069 Mon Sep 17 00:00:00 2001 From: InAnYan Date: Wed, 13 Nov 2024 15:28:57 +0200 Subject: [PATCH 3/9] Some work --- module/core/format_tools/src/format.rs | 4 + .../src/format/output_format/records.rs | 7 ++ .../src/format/output_format/table.rs | 8 +- .../core/format_tools/src/format/wrap_text.rs | 106 ++++++++++++++++++ 4 files changed, 123 insertions(+), 2 deletions(-) create mode 100644 module/core/format_tools/src/format/wrap_text.rs diff --git a/module/core/format_tools/src/format.rs b/module/core/format_tools/src/format.rs index fb207181b3..03321941f7 100644 --- a/module/core/format_tools/src/format.rs +++ b/module/core/format_tools/src/format.rs @@ -289,6 +289,7 @@ pub mod string; pub mod table; pub mod to_string; pub mod to_string_with_fallback; +pub mod wrap_text; /// A strucutre for diagnostic and demonstration purpose. #[ doc( hidden ) ] @@ -317,6 +318,7 @@ pub mod own table::orphan::*, to_string::orphan::*, to_string_with_fallback::orphan::*, + wrap_text::orphan::*, }; } @@ -369,6 +371,7 @@ pub mod exposed table::exposed::*, to_string::exposed::*, to_string_with_fallback::exposed::*, + wrap_text::exposed::*, }; } @@ -391,6 +394,7 @@ pub mod prelude table::prelude::*, to_string::prelude::*, to_string_with_fallback::prelude::*, + wrap_text::prelude::*, }; } diff --git a/module/core/format_tools/src/format/output_format/records.rs b/module/core/format_tools/src/format/output_format/records.rs index efc944c5d8..4ecc41ade4 100644 --- a/module/core/format_tools/src/format/output_format/records.rs +++ b/module/core/format_tools/src/format/output_format/records.rs @@ -34,6 +34,8 @@ use core:: }; use std::sync::OnceLock; +use format::wrap_text::wrap_text; + /// A struct representing the list of records( rows ) output format. /// /// `Records` provides an implementation for table formatting that outputs @@ -181,6 +183,11 @@ impl TableOutputFormat for Records for ( icol, col ) in row.iter().enumerate() { let key = col_names.get(icol).map( Cow::borrow ).unwrap_or( "" ); + + if icol > 0 + { + write!( c.buf, "{}", self.row_separator )?; + } write!( c.buf, "{}", self.row_prefix )?; diff --git a/module/core/format_tools/src/format/output_format/table.rs b/module/core/format_tools/src/format/output_format/table.rs index eddf90ff79..8a4aa01c51 100644 --- a/module/core/format_tools/src/format/output_format/table.rs +++ b/module/core/format_tools/src/format/output_format/table.rs @@ -22,6 +22,8 @@ use core:: }; use std::sync::OnceLock; +use format::wrap_text::wrap_text; + /// A struct representing the classic table output format. /// /// `Table` provides a standard implementation for table formatting, @@ -176,11 +178,13 @@ impl TableOutputFormat for Table let row_separator = &self.row_separator; let h = self.h.to_string(); + let data = wrap_text( &x.data, 0 ); + let column_count = x.header().count(); let mut col_width : Vec< usize > = vec![ 0; column_count ]; - for ( _, row ) in x.data.iter().enumerate() + for ( _, row ) in data.iter().enumerate() { for ( icol, col ) in row.iter().enumerate() { @@ -194,7 +198,7 @@ impl TableOutputFormat for Table + column_count * ( self.cell_postfix.chars().count() + self.cell_prefix.chars().count() ) + if column_count == 0 { 0 } else { ( column_count - 1 ) * self.cell_separator.chars().count() }; - for ( irow, row ) in x.data.iter().enumerate() + for ( irow, row ) in data.iter().enumerate() { if irow == 1 && x.has_header && self.delimitting_header { diff --git a/module/core/format_tools/src/format/wrap_text.rs b/module/core/format_tools/src/format/wrap_text.rs new file mode 100644 index 0000000000..7f43226915 --- /dev/null +++ b/module/core/format_tools/src/format/wrap_text.rs @@ -0,0 +1,106 @@ +mod private +{ + + use std::borrow::{ Cow }; + + use crate::*; + + pub fn wrap_text< 'data > + ( + data: &'data Vec< Vec< Cow< 'data, str > > >, + _limit: usize + ) -> Vec< Vec< Cow< 'data, str > > > + { + let mut new_data = Vec::new(); + + for row in data + { + let unwrapped_text : Vec< Vec< Cow< 'data, str > > > = row.iter().map( |c| string::lines( c.as_ref() ).map( Cow::from ).collect() ).collect(); + + let max_rows = unwrapped_text.iter().map( Vec::len ).max().unwrap_or(0); + + let mut transposed : Vec< Vec< Cow< 'data, str > > > = Vec::new(); + + for i in 0..max_rows + { + let mut row_vec : Vec< Cow< 'data, str > > = Vec::new(); + + for col_lines in &unwrapped_text + { + if col_lines.len() > i + { + row_vec.push( col_lines[ i ].clone() ); + } + else + { + row_vec.push( Cow::from( "" ) ); + } + } + + transposed.push( row_vec ); + } + + new_data.extend( transposed ); + } + + new_data + } +} + +#[ doc( inline ) ] +#[ 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 private:: + { + wrap_text, + }; +} + +/// Orphan namespace of the module. +#[ allow( unused_imports ) ] +pub mod orphan +{ + use super::*; + pub use super::super::to_string_with_fallback; + + #[ doc( inline ) ] + pub use exposed::*; + + #[ doc( inline ) ] + pub use private:: + { + }; + +} + +/// Exposed namespace of the module. +#[ allow( unused_imports ) ] +pub mod exposed +{ + use super::*; + #[ doc( inline ) ] + pub use prelude::*; + + #[ doc( inline ) ] + pub use private:: + { + }; + +} + +/// Prelude to use essentials: `use my_module::prelude::*`. +#[ allow( unused_imports ) ] +pub mod prelude +{ + use super::*; +} \ No newline at end of file From 8ccea600a2022a5d2ca18c9c4100e29025819322 Mon Sep 17 00:00:00 2001 From: InAnYan Date: Thu, 14 Nov 2024 15:06:30 +0200 Subject: [PATCH 4/9] Clean code --- .../src/format/output_format/table.rs | 19 ++----------------- 1 file changed, 2 insertions(+), 17 deletions(-) diff --git a/module/core/format_tools/src/format/output_format/table.rs b/module/core/format_tools/src/format/output_format/table.rs index 8a4aa01c51..20fa2cc7b9 100644 --- a/module/core/format_tools/src/format/output_format/table.rs +++ b/module/core/format_tools/src/format/output_format/table.rs @@ -225,23 +225,8 @@ impl TableOutputFormat for Table write!( c.buf, "{}", cell_prefix )?; - let lspaces = if cell_width >= width - { - ( cell_width - width ) / 2 - } - else - { - 0 - }; - - let rspaces = if cell_width >= width - { - ( ( cell_width - width ) as f32 / 2 as f32 ).round() as usize - } - else - { - 0 - }; + let lspaces = ( cell_width - width ) / 2; + let rspaces = ( ( cell_width - width ) as f32 / 2 as f32 ).round() as usize; if lspaces > 0 { From 7d5dd3a5dcd0da3863b51bbb064fb5dbe759fab5 Mon Sep 17 00:00:00 2001 From: InAnYan Date: Thu, 14 Nov 2024 16:11:07 +0200 Subject: [PATCH 5/9] Only 2 left --- module/core/format_tools/src/format.rs | 4 - .../src/format/output_format/records.rs | 77 ++++++++----- .../src/format/output_format/table.rs | 64 +++++++++-- .../core/format_tools/src/format/wrap_text.rs | 106 ------------------ 4 files changed, 103 insertions(+), 148 deletions(-) delete mode 100644 module/core/format_tools/src/format/wrap_text.rs diff --git a/module/core/format_tools/src/format.rs b/module/core/format_tools/src/format.rs index 03321941f7..fb207181b3 100644 --- a/module/core/format_tools/src/format.rs +++ b/module/core/format_tools/src/format.rs @@ -289,7 +289,6 @@ pub mod string; pub mod table; pub mod to_string; pub mod to_string_with_fallback; -pub mod wrap_text; /// A strucutre for diagnostic and demonstration purpose. #[ doc( hidden ) ] @@ -318,7 +317,6 @@ pub mod own table::orphan::*, to_string::orphan::*, to_string_with_fallback::orphan::*, - wrap_text::orphan::*, }; } @@ -371,7 +369,6 @@ pub mod exposed table::exposed::*, to_string::exposed::*, to_string_with_fallback::exposed::*, - wrap_text::exposed::*, }; } @@ -394,7 +391,6 @@ pub mod prelude table::prelude::*, to_string::prelude::*, to_string_with_fallback::prelude::*, - wrap_text::prelude::*, }; } diff --git a/module/core/format_tools/src/format/output_format/records.rs b/module/core/format_tools/src/format/output_format/records.rs index 4ecc41ade4..78d32b2938 100644 --- a/module/core/format_tools/src/format/output_format/records.rs +++ b/module/core/format_tools/src/format/output_format/records.rs @@ -34,8 +34,6 @@ use core:: }; use std::sync::OnceLock; -use format::wrap_text::wrap_text; - /// A struct representing the list of records( rows ) output format. /// /// `Records` provides an implementation for table formatting that outputs @@ -158,48 +156,57 @@ impl TableOutputFormat for Records ) -> fmt::Result { - let col_names : Vec< Cow< 'data, str > > = x.header().collect(); + let field_names : Vec< Cow< 'data, str > > = x.header().collect(); let key_width = x.header().fold( 0, | acc, cell | acc.max( cell.len() ) ); write!( c.buf, "{}", self.table_prefix )?; - let mut first = true; - - for ( irow, row ) in x.rows().enumerate() + for ( ientry, entry ) in x.rows().enumerate() { - if first - { - first = false; - } - else + if ientry > 0 { write!( c.buf, "{}", self.table_separator )?; } - writeln!( c.buf, " = {}", irow + 1 )?; + writeln!( c.buf, " = {}", ientry + 1 )?; - let value_width = row.iter().fold( 0, | acc, cell | acc.max( cell.len() ) ); + let row = wrap_text( entry, 0 ); - for ( icol, col ) in row.iter().enumerate() - { - let key = col_names.get(icol).map( Cow::borrow ).unwrap_or( "" ); + let value_width = row.iter().map( |sr| sr.iter().map( |c| c.chars().count() ).max().unwrap_or(0) ).max().unwrap_or(0); - if icol > 0 + let mut row_count = 0; + + for ( ifield, field ) in row.iter().enumerate() + { + for ( irow, row ) in field.iter().enumerate() { - write!( c.buf, "{}", self.row_separator )?; + if row_count > 0 + { + write!( c.buf, "{}", self.row_separator )?; + } + row_count += 1; + + let key = if irow > 0 + { + "" + } + else + { + field_names.get( ifield ).map( Cow::borrow ).unwrap_or( "" ) + }; + + write!( c.buf, "{}", self.row_prefix )?; + + write!( c.buf, "{}", self.cell_prefix )?; + write!( c.buf, "{: +( + data: &'data Vec< Cow< 'data, str > >, + _limit: usize +) +-> Vec< Vec< &'data str > > +{ + data.iter().map( |c| string::lines( c ).collect() ).collect() +} \ No newline at end of file diff --git a/module/core/format_tools/src/format/output_format/table.rs b/module/core/format_tools/src/format/output_format/table.rs index 20fa2cc7b9..00eedd790f 100644 --- a/module/core/format_tools/src/format/output_format/table.rs +++ b/module/core/format_tools/src/format/output_format/table.rs @@ -16,14 +16,13 @@ use print:: InputExtract, Context, }; +use std::borrow::Cow; use core:: { fmt, }; use std::sync::OnceLock; -use format::wrap_text::wrap_text; - /// A struct representing the classic table output format. /// /// `Table` provides a standard implementation for table formatting, @@ -188,7 +187,7 @@ impl TableOutputFormat for Table { for ( icol, col ) in row.iter().enumerate() { - col_width[ icol ] = col_width[ icol ].max( col.chars().count() ); + col_width[ icol ] = col_width[ icol ].max( col.content.chars().count() ); } } @@ -215,8 +214,9 @@ impl TableOutputFormat for Table for ( icol, col ) in row.iter().enumerate() { - let cell_width = col_width[ icol ]; - let width = col.len(); + let cell_width = col.wrap_width; + let col_width = col_width[ icol ]; + let slice_width = col.content.chars().count(); if icol > 0 { @@ -225,15 +225,15 @@ impl TableOutputFormat for Table write!( c.buf, "{}", cell_prefix )?; - let lspaces = ( cell_width - width ) / 2; - let rspaces = ( ( cell_width - width ) as f32 / 2 as f32 ).round() as usize; + let lspaces = ( col_width - cell_width ) / 2; + let rspaces = ( ( col_width - cell_width ) as f32 / 2 as f32 ).round() as usize + cell_width - slice_width; if lspaces > 0 { write!( c.buf, "{: 0 { @@ -249,3 +249,51 @@ impl TableOutputFormat for Table Ok(()) } } + +struct WrappedCell< 'data > +{ + wrap_width : usize, + content : Cow< 'data, str > +} + +fn wrap_text< 'data > +( + data: &'data Vec< Vec< Cow< 'data, str > > >, + _limit: usize +) -> Vec< Vec< WrappedCell< 'data > > > +{ + let mut new_data = Vec::new(); + + for row in data + { + let unwrapped_text : Vec< Vec< Cow< 'data, str > > > = row.iter().map( |c| string::lines( c.as_ref() ).map( Cow::from ).collect() ).collect(); + + let max_rows = unwrapped_text.iter().map( Vec::len ).max().unwrap_or(0); + + let mut transposed : Vec< Vec< WrappedCell< 'data > > > = Vec::new(); + + for i in 0..max_rows + { + let mut row_vec : Vec< WrappedCell< 'data > > = Vec::new(); + + for col_lines in &unwrapped_text + { + 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 ); + } + + new_data.extend( transposed ); + } + + new_data +} \ No newline at end of file diff --git a/module/core/format_tools/src/format/wrap_text.rs b/module/core/format_tools/src/format/wrap_text.rs deleted file mode 100644 index 7f43226915..0000000000 --- a/module/core/format_tools/src/format/wrap_text.rs +++ /dev/null @@ -1,106 +0,0 @@ -mod private -{ - - use std::borrow::{ Cow }; - - use crate::*; - - pub fn wrap_text< 'data > - ( - data: &'data Vec< Vec< Cow< 'data, str > > >, - _limit: usize - ) -> Vec< Vec< Cow< 'data, str > > > - { - let mut new_data = Vec::new(); - - for row in data - { - let unwrapped_text : Vec< Vec< Cow< 'data, str > > > = row.iter().map( |c| string::lines( c.as_ref() ).map( Cow::from ).collect() ).collect(); - - let max_rows = unwrapped_text.iter().map( Vec::len ).max().unwrap_or(0); - - let mut transposed : Vec< Vec< Cow< 'data, str > > > = Vec::new(); - - for i in 0..max_rows - { - let mut row_vec : Vec< Cow< 'data, str > > = Vec::new(); - - for col_lines in &unwrapped_text - { - if col_lines.len() > i - { - row_vec.push( col_lines[ i ].clone() ); - } - else - { - row_vec.push( Cow::from( "" ) ); - } - } - - transposed.push( row_vec ); - } - - new_data.extend( transposed ); - } - - new_data - } -} - -#[ doc( inline ) ] -#[ 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 private:: - { - wrap_text, - }; -} - -/// Orphan namespace of the module. -#[ allow( unused_imports ) ] -pub mod orphan -{ - use super::*; - pub use super::super::to_string_with_fallback; - - #[ doc( inline ) ] - pub use exposed::*; - - #[ doc( inline ) ] - pub use private:: - { - }; - -} - -/// Exposed namespace of the module. -#[ allow( unused_imports ) ] -pub mod exposed -{ - use super::*; - #[ doc( inline ) ] - pub use prelude::*; - - #[ doc( inline ) ] - pub use private:: - { - }; - -} - -/// Prelude to use essentials: `use my_module::prelude::*`. -#[ allow( unused_imports ) ] -pub mod prelude -{ - use super::*; -} \ No newline at end of file From dff2ccbaaf899a49d58ccfe39f1fd722a0f53c05 Mon Sep 17 00:00:00 2001 From: InAnYan Date: Thu, 14 Nov 2024 16:14:52 +0200 Subject: [PATCH 6/9] Only 1 left --- .../core/format_tools/src/format/output_format/table.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/module/core/format_tools/src/format/output_format/table.rs b/module/core/format_tools/src/format/output_format/table.rs index 00eedd790f..ca7476c32b 100644 --- a/module/core/format_tools/src/format/output_format/table.rs +++ b/module/core/format_tools/src/format/output_format/table.rs @@ -250,6 +250,7 @@ impl TableOutputFormat for Table } } +#[ derive( Debug ) ] struct WrappedCell< 'data > { wrap_width : usize, @@ -269,9 +270,14 @@ fn wrap_text< 'data > let unwrapped_text : Vec< Vec< Cow< 'data, str > > > = row.iter().map( |c| string::lines( c.as_ref() ).map( Cow::from ).collect() ).collect(); let max_rows = unwrapped_text.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(); @@ -295,5 +301,6 @@ fn wrap_text< 'data > new_data.extend( transposed ); } + dbg!(&new_data); new_data } \ No newline at end of file From 35e23d11ec45cf1c0ce33abd6c3f5efe38024f2c Mon Sep 17 00:00:00 2001 From: InAnYan Date: Thu, 14 Nov 2024 16:36:29 +0200 Subject: [PATCH 7/9] All tests passed --- .../src/format/output_format/keys.rs | 4 +-- .../src/format/output_format/records.rs | 10 +++++--- .../src/format/output_format/table.rs | 25 ++++++++++++------- module/core/format_tools/src/format/print.rs | 25 ++++++++++++------- 4 files changed, 41 insertions(+), 23 deletions(-) diff --git a/module/core/format_tools/src/format/output_format/keys.rs b/module/core/format_tools/src/format/output_format/keys.rs index c88f8526b4..5e2397dcb8 100644 --- a/module/core/format_tools/src/format/output_format/keys.rs +++ b/module/core/format_tools/src/format/output_format/keys.rs @@ -94,7 +94,7 @@ impl TableOutputFormat for Keys if x.has_header && x.data.len() != 0 { - for col in &x.data[0] + for col in &x.data[0].1 { write!( c.buf, " - {}\n", col )?; } @@ -102,7 +102,7 @@ impl TableOutputFormat for Keys if x.data.len() != 0 { - write!( c.buf, " {} fields\n", x.data[0].len() )?; + write!( c.buf, " {} fields\n", x.data[0].1.len() )?; } Ok(()) diff --git a/module/core/format_tools/src/format/output_format/records.rs b/module/core/format_tools/src/format/output_format/records.rs index 78d32b2938..c6f8ab4d68 100644 --- a/module/core/format_tools/src/format/output_format/records.rs +++ b/module/core/format_tools/src/format/output_format/records.rs @@ -161,14 +161,18 @@ impl TableOutputFormat for Records write!( c.buf, "{}", self.table_prefix )?; - for ( ientry, entry ) in x.rows().enumerate() + let mut actual_entries = 0; + + for ( ientry, entry ) in x.rows() { - if ientry > 0 + if actual_entries > 0 { write!( c.buf, "{}", self.table_separator )?; } - writeln!( c.buf, " = {}", ientry + 1 )?; + actual_entries += 1; + + writeln!( c.buf, " = {}", ientry )?; let row = wrap_text( entry, 0 ); diff --git a/module/core/format_tools/src/format/output_format/table.rs b/module/core/format_tools/src/format/output_format/table.rs index ca7476c32b..38e695710f 100644 --- a/module/core/format_tools/src/format/output_format/table.rs +++ b/module/core/format_tools/src/format/output_format/table.rs @@ -183,7 +183,7 @@ impl TableOutputFormat for Table let mut col_width : Vec< usize > = vec![ 0; column_count ]; - for ( _, row ) in data.iter().enumerate() + for ( _, row ) in data.iter() { for ( icol, col ) in row.iter().enumerate() { @@ -197,19 +197,23 @@ impl TableOutputFormat for Table + column_count * ( self.cell_postfix.chars().count() + self.cell_prefix.chars().count() ) + if column_count == 0 { 0 } else { ( column_count - 1 ) * self.cell_separator.chars().count() }; - for ( irow, row ) in data.iter().enumerate() + let mut actual_rows = 0; + + for ( _, row ) in data.iter() { - if irow == 1 && x.has_header && self.delimitting_header + if actual_rows == 1 && x.has_header && self.delimitting_header { write!( c.buf, "{}", row_separator )?; write!( c.buf, "{}", h.repeat( max_row_width ) )?; } - if irow > 0 + if actual_rows > 0 { write!( c.buf, "{}", row_separator )?; } + actual_rows += 1; + write!( c.buf, "{}", row_prefix )?; for ( icol, col ) in row.iter().enumerate() @@ -259,13 +263,14 @@ struct WrappedCell< 'data > fn wrap_text< 'data > ( - data: &'data Vec< Vec< Cow< 'data, str > > >, + data: &'data Vec< ( usize, Vec< Cow< 'data, str > > ) >, _limit: usize -) -> Vec< Vec< WrappedCell< 'data > > > +) +-> Vec< ( usize, Vec< WrappedCell< 'data > > ) > { let mut new_data = Vec::new(); - for row in data + for ( id, row ) in data { let unwrapped_text : Vec< Vec< Cow< 'data, str > > > = row.iter().map( |c| string::lines( c.as_ref() ).map( Cow::from ).collect() ).collect(); @@ -298,9 +303,11 @@ fn wrap_text< 'data > transposed.push( row_vec ); } - new_data.extend( transposed ); + for row in transposed + { + new_data.push( ( *id, row ) ); + } } - dbg!(&new_data); new_data } \ No newline at end of file diff --git a/module/core/format_tools/src/format/print.rs b/module/core/format_tools/src/format/print.rs index ef0b879787..c26d920f15 100644 --- a/module/core/format_tools/src/format/print.rs +++ b/module/core/format_tools/src/format/print.rs @@ -238,7 +238,8 @@ mod private /// Extracted data for each cell. /// If the table has a header, then the first row is treated as a header row with column names. - pub data : Vec< Vec< Cow< 'data, str > > >, + /// Each row is a tuple where the first element is original (unfiltered) row index, and the second are columns. + pub data : Vec< ( usize, Vec< Cow< 'data, str > > ) >, } @@ -252,7 +253,7 @@ mod private /// This function provides an iterator that yields each row descriptor along with its index. /// If the table has a header, the first row is skipped, ensuring that iteration starts from /// the first data row. - pub fn rows( & self ) -> impl _IteratorTrait< Item = &Vec< Cow< 'data, str > > > + pub fn rows( & self ) -> impl _IteratorTrait< Item = &( usize, Vec< Cow< 'data, str > > ) > { self.data .iter() @@ -267,11 +268,11 @@ mod private { if self.has_header && self.data.len() != 0 { - Box::new( self.data[ 0 ].iter().cloned() ) + Box::new( self.data[ 0 ].1.iter().cloned() ) } else { - let col_count = if self.data.len() == 0 { 0 } else { self.data[0].len() }; + let col_count = if self.data.len() == 0 { 0 } else { self.data[0].1.len() }; Box::new( std::iter::repeat( Cow::Borrowed( "" ) ).take( col_count ) ) } } @@ -297,10 +298,10 @@ mod private { let mut has_header = false; - let mut data : Vec< Vec< Cow< 't, str > > > = Vec::new(); + let mut data : Vec< ( usize, Vec< Cow< 't, str > > ) > = Vec::new(); let rows = table.rows(); - let mut row_add = | row_iter : &'_ mut dyn _IteratorTrait< Item = ( &'t CellKey, Cow< 't, str > ) > | + let mut row_add = | id: usize, row_iter : &'_ mut dyn _IteratorTrait< Item = ( &'t CellKey, Cow< 't, str > ) > | { let fields : Vec< Cow< 't, str > > = row_iter .filter_map @@ -319,10 +320,12 @@ mod private if filter_row.filter_row( &fields ) { - data.push( fields ); + data.push( ( id, fields ) ); } }; + let mut row_id = 0; + if let Some( header ) = table.header() { has_header = true; @@ -332,7 +335,9 @@ mod private ( key, Cow::Borrowed( title ) ) }); - row_add( &mut row2 ); + row_add( row_id, &mut row2 ); + + row_id += 1; } for row in rows @@ -360,7 +365,9 @@ mod private } ); - row_add( &mut row2 ); + row_add( row_id, &mut row2 ); + + row_id += 1; } let x = InputExtract::< '_ > From 801648592f70bbc8fb1801b00dfadeaee5fdbcf8 Mon Sep 17 00:00:00 2001 From: InAnYan Date: Thu, 14 Nov 2024 16:49:07 +0200 Subject: [PATCH 8/9] Add parameter --- module/core/format_tools/src/format/output_format/table.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/module/core/format_tools/src/format/output_format/table.rs b/module/core/format_tools/src/format/output_format/table.rs index 38e695710f..b7a59a90e1 100644 --- a/module/core/format_tools/src/format/output_format/table.rs +++ b/module/core/format_tools/src/format/output_format/table.rs @@ -177,7 +177,7 @@ impl TableOutputFormat for Table let row_separator = &self.row_separator; let h = self.h.to_string(); - let data = wrap_text( &x.data, 0 ); + let data = wrap_text( &x.data, self.max_width ); let column_count = x.header().count(); From c849a69555cd335fd226ed29316e40e8f30b5e2d Mon Sep 17 00:00:00 2001 From: InAnYan Date: Thu, 14 Nov 2024 16:50:46 +0200 Subject: [PATCH 9/9] Add LinesWithLimit --- .../src/format/output_format/table.rs | 4 +- module/core/format_tools/src/format/string.rs | 98 +++++++++++++++++++ 2 files changed, 100 insertions(+), 2 deletions(-) diff --git a/module/core/format_tools/src/format/output_format/table.rs b/module/core/format_tools/src/format/output_format/table.rs index b7a59a90e1..749c529dec 100644 --- a/module/core/format_tools/src/format/output_format/table.rs +++ b/module/core/format_tools/src/format/output_format/table.rs @@ -264,7 +264,7 @@ struct WrappedCell< 'data > fn wrap_text< 'data > ( data: &'data Vec< ( usize, Vec< Cow< 'data, str > > ) >, - _limit: usize + limit: usize ) -> Vec< ( usize, Vec< WrappedCell< 'data > > ) > { @@ -272,7 +272,7 @@ fn wrap_text< 'data > for ( id, row ) in data { - let unwrapped_text : Vec< Vec< Cow< 'data, str > > > = row.iter().map( |c| string::lines( c.as_ref() ).map( Cow::from ).collect() ).collect(); + let unwrapped_text : Vec< Vec< Cow< 'data, str > > > = row.iter().map( |c| string::lines_with_limit( c.as_ref(), limit ).map( Cow::from ).collect() ).collect(); let max_rows = unwrapped_text.iter().map( Vec::len ).max().unwrap_or(0); diff --git a/module/core/format_tools/src/format/string.rs b/module/core/format_tools/src/format/string.rs index 619d1690c2..1cf3986af0 100644 --- a/module/core/format_tools/src/format/string.rs +++ b/module/core/format_tools/src/format/string.rs @@ -84,6 +84,35 @@ mod private [ width, height ] } + pub fn size_with_limit< S : AsRef< str > > + ( + src : S, + limit_width : usize, + ) + -> [ usize; 2 ] + { + if limit_width == 0 + { + return size( src ); + } + + let text = src.as_ref(); + let mut height = 0; + let mut width = 0; + + for line in lines_with_limit( text, limit_width ) + { + height += 1; + let line_length = line.len(); + if line_length > width + { + width = line_length; + } + } + + [ width, height ] + } + /// Returns an iterator over the lines of a string slice. /// /// This function provides an iterator that yields each line of the input string slice. @@ -114,6 +143,16 @@ mod private Lines::new( src.as_ref() ) } + pub fn lines_with_limit< S : AsRef< str > + ?Sized > + ( + src : & S, + limit_width : usize + ) + -> LinesWithLimit< '_ > + { + LinesWithLimit::new( src.as_ref(), limit_width ) + } + /// An iterator over the lines of a string slice. /// /// This struct implements the `Iterator` trait, allowing you to iterate over the lines @@ -128,6 +167,7 @@ mod private has_trailing_newline : bool, finished : bool, } + impl< 'a > Lines< 'a > { fn new( input : &'a str ) -> Self @@ -172,6 +212,61 @@ mod private } } + #[ derive( Debug ) ] + pub struct LinesWithLimit< 'a > + { + lines : Lines< 'a >, + limit_width : usize, + cur : Option< &'a str >, + } + + impl< 'a > LinesWithLimit< 'a > + { + fn new( input : &'a str, limit_width : usize ) -> Self + { + LinesWithLimit + { + lines : lines( input ), + limit_width, + cur : None, + } + } + } + + impl< 'a > Iterator for LinesWithLimit< 'a > + { + type Item = &'a str; + + fn next( &mut self ) -> Option< Self::Item > + { + if self.cur.is_none() || self.cur.is_some_and( str::is_empty ) + { + self.cur = self.lines.next(); + } + + match self.cur + { + None => return None, + + Some( cur ) => + { + if self.limit_width == 0 + { + self.cur = None; + Some( cur ) + } + else + { + let (chunk, rest) = cur.split_at(self.limit_width.min(cur.len())); + self.cur = Some( rest ); + + Some(chunk) + } + } + } + } + } + } #[ allow( unused_imports ) ] @@ -189,8 +284,11 @@ pub mod own pub use private:: { size, + size_with_limit, lines, Lines, + lines_with_limit, + LinesWithLimit, }; }