From c30378cf909f4be3c6bf3dbe46f9270ac80667d2 Mon Sep 17 00:00:00 2001 From: Wandalen Date: Mon, 19 Aug 2024 18:32:12 +0300 Subject: [PATCH] Evolve format tools (#1435) --- module/core/format_tools/src/format.rs | 61 +- .../core/format_tools/src/format/as_table.rs | 183 ++++-- .../core/format_tools/src/format/md_math.rs | 95 ++++ module/core/format_tools/src/format/print.rs | 519 ++++++++++++++---- module/core/format_tools/src/format/string.rs | 220 ++++++++ module/core/format_tools/src/format/table.rs | 367 +++++++++---- .../core/format_tools/src/format/to_string.rs | 24 +- .../format_tools/src/format/to_string/aref.rs | 12 +- module/core/format_tools/src/lib.rs | 1 + .../format_tools/tests/inc/fields_test.rs | 16 +- .../format_tools/tests/inc/md_math_test.rs | 17 + module/core/format_tools/tests/inc/mod.rs | 5 +- .../core/format_tools/tests/inc/print_test.rs | 233 +++++--- .../tests/inc/print_without_wrap.rs | 161 ++++++ .../format_tools/tests/inc/string_test.rs | 144 +++++ module/core/format_tools/tests/tests.rs | 2 +- .../former_tests/name_collision_context.rs | 19 - .../inc/former_tests/name_collision_core.rs | 19 - .../inc/former_tests/name_collision_end.rs | 24 - ...lision_former_hashmap_without_parameter.rs | 5 +- ...llision_former_vector_without_parameter.rs | 5 +- .../inc/former_tests/name_collision_on_end.rs | 18 - .../tests/inc/former_tests/name_collisions.rs | 128 +++-- .../collections_without_subformer.rs | 58 +- module/core/former/tests/inc/mod.rs | 9 +- module/core/former_meta/src/derive_former.rs | 14 +- .../former_meta/src/derive_former/field.rs | 39 +- module/core/reflect_tools/src/reflect.rs | 20 - .../core/reflect_tools/src/reflect/fields.rs | 107 +++- .../src/reflect/wrapper/maybe_as.rs | 30 +- .../tests/inc/fundamental/fields_test.rs | 16 +- module/core/reflect_tools_meta/src/lib.rs | 4 +- 32 files changed, 1999 insertions(+), 576 deletions(-) create mode 100644 module/core/format_tools/src/format/md_math.rs create mode 100644 module/core/format_tools/src/format/string.rs create mode 100644 module/core/format_tools/tests/inc/md_math_test.rs create mode 100644 module/core/format_tools/tests/inc/print_without_wrap.rs create mode 100644 module/core/format_tools/tests/inc/string_test.rs delete mode 100644 module/core/former/tests/inc/former_tests/name_collision_context.rs delete mode 100644 module/core/former/tests/inc/former_tests/name_collision_core.rs delete mode 100644 module/core/former/tests/inc/former_tests/name_collision_end.rs delete mode 100644 module/core/former/tests/inc/former_tests/name_collision_on_end.rs diff --git a/module/core/format_tools/src/format.rs b/module/core/format_tools/src/format.rs index e599e2bf47..2ba8ca3428 100644 --- a/module/core/format_tools/src/format.rs +++ b/module/core/format_tools/src/format.rs @@ -28,7 +28,7 @@ pub( crate ) mod private {{ ( ::core::stringify!( $key ), - $crate::MaybeAs::< 'a, str, $how >::from + $crate::MaybeAs::< '_, str, $how >::from ( $crate::to_string_with_fallback!( $how, $fallback1, $fallback2, $src ) ), @@ -36,7 +36,6 @@ pub( crate ) mod private }}; } - /// Macro to create a field with optional fallbacks. /// /// This macro helps to convert a field of a structure into one or another string representation @@ -105,6 +104,55 @@ pub( crate ) mod private } + /// Converting representations to a reference on a string slice, + /// but if not possible, to a display string, and if that is also not possible, then to a debug string. + /// + /// Macros for converting fields to different string representations in a prioritized manner: + /// 1. Reference to a string slice. + /// 2. Display string. + /// 3. Debug string with miltiline. + pub mod ref_or_display_or_debug_multiline + { + + /// Macro to create a field with key using reference, display, or debug formatting. + /// + /// This macro attempts to convert the field to a reference to a string slice. + /// If that is not possible, it tries to use the Display trait for conversion. + /// If that also fails, it falls back to using the Debug trait with multiline. + #[ macro_export ] + macro_rules! ref_or_display_or_debug_multiline_field_with_key + { + ( + $key : ident, + $src : expr + $(,)? + ) + => + {{ + $crate::_field_with_key!( $key, $src, $crate::WithRef, $crate::WithDisplay, $crate::WithDebugMultiline ) + }}; + } + + /// Macro to create a field using reference, display, or debug formatting. + /// + /// This macro attempts to convert the field to a reference to a string slice. + /// If that is not possible, it tries to use the Display trait for conversion. + /// If that also fails, it falls back to using the Debug trait with multiline. + #[ macro_export ] + macro_rules! ref_or_display_or_debug_multiline_field + { + ( $( $t:tt )+ ) + => + {{ + $crate::_field!( $( $t )+, $crate::WithRef, $crate::WithDisplay, $crate::WithDebugMultiline ) + }} + } + + pub use ref_or_display_or_debug_multiline_field_with_key as field_with_key; + pub use ref_or_display_or_debug_multiline_field as field; + + } + /// Converting representations to a reference on a string slice, /// but if not possible, to a display string, and if that is also not possible, then to a debug string. /// @@ -206,7 +254,9 @@ pub( crate ) mod private pub mod to_string; pub mod to_string_with_fallback; pub mod as_table; +pub mod md_math; pub mod print; +pub mod string; pub mod table; #[ doc( inline ) ] @@ -225,7 +275,9 @@ pub mod own to_string::orphan::*, to_string_with_fallback::orphan::*, as_table::orphan::*, + md_math::orphan::*, print::orphan::*, + string::orphan::*, table::orphan::*, }; @@ -244,6 +296,7 @@ pub mod orphan pub use private:: { ref_or_display_or_debug, + ref_or_display_or_debug_multiline, ref_or_debug, }; @@ -264,7 +317,9 @@ pub mod exposed to_string::exposed::*, to_string_with_fallback::exposed::*, as_table::exposed::*, + md_math::exposed::*, print::exposed::*, + string::exposed::*, table::exposed::*, }; @@ -282,7 +337,9 @@ pub mod prelude to_string::prelude::*, to_string_with_fallback::prelude::*, as_table::prelude::*, + md_math::prelude::*, print::prelude::*, + string::prelude::*, table::prelude::*, }; diff --git a/module/core/format_tools/src/format/as_table.rs b/module/core/format_tools/src/format/as_table.rs index 2e08d5acaf..f10f826b43 100644 --- a/module/core/format_tools/src/format/as_table.rs +++ b/module/core/format_tools/src/format/as_table.rs @@ -7,64 +7,74 @@ pub( crate ) mod private { use crate::*; - use core::ops::{ Deref }; - use core::marker::PhantomData; - use core::fmt; + use core:: + { + ops::{ Deref }, + marker::PhantomData, + fmt, + }; - /// Transparent wrapper for table-like structures. + /// Transparent wrapper for interpreting data as a table. + /// + /// `AsTable` provides a reference-based wrapper for table-like structures, + /// encapsulating type information needed to interpret data as a table. + /// #[ repr( transparent ) ] #[ derive( Clone, Copy ) ] - pub struct AsTable< 'a, T, RowKey, Row, CellKey, Cell, Title > + pub struct AsTable< 'table, Table, RowKey, Row, CellKey, CellRepr > ( - &'a T, - ::core::marker::PhantomData< ( &'a (), fn () -> ( RowKey, Row, CellKey, Box< Cell >, Title ) ) >, + &'table Table, + ::core::marker::PhantomData + <( + &'table (), + fn() -> ( &'table RowKey, Row, &'table CellKey, CellRepr ), + )>, ) where - Row : Clone + for< 'cell > Cells< 'cell, CellKey, Cell, () > + 'a, - Title : fmt::Debug, - Cell : fmt::Debug + 'a, - Cell : std::borrow::ToOwned + ?Sized, - CellKey : fmt::Debug + Clone, + RowKey : table::RowKey, + Row : Cells< CellKey, CellRepr >, + CellKey : table::CellKey + ?Sized, + CellRepr : table::CellRepr ; - impl< 'a, T, RowKey, Row, CellKey, Cell, Title > AsTable< 'a, T, RowKey, Row, CellKey, Cell, Title > + impl< 'table, Table, RowKey, Row, CellKey, CellRepr > + AsTable< 'table, Table, RowKey, Row, CellKey, CellRepr > where - Row : Clone + for< 'cell > Cells< 'cell, CellKey, Cell, () > + 'a, - Title : fmt::Debug, - Cell : fmt::Debug + 'a, - Cell : std::borrow::ToOwned + ?Sized, - CellKey : fmt::Debug + Clone, + RowKey : table::RowKey, + Row : Cells< CellKey, CellRepr >, + CellKey : table::CellKey + ?Sized, + CellRepr : table::CellRepr, { /// Just a constructor. - pub fn new( src : &'a T ) -> Self + pub fn new( src : &'table Table ) -> Self { Self( src, Default::default() ) } } - impl< 'a, T, RowKey, Row, CellKey, Cell, Title > AsRef< T > for AsTable< 'a, T, RowKey, Row, CellKey, Cell, Title > + impl< 'table, Table, RowKey, Row, CellKey, CellRepr > AsRef< Table > + for AsTable< 'table, Table, RowKey, Row, CellKey, CellRepr > where - Row : Clone + for< 'cell > Cells< 'cell, CellKey, Cell, () > + 'a, - Title : fmt::Debug, - Cell : fmt::Debug + 'a, - Cell : std::borrow::ToOwned + ?Sized, - CellKey : fmt::Debug + Clone, + RowKey : table::RowKey, + Row : Cells< CellKey, CellRepr >, + CellKey : table::CellKey + ?Sized, + CellRepr : table::CellRepr, { - fn as_ref( &self ) -> &T + fn as_ref( &self ) -> &Table { &self.0 } } - impl< 'a, T, RowKey, Row, CellKey, Cell, Title > Deref for AsTable< 'a, T, RowKey, Row, CellKey, Cell, Title > + impl< 'table, Table, RowKey, Row, CellKey, CellRepr > Deref + for AsTable< 'table, Table, RowKey, Row, CellKey, CellRepr > where - Row : Clone + for< 'cell > Cells< 'cell, CellKey, Cell, () > + 'a, - Title : fmt::Debug, - Cell : fmt::Debug + 'a, - Cell : std::borrow::ToOwned + ?Sized, - CellKey : fmt::Debug + Clone, + RowKey : table::RowKey, + Row : Cells< CellKey, CellRepr >, + CellKey : table::CellKey + ?Sized, + CellRepr : table::CellRepr, { - type Target = T; + type Target = Table; fn deref( &self ) -> &Self::Target { @@ -72,38 +82,112 @@ pub( crate ) mod private } } - impl< 'a, T, RowKey, Row, CellKey, Cell, Title > From< &'a T > - for AsTable< 'a, T, RowKey, Row, CellKey, Cell, Title > + impl< 'table, Table, RowKey, Row, CellKey, CellRepr > From< &'table Table > + for AsTable< 'table, Table, RowKey, Row, CellKey, CellRepr > where - Row : Clone + for< 'cell > Cells< 'cell, CellKey, Cell, () > + 'a, - Title : fmt::Debug, - Cell : fmt::Debug + 'a, - Cell : std::borrow::ToOwned + ?Sized, - CellKey : fmt::Debug + Clone, + RowKey : table::RowKey, + Row : Cells< CellKey, CellRepr >, + CellKey : table::CellKey + ?Sized, + CellRepr : table::CellRepr, { - fn from( table : &'a T ) -> Self + fn from( table : &'table Table ) -> Self { AsTable( table, PhantomData ) } } - impl< 'a, T, RowKey, Row, CellKey, Cell, Title > fmt::Debug for AsTable< 'a, T, RowKey, Row, CellKey, Cell, Title > + impl< 'table, Table, RowKey, Row, CellKey, CellRepr > fmt::Debug + for AsTable< 'table, Table, RowKey, Row, CellKey, CellRepr > where - T : fmt::Debug, - Row : Clone + for< 'cell > Cells< 'cell, CellKey, Cell, () > + 'a, - Title : fmt::Debug, - Cell : std::borrow::ToOwned + ?Sized, - Cell : fmt::Debug + 'a, - CellKey : fmt::Debug + Clone, + Table : fmt::Debug, + RowKey : table::RowKey, + Row : Cells< CellKey, CellRepr >, + CellKey : table::CellKey + ?Sized, + CellRepr : table::CellRepr, { fn fmt( &self, f : &mut fmt::Formatter< '_ > ) -> fmt::Result { - f.debug_struct( "AsTable" ) + f + .debug_struct( "AsTable" ) .field( "0", &self.0 ) .finish() } } + // = + + /// Trait for converting data references into `AsTable` references. + /// + /// `IntoAsTable` provides a way to interpret data as a table, encapsulating + /// the necessary type information for handling table-like structures. + /// + pub trait IntoAsTable + { + /// The type representing the table. + type Table; + + /// The type used to identify each row. + type RowKey : table::RowKey; + + /// The type representing a row, must implement `Cells`. + type Row : Cells< Self::CellKey, Self::CellRepr >; + + /// The type used to identify cells within a row, must implement `Key` and can be unsized. + type CellKey : table::CellKey + ?Sized; + + /// The type representing the content of a cell, must implement `CellRepr`. + type CellRepr : table::CellRepr; + + /// Converts the data reference into an `AsTable` reference. + fn as_table( &self ) -> AsTable< '_, Self::Table, Self::RowKey, Self::Row, Self::CellKey, Self::CellRepr >; + } + + impl< 'table, Table, RowKey, Row, CellKey, CellRepr > IntoAsTable + for AsTable< 'table, Table, RowKey, Row, CellKey, CellRepr > + where + RowKey : table::RowKey, + Row : Cells< CellKey, CellRepr >, + CellKey : table::CellKey + ?Sized, + CellRepr : table::CellRepr, + Self : Copy, + { + + type Table = Table; + type RowKey = RowKey; + type Row = Row; + type CellKey = CellKey; + type CellRepr = CellRepr; + + fn as_table( &self ) -> AsTable< '_, Self::Table, Self::RowKey, Self::Row, Self::CellKey, Self::CellRepr > + { + *self + } + + } + +// impl< Row > IntoAsTable +// for Vec< Row > +// where +// Row : Cells< Self::CellKey, Self::CellRepr >, +// // CellKey : table::CellKey + ?Sized, +// // CellRepr : table::CellRepr, +// { +// +// type Table = Self; +// type RowKey = usize; +// type Row = Row; +// type CellKey = str; +// type CellRepr = WithRef; +// +// fn as_table( &self ) -> AsTable< '_, Self::Table, Self::RowKey, Self::Row, Self::CellKey, Self::CellRepr > +// { +// AsTable::from( self ) +// } +// +// } + + // pub struct AsTable< 'table, Table, RowKey, Row, CellKey, CellRepr > + } #[ allow( unused_imports ) ] @@ -138,6 +222,7 @@ pub mod exposed pub use private:: { AsTable, + IntoAsTable, }; } diff --git a/module/core/format_tools/src/format/md_math.rs b/module/core/format_tools/src/format/md_math.rs new file mode 100644 index 0000000000..7136be90e7 --- /dev/null +++ b/module/core/format_tools/src/format/md_math.rs @@ -0,0 +1,95 @@ +//! Multidimensional math utilities. +//! +//! Provides functionality for converting multidimensional indices into flat offsets, +//! useful for operations involving multidimensional arrays or grids. + +/// Internal namespace. +pub( crate ) mod private +{ + use core:: + { + fmt, + ops::{ Add, Mul }, + cmp::PartialOrd, + }; + + /// Trait for converting a multidimensional index into a flat offset. + /// + /// This trait is implemented for 3-dimensional arrays, allowing conversion of a + /// 3D index into a single linear offset. It is useful for mapping coordinates in + /// a 3D space to a flat array. + pub trait MdOffset< T > + { + /// Converts a 3D index into a flat offset. + /// + /// # Arguments + /// + /// - `md_index`: A 3-element array representing the multidimensional index. + /// + /// # Returns + /// + /// A value of type `T` representing the flat offset. + fn md_offset( & self, md_index : [ T ; 3 ] ) -> T; + } + + impl< T > MdOffset< T > for [ T ; 3 ] + where + T : Mul< T, Output = T > + Add< T, Output = T > + PartialOrd + Copy + fmt::Debug, + { + fn md_offset( & self, md_index : [ T ; 3 ] ) -> T + { + debug_assert!( md_index[ 0 ] < self[ 0 ], "md_index : {md_index:?} | md_size : {self:?}" ); + debug_assert!( md_index[ 1 ] < self[ 1 ], "md_index : {md_index:?} | md_size : {self:?}" ); + debug_assert!( md_index[ 2 ] < self[ 2 ], "md_index : {md_index:?} | md_size : {self:?}" ); + let m1 = self[ 0 ]; + let m2 = m1 * self[ 1 ]; + md_index[ 0 ] + m1 * md_index[ 1 ] + m2 * md_index[ 2 ] + } + } + +} + +#[ 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:: + { + MdOffset, + }; + +} + +/// Orphan namespace of the module. +#[ allow( unused_imports ) ] +pub mod orphan +{ + use super::*; + #[ doc( inline ) ] + pub use exposed::*; +} + +/// Exposed namespace of the module. +#[ allow( unused_imports ) ] +pub mod exposed +{ + use super::*; + pub use super::super::md_math; + +} + +/// Prelude to use essentials: `use my_module::prelude::*`. +#[ allow( unused_imports ) ] +pub mod prelude +{ + use super::*; +} + diff --git a/module/core/format_tools/src/format/print.rs b/module/core/format_tools/src/format/print.rs index 022bf33783..3e010dafca 100644 --- a/module/core/format_tools/src/format/print.rs +++ b/module/core/format_tools/src/format/print.rs @@ -1,5 +1,5 @@ //! -//! Nice print. +//! Print data as table. //! /// Internal namespace. @@ -7,39 +7,95 @@ pub( crate ) mod private { use crate::*; - use core::fmt; + use std:: + { + borrow::Cow, + collections::HashMap, + }; + use core:: + { + fmt, + }; use former::Former; //= - /// Struct to hold options to print data as table. + /// A struct to configure options for printing data as a table. + /// + /// The `Styles` struct provides customizable delimiters for formatting table data. It allows + /// you to define how table data should be separated and formatted, making it adaptable to + /// various needs. + /// + /// # Fields + /// + /// - `cell_separator`: A `String` that specifies the delimiter used to separate columns + /// within a table. This is the character or string that separates each column. + /// + /// - `row_prefix`: A `String` that specifies the prefix added to each row. This can be + /// used to add a consistent start to each row. + /// + /// - `row_postfix`: A `String` that specifies the postfix added to each row. This can be + /// used to add a consistent end to each row. + /// + /// - `row_postfix`: A `String` that specifies the postfix added to each row. This can be + /// used to add a consistent end to each row. + /// + /// ``` #[ derive( Debug, Former ) ] pub struct Styles { + + /// Delimiter for adding prefix to a cell. + pub cell_prefix : String, + /// Delimiter for adding postfix to a cell. + pub cell_postfix : String, /// Delimiter for separating table columns. - pub separator : String, + pub cell_separator : String, + + /// Delimiter for adding prefix to a row. + pub row_prefix : String, + /// Delimiter for adding postfix to a row. + pub row_postfix : String, + /// Delimiter for adding in between of rows. + pub row_separator : String, + } impl Default for Styles { fn default() -> Self { - let separator = " | ".to_string(); - Styles { separator } + let cell_prefix = "".to_string(); + let cell_postfix = "".to_string(); + let cell_separator = " │ ".to_string(); + let row_prefix = "│ ".to_string(); + let row_postfix = " │".to_string(); + let row_separator = "\n".to_string(); + // let filter_col = FilterColumnls::default(); + Self + { + cell_prefix, + cell_postfix, + cell_separator, + row_prefix, + row_postfix, + row_separator, + // filter_col, + } } } /// Struct for formatting tables. - pub struct Context< 'a > + pub struct Context< 'buf > { - buf : &'a mut dyn fmt::Write, + buf : &'buf mut dyn fmt::Write, styles : Styles, } - impl< 'a > Context< 'a > + impl< 'buf > Context< 'buf > { /// Just constructr. - pub fn new( buf : &'a mut dyn fmt::Write, styles : Styles ) -> Self + pub fn new( buf : &'buf mut dyn fmt::Write, styles : Styles ) -> Self { Self { buf, styles } } @@ -58,21 +114,21 @@ pub( crate ) mod private } /// A trait for converting tables to a string representation. - pub trait TableToString< 'a > + pub trait TableToString< 'data > { /// Converts the table to a string representation. /// /// # Returns /// /// A `String` containing the formatted table. - fn table_to_string( &'a self ) -> String; + fn table_to_string( &'data self ) -> String; } - impl< 'a, T > TableToString< 'a > for T + impl< 'data, T > TableToString< 'data > for T where - T : TableFormatter< 'a > + T : TableFormatter< 'data > { - fn table_to_string( &'a self ) -> String + fn table_to_string( &'data self ) -> String { let mut output = String::new(); let mut context = Context @@ -80,140 +136,384 @@ pub( crate ) mod private buf : &mut output, styles : Styles::default(), }; - T::fmt( self, &mut context ).expect( "Formatting failed" ); + T::fmt( self, &mut context ).expect( "Table formatting failed" ); output } } - /// A trait for formatting tables. + /// Trait for defining table formatting logic. /// - /// This trait defines a method for formatting tables, allowing implementations - /// to specify how a table should be formatted and displayed. + /// `TableFormatter` allows implementations to specify how tables are formatted + /// and displayed, providing flexibility in presentation. /// - - pub trait TableFormatter< 'b > + /// # Type Parameters + /// + /// - `'data`: The lifetime of the data being formatted. + /// + pub trait TableFormatter< 'data > { - /// Formats the table and writes the result to the given formatter. - fn fmt< 'a >( &'b self, f : &mut Context< 'a > ) -> fmt::Result; + /// Formats the table and writes the result to the provided context. + fn fmt< 'buf >( & 'data self, f : & mut Context< 'buf > ) -> fmt::Result; } /// A trait for formatting tables. - impl< 'a, T, RowKey, Row, CellKey, Cell, Title > TableFormatter< 'a > - for AsTable< 'a, T, RowKey, Row, CellKey, Cell, Title > + impl< 'data, T, RowKey, Row, CellKey, CellRepr > TableFormatter< 'data > + for AsTable< 'data, T, RowKey, Row, CellKey, CellRepr > where - Self : TableRows< 'a, RowKey, Row, CellKey, Cell >, - Self : TableHeader< 'a, CellKey, Title >, - Self : TableSize< 'a >, - Row : Clone + for< 'cell > Cells< 'cell, CellKey, Cell, () > + 'a, - Title : fmt::Debug, - Cell : fmt::Debug + Clone + 'a, - CellKey : fmt::Debug + Clone, + Self : TableRows< CellKey = CellKey, CellRepr = CellRepr, RowKey = RowKey, Row = Row >, + Self : TableHeader< CellKey = CellKey >, + Self : TableSize, + RowKey : table::RowKey, + Row : Cells< CellKey, CellRepr >, + CellKey : table::CellKey + ?Sized, + CellRepr : table::CellRepr, { - fn fmt( &'a self, f : &mut Context< '_ > ) -> fmt::Result + fn fmt< 'a >( &'data self, f : &mut Context< 'a > ) -> fmt::Result { - let table_size = self.table_size(); - let mut col_widths : Vec< usize > = vec![ 0 ; table_size[ 1 ] ]; - let separator = &f.styles.separator; + use md_math::MdOffset; - // println!( "{}", self.header().is_some() ); - - if let Some( header ) = self.header() - { - let mut i = 0; - for ( _key, title ) in header + FormatExtract::extract + ( + self, + All, + | x | { - col_widths[ i ] = format!( "{:?}", title ).len(); - i += 1; + + let cell_prefix = &f.styles.cell_prefix; + let cell_postfix = &f.styles.cell_postfix; + let cell_separator = &f.styles.cell_separator; + let row_prefix = &f.styles.row_prefix; + let row_postfix = &f.styles.row_postfix; + let row_separator = &f.styles.row_separator; + + for ( irow, row ) in x.row_descriptors.iter().enumerate() + { + let height = row.0; + + for islice in 0..height + { + + if irow > 0 + { + write!( f.buf, "{}", row_separator )?; + } + + write!( f.buf, "{}", row_prefix )?; + + for k in &x.col_order + { + let col = &x.col_descriptors[ k ]; + let cell_width = x.data[ irow ][ k ].1[0]; + let width = col.0; + let icol = col.1; + 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!( f.buf, "{}", cell_separator )?; + } + + write!( f.buf, "{}", cell_prefix )?; + + let lspaces = ( width - cell_width ) / 2; + let rspaces = ( width - cell_width + 1 ) / 2 + cell_width - slice.len(); + // println!( "icol : {icol} | irow : {irow} | width : {width} | cell_width : {cell_width} | lspaces : {lspaces} | rspaces : {rspaces}" ); + + if lspaces > 0 + { + write!( f.buf, "{: 0 + { + write!( f.buf, "{:>width$}", " ", width = rspaces )?; + } + + write!( f.buf, "{}", cell_postfix )?; + } + + write!( f.buf, "{}", row_postfix )?; + } + + } + + Ok(()) } - writeln!( f.buf )?; - } + ) + } + } - // dbg!( &col_widths ); + /// A struct for extracting and organizing table data for formatting. + /// + /// `FormatExtract` holds metadata and content necessary for formatting tables, + /// including dimensions, column order, and data slices. It facilitates the + /// transformation of raw table data into a structured format suitable for + /// rendering as a table. + /// - // Collect rows - let mut all_rows : Vec< Vec< String > > = Vec::new(); - for row in self.rows() + #[ allow( dead_code ) ] + #[ derive( Debug ) ] + pub struct FormatExtract< 'data, CellKey > + where + CellKey : table::CellKey + ?Sized, + { + + /// Multidimensional size in number of columns per table and number of rows per table. + pub mcells : [ usize ; 2 ], + + /// Order of columns must be as stable as possible. + pub col_order : Vec< &'data CellKey >, + + /// Descriptors for each column, including optional title, width, and index. + // key width, index + pub col_descriptors : HashMap< &'data CellKey, ( usize, usize ) >, + + /// Descriptors for each row, including height. + // height + pub row_descriptors : Vec< ( usize, ) >, + + /// Extracted data for each cell, including string content and size. + // key, string, size, + pub data : Vec< HashMap< &'data CellKey, ( Cow< 'data, str >, [ usize ; 2 ] ) > >, + + /// 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 >, + + /// Indicates if the table has a header. + pub has_header : bool, + + } + + /// Filter columns of a table to print it only partially. + pub trait FilterCol : fmt::Debug + { + /// Filter columns of a table to print it only partially. + fn filter_col< CellKey >( &self, key : &CellKey ) -> bool + where + CellKey : table::CellKey + ?Sized, + ; + } + + /// Filter passing all elements. + #[ derive( Debug, Default, PartialEq ) ] + pub struct All; + impl FilterCol for All + { + #[ inline( always ) ] + fn filter_col< CellKey >( &self, _key : &CellKey ) -> bool + where + CellKey : table::CellKey + ?Sized, + { + true + } + } + + /// Filter skipping all elements. + #[ derive( Debug, Default, PartialEq ) ] + pub struct No; + impl FilterCol for No + { + #[ inline( always ) ] + fn filter_col< CellKey >( &self, _key : &CellKey ) -> bool + where + CellKey : table::CellKey + ?Sized, + { + false + } + } + + // + + impl< 'data, CellKey > FormatExtract< 'data, CellKey > + where + CellKey : table::CellKey + ?Sized, + { + + pub fn extract< 't, Table, RowKey, Row, CellRepr > // xxx : RowKey? + ( + table : &'t Table, + filter_col : impl FilterCol, + callback : impl for< 'a2 > FnOnce( &'a2 FormatExtract< 'a2, CellKey > ) -> fmt::Result, + ) + -> fmt::Result + where + 't : 'data, + Table : TableRows< RowKey = RowKey, Row = Row, CellKey = CellKey, CellRepr = CellRepr >, + Table : TableHeader< CellKey = CellKey >, + Table : TableSize, + Row : Cells< CellKey, CellRepr > + 'data, + CellRepr : table::CellRepr, + { + use md_math::MdOffset; + + let mcells = table.mcells(); + // key width, index + let mut col_descriptors : HashMap< &CellKey, ( usize, usize ) > = HashMap::new(); + // height + let mut row_descriptors : Vec< ( usize, ) > = Vec::with_capacity( mcells[ 1 ] ); + + let mut col_order : Vec< &CellKey > = Vec::new(); + let mut has_header = false; + + let mut data : Vec< HashMap< &CellKey, ( Cow< 'data, str >, [ usize ; 2 ] ) > > = Vec::new(); + let rows = table.rows(); + let mut irow : isize = -1; + + let mut row_add = | row : &'_ mut dyn _IteratorTrait< Item = ( &'data CellKey, Cow< 'data, str > ) > | { - let fields : Vec< String > = row - .cells() - .map + + irow += 1; + row_descriptors.push( ( 1, ) ); + + let fields : HashMap< &CellKey, ( Cow< 'data, str >, [ usize ; 2 ] ) > = row + .filter_map ( - | ( _key, cell ) | + | ( key, val ) | { - match cell.0 + + if !filter_col.filter_col( key ) { - Some( cell ) => format!( "{:?}", &cell ), - None => "".to_string(), + return None; } + + let sz = string::size( &val ); + let l = col_descriptors.len(); + row_descriptors[ irow as usize ] = ( row_descriptors[ irow as usize ].0.max( sz[ 1 ] ), ); + + col_descriptors + .entry( key ) + .and_modify( | col | + { + col.0 = col.0.max( sz[ 0 ] ); + }) + .or_insert_with( || + { + col_order.push( key ); + ( sz[ 0 ], l ) + // let title = if is_title { Some( val.as_ref() ) } else { None }; + // ( title, sz[ 0 ], l ) + }); + + return Some( ( key, ( val, sz ) ) ); } ) .collect(); - all_rows.push( fields ); - } + data.push( fields ); + - for row in &all_rows + }; + + // process header first + + if let Some( header ) = table.header() { - for ( i, cell ) in row.iter().enumerate() + 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 ) | { - if col_widths.len() <= i - { - col_widths.push( cell.len() ); - } - else if cell.len() > col_widths[ i ] - { - col_widths[ i ] = cell.len(); - } - } + // let title_str : Cow< '_, str > = Cow::Owned( format!( "{}", title ) ); + ( key, Cow::Borrowed( title ) ) + }); + + row_add( &mut row2 ); } - // Write the header if provided - if let Some( header ) = self.header() + // Collect rows + // key, string, size, + for row in rows { - let mut i = 0; - for ( _key, title ) in header - { - if i > 0 + // assert!( row.cells().len() <= usize::MAX, "Row of a table has too many cells" ); + + let mut row2 = row + .cells() + .map + ( + | ( key, val ) | { - write!( f.buf, "{}", separator )?; + + let val = match val.0 + { + Some( val ) => + { + val + } + None => + { + Cow::Borrowed( "" ) + } + }; + + return ( key, val ); } - write!( f.buf, "{:^width$}", format!( "{:?}", title ), width = col_widths[ i ] )?; - // write!( f.buf, "{:?}", title )?; - i += 1; - } - writeln!( f.buf )?; + ); + + row_add( &mut row2 ); + } - // dbg!( &col_widths ); + // cook slices multi-matrix + + let mut slices_dim = [ 1, mcells[ 0 ], mcells[ 1 ] + ( if has_header { 1 } else { 0 } ) ]; + slices_dim[ 0 ] = row_descriptors + .iter() + .fold( 0, | acc : usize, e | acc.max( e.0 ) ) + ; + + let slices_len = slices_dim[ 0 ] * slices_dim[ 1 ] * slices_dim[ 2 ]; + let slices : Vec< &str > = vec![ "" ; slices_len ]; + + let mut x = FormatExtract::< '_, CellKey > + { + mcells, + col_order, + 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; - // Write rows with proper alignment - for row in &all_rows + for row_data in x.data.iter() { - let mut i = 0; - for cell in row + + irow += 1; + + for ( icol, k ) in x.col_order.iter().enumerate() { - if i > 0 + let cell = &row_data[ k ]; + string::lines( cell.0.as_ref() ) + .enumerate() + .for_each( | ( layer, s ) | { - write!( f.buf, "{}", separator )?; - } - write!( f.buf, "{:^width$}", cell, width = col_widths[ i ] )?; - i += 1; + let md_index = [ layer, icol, irow as usize ]; + slices[ x.slices_dim.md_offset( md_index ) ] = s; + }) + ; } - writeln!( f.buf )?; + } - // // Write rows with proper alignment - // for row in all_rows - // { - // let formatted_row : Vec< String > = row - // .iter() - // .enumerate() - // .map( | ( i, cell ) | format!( "{:?^width$}", cell, width = col_widths[ i ] ) ) - // .collect(); - // writeln!( f.buf, "{}", formatted_row.join( separator ) )?; - // } - - Ok(()) + std::mem::swap( &mut x.slices, &mut slices ); + + return callback( &x ); } + } } @@ -228,6 +528,14 @@ pub mod own use super::*; #[ doc( inline ) ] pub use orphan::*; + + #[ doc( inline ) ] + pub use private:: + { + All, + No, + }; + } /// Orphan namespace of the module. @@ -244,6 +552,7 @@ pub mod orphan pub mod exposed { use super::*; + pub use super::super::print; #[ doc( inline ) ] pub use private:: diff --git a/module/core/format_tools/src/format/string.rs b/module/core/format_tools/src/format/string.rs new file mode 100644 index 0000000000..21c750873d --- /dev/null +++ b/module/core/format_tools/src/format/string.rs @@ -0,0 +1,220 @@ +//! +//! String tools. +//! + +// xxx : move to crate string_tools + +/// Internal namespace. +pub( crate ) mod private +{ + + // use crate::*; + + /// Returns the size of the text in `src` as a `[ width, height ]` array. + /// + /// This function calculates the dimensions of the input text, where the width is defined + /// as the length of the longest line, and the height is the total number of lines. It + /// handles various edge cases, including empty strings and strings with trailing newlines, + /// to ensure accurate dimension calculation. + /// + /// # Arguments + /// + /// * `src` - A string slice or any type that can be referenced as a string. This allows + /// for flexibility in passing different string-like types. + /// + /// # Returns + /// + /// A `[usize; 2]` array representing the dimensions of the text: + /// - `width`: The length of the longest line in the text. + /// - `height`: The total number of lines in the text. + /// + /// # Nuances + /// + /// - **Empty Strings**: If the input string is empty, the function returns `[0, 1]` + /// because there is one line with a width of zero. + /// - **Trailing Newlines**: If the input string ends with a newline character, it is + /// treated as having an additional empty line at the end. + /// - **Empty Lines**: Empty lines within the text are counted as lines with a width of zero. + /// + /// # Examples + /// + /// ``` + /// let text = "Hello\nWorld\nThis is a test"; + /// let dimensions = format_tools::string::size( text ); + /// assert_eq!( dimensions, [ 14, 3 ] ); + /// ``` + /// + /// In this example, the function returns `[ 14, 3 ]` because the longest line ( "This is a test" ) + /// has 14 characters, and there are 3 lines in total. + /// + /// ``` + /// let text = ""; + /// let dimensions = format_tools::string::size( text ); + /// assert_eq!( dimensions, [ 0, 1 ] ); + /// ``` + /// + /// Here, the function returns `[0, 1]` because the input is an empty string, which is considered + /// as a single line with zero width. + /// + /// ``` + /// let text = "Line 1\n\nLine 3\n"; + /// let dimensions = format_tools::string::size( text ); + /// assert_eq!( dimensions, [ 6, 4 ] ); + /// ``` + /// + /// In this example, the function returns `[ 6, 4 ]` because the longest line ( "Line 1" or "Line 3" ) + /// has 6 characters, there are 4 lines in total, including the empty line and the trailing newline. + + pub fn size< S : AsRef< str > >( src : S ) -> [ usize ; 2 ] + { + let text = src.as_ref(); + let mut height = 0; + let mut width = 0; + + for line in lines( text ) + { + height += 1; + let line_length = line.chars().count(); + 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. + /// It is an enhancement over the standard `str::lines()` method, as it handles trailing + /// newlines by returning an additional empty line if the input string ends with a newline. + /// + /// # Arguments + /// + /// * `src` - A reference to a type that can be converted to a string slice. This allows + /// for flexibility in passing various string-like types. + /// + /// # Returns + /// + /// An iterator of type `Lines` that yields each line as a `&str`. + /// + /// # Examples + /// + /// ``` + /// let text = "Hello\nWorld\n"; + /// let mut lines = format_tools::string::lines( text ); + /// assert_eq!( lines.next(), Some( "Hello" ) ); + /// assert_eq!( lines.next(), Some( "World" ) ); + /// assert_eq!( lines.next(), Some( "" ) ); + /// assert_eq!( lines.next(), None ); + /// ``` + pub fn lines< S : AsRef< str > + ?Sized >( src : & S ) -> Lines< '_ > + { + Lines::new( src.as_ref() ) + } + + /// An iterator over the lines of a string slice. + /// + /// This struct implements the `Iterator` trait, allowing you to iterate over the lines + /// of a string. It enhances the standard `str::Lines` iterator by handling trailing + /// newlines, ensuring that an additional empty line is returned if the input string + /// ends with a newline character. + /// ``` + #[ derive( Debug ) ] + pub struct Lines< 'a > + { + lines : std::str::Lines< 'a >, + has_trailing_newline : bool, + finished : bool, + } + impl< 'a > Lines< 'a > + { + fn new( input : &'a str ) -> Self + { + let has_trailing_newline = input.len() == 0 || input.ends_with( '\n' ); + Lines + { + lines : input.lines(), + has_trailing_newline, + finished : false, + } + } + } + + impl< 'a > Iterator for Lines< 'a > + { + type Item = &'a str; + + fn next( &mut self ) -> Option< Self::Item > + { + if self.finished + { + return None; + } + + match self.lines.next() + { + Some( line ) => Some( line ), + None => + { + if self.has_trailing_newline + { + self.finished = true; + Some( "" ) + } + else + { + None + } + } + } + } + } + +} + +#[ 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:: + { + size, + lines, + Lines, + }; + +} + +/// Orphan namespace of the module. +#[ allow( unused_imports ) ] +pub mod orphan +{ + use super::*; + #[ doc( inline ) ] + pub use exposed::*; +} + +/// Exposed namespace of the module. +#[ allow( unused_imports ) ] +pub mod exposed +{ + use super::*; + pub use super::super::string; +} + +/// Prelude to use essentials: `use my_module::prelude::*`. +#[ allow( unused_imports ) ] +pub mod prelude +{ + use super::*; +} diff --git a/module/core/format_tools/src/format/table.rs b/module/core/format_tools/src/format/table.rs index 500123b7e7..0dec5e7e0e 100644 --- a/module/core/format_tools/src/format/table.rs +++ b/module/core/format_tools/src/format/table.rs @@ -7,78 +7,236 @@ pub( crate ) mod private { use crate::*; - use core::fmt; - use std::borrow::Cow; + use core:: + { + fmt, + borrow::Borrow, + }; + // use std::borrow::Cow; use reflect_tools:: { IteratorTrait, Fields, }; - // == + // = - /// A trait for iterating over all rows of a table. - pub trait TableSize< 'a > + /// Trait for types used as keys of rows in table-like structures. + /// + + pub trait RowKey { - /// Returns size of a table. - fn table_size( &'a self ) -> [ usize ; 2 ]; } - /// A trait for iterating over all rows of a table. - pub trait TableRows< 'a, RowKey, Row, CellKey, Cell > + impl< T > RowKey for T where - Row : Clone + for< 'cell > Cells< 'cell, CellKey, Cell, () > + 'a, - Cell : fmt::Debug + 'a, - Cell : std::borrow::ToOwned + ?Sized, + T : ?Sized, { - /// Returns an iterator over all rows of the table. - fn rows( &'a self ) -> impl IteratorTrait< Item = Row >; } - /// Trait returning headers of a table if any. - pub trait TableHeader< 'a, CellKey, Title > + /// Trait for types used as keys of cells in table-like structures. + /// + /// The `CellKey` trait aggregates necessary bounds for keys, ensuring they support + /// debugging, equality comparison, and hashing. + /// + + pub trait CellKey where - Title : fmt::Debug, + Self : fmt::Debug + std::cmp::Eq + std::hash::Hash + Borrow< str >, + { + } + + impl< T > CellKey for T + where + T : fmt::Debug + std::cmp::Eq + std::hash::Hash + Borrow< str > + ?Sized, { - /// Returns an iterator over all fields of the specified type within the entity. - fn header( &'a self ) -> Option< impl IteratorTrait< Item = ( CellKey, Title ) > >; } + /// Trait for types representing table cell content. + /// + /// `CellRepr` aggregates necessary bounds for types used as cell representations, + /// ensuring they are copyable and have a static lifetime. + /// + + pub trait CellRepr + where + Self : Copy + 'static, + { + } + + impl< T > CellRepr for T + where + T : Copy + 'static + ?Sized, + { + } + + // = + /// A trait for iterating over all cells of a row. - pub trait Cells< 'a, CellKey, Cell, Kind > + pub trait Cells< CellKey, CellRepr > where - Cell : fmt::Debug + 'a, - // &'a Cell : Clone, - Cell : std::borrow::ToOwned + ?Sized, - Kind : Copy + 'static, + CellRepr : table::CellRepr, + CellKey : table::CellKey + ?Sized, { /// Returns an iterator over all cells of the row. - fn cells( &'a self ) -> impl IteratorTrait< Item = ( CellKey, MaybeAs< 'a, Cell, Kind > ) > - // fn cells( &'a self ) -> impl IteratorTrait< Item = ( CellKey, Option< Cell > ) > + fn cells< 'a, 'b >( &'a self ) -> impl IteratorTrait< Item = ( &'b CellKey, MaybeAs< 'b, str, CellRepr > ) > + where + 'a : 'b, + CellKey : 'b, ; } - // == + impl< Row, CellKey, CellRepr > Cells< CellKey, CellRepr > + for Row + where + CellKey : table::CellKey + ?Sized, + for< 'k, 'v > + Row : Fields + < + &'k CellKey, + MaybeAs< 'v, str, CellRepr >, + Key< 'k > = &'k CellKey, + Val< 'v > = MaybeAs< 'v, str, CellRepr >, + > + 'k + 'v, + // for< 'v > MaybeAs< 'v, str, CellRepr > : From + // < + // MaybeAs< 'v, str, CellRepr >, + // >, + CellRepr : table::CellRepr, + { - impl< 'a, T, RowKey, Row, CellKey, Cell, Title > TableSize< 'a > - for AsTable< 'a, T, RowKey, Row, CellKey, Cell, Title > + fn cells< 'a, 'b >( &'a self ) -> impl IteratorTrait< Item = ( &'b CellKey, MaybeAs< 'b, str, CellRepr > ) > + where + 'a : 'b, + CellKey : 'b, + { + self.fields().map + ( + move | ( key, cell ) | + { + ( key, cell ) + // ( key.clone(), cell.clone() ) + // ( key, cell.into() ) + } + ) + } + + } + + // = + + /// Trait for iterating over rows in a table. + /// + /// `TableRows` provides an interface to access all rows in a table, + /// allowing iteration over the data structure. + /// + /// # Associated Types + /// + /// - `RowKey`: The type used to identify each row. + /// + /// - `Row`: The type representing a row, which must implement `Cells` + /// for the specified `CellKey` and `CellRepr`. + /// + /// - `CellKey`: The type used to identify cells within a row, requiring + /// implementation of the `Key` trait. + /// + /// - `CellRepr`: The type representing the content of a cell, requiring + /// implementation of the `CellRepr` trait. + /// + /// # Required Methods + /// + /// - `rows(&self) -> impl IteratorTrait`: + /// Returns an iterator over all rows in the table. + pub trait TableRows + { + /// + /// The type used to identify each row. + type RowKey; + /// + /// The type representing a row, which must implement `Cells` + /// for the specified `CellKey` and `CellRepr`. + type Row : Cells< Self::CellKey, Self::CellRepr >; + /// + /// The type used to identify cells within a row, requiring + /// implementation of the `Key` trait. + type CellKey : table::CellKey + ?Sized; + /// + /// The type representing the content of a cell, requiring + /// implementation of the `CellRepr` trait. + type CellRepr : table::CellRepr; + + /// Returns an iterator over all rows of the table. + fn rows< 'a >( & 'a self ) -> impl IteratorTrait< Item = & 'a Self::Row > + where + Self::Row : 'a; + } + + + impl< T, RowKey, Row, CellKey, CellRepr > + TableRows<> + for AsTable< '_, T, RowKey, Row, CellKey, CellRepr > where - Self : TableRows< 'a, RowKey, Row, CellKey, Cell >, - Row : Clone + for< 'cell > Cells< 'cell, CellKey, Cell, () > + 'a, - Title : fmt::Debug, - Cell : fmt::Debug + 'a, - Cell : std::borrow::ToOwned + ?Sized, - CellKey : fmt::Debug + Clone, - { - fn table_size( &'a self ) -> [ usize ; 2 ] + + for< 'k, 'v > T : Fields + < + RowKey, + &'v Row, + Key< 'k > = RowKey, + Val< 'v > = &'v Row, + > + 'k + 'v, + + RowKey : table::RowKey, + Row : Cells< CellKey, CellRepr >, + CellKey : table::CellKey + ?Sized, + CellRepr : table::CellRepr, + { + type RowKey = RowKey; + type Row = Row; + type CellKey = CellKey; + type CellRepr = CellRepr; + + fn rows< 'a >( &'a self ) -> impl IteratorTrait< Item = &'a Self::Row > + where + Self::Row : 'a { - let mut rows = self.rows(); + self.as_ref().fields() + .filter_map( move | ( _k, e ) : ( RowKey, &Row ) | + { + Some( e ) + }) + .collect::< Vec< _ > >().into_iter() + } + + } + + // = + + /// A trait for iterating over all rows of a table. + pub trait TableSize + { + /// Returns multi-dimensional size of a table. + fn mcells( &self ) -> [ usize ; 2 ]; + } + + impl< T, RowKey, Row, CellKey, CellRepr > TableSize + for AsTable< '_, T, RowKey, Row, CellKey, CellRepr > + where + Self : TableRows< RowKey = RowKey, Row = Row, CellKey = CellKey, CellRepr = CellRepr >, + RowKey : table::RowKey, + Row : Cells< CellKey, CellRepr >, + CellKey : table::CellKey + ?Sized, + CellRepr : table::CellRepr, + { + fn mcells( &self ) -> [ usize ; 2 ] + { + let rows = self.rows(); let nrows = rows.len(); - let row = rows.next(); - if let Some( row ) = row + let row = rows.clone().next(); + if let Some( row2 ) = row { - let ncells = row.cells().len(); - [ nrows, ncells ] + let cit = row2.cells().clone(); + let mcells = cit.len(); + [ mcells, nrows ] } else { @@ -87,90 +245,55 @@ pub( crate ) mod private } } - impl< 'a, T, RowKey, Row, CellKey, Cell, Title > TableRows< 'a, RowKey, Row, CellKey, Cell > - for AsTable< 'a, T, RowKey, Row, CellKey, Cell, Title > - where - T : Fields< 'a, RowKey, Option< Cow< 'a, Row > > >, - Row : Clone + for< 'cell > Cells< 'cell, CellKey, Cell, () > + 'a, - Title : fmt::Debug, - Cell : fmt::Debug + 'a, - Cell : std::borrow::ToOwned + ?Sized, - CellKey : fmt::Debug + Clone, - { - - fn rows( &'a self ) -> impl IteratorTrait< Item = Row > - { - self.as_ref().fields().filter_map( move | ( _k, e ) | - { - match e - { - Some( e ) => Some( e.into_owned() ), - None => None, - } - }).collect::< Vec< _ > >().into_iter() - } + // = + /// Trait returning headers of a table if any. + pub trait TableHeader + { + /// The type used to identify cells within a row, requiring + /// implementation of the `Key` trait. + type CellKey : table::CellKey + ?Sized; + /// Returns an iterator over all fields of the specified type within the entity. + fn header( &self ) -> Option< impl IteratorTrait< Item = ( &Self::CellKey, &'_ str ) > >; } -// impl< 'a, T, RowKey, Row, CellKey, Cell > TableHeader< 'a, CellKey, CellKey > -// for AsTable< 'a, T, RowKey, Row, CellKey, Cell, CellKey > -// where -// Self : TableRows< 'a, RowKey, Row, CellKey, Cell >, -// Row : Clone + for< 'cell > Cells< 'cell, CellKey, Cell, () > + 'a, -// CellKey : fmt::Debug + Clone, -// Cell : fmt::Debug + 'a, -// CellKey : fmt::Debug + Clone, -// { -// -// fn header( &'a self ) -> Option< impl IteratorTrait< Item = ( CellKey, CellKey ) > > -// { -// let mut rows = self.rows(); -// let row = rows.next(); -// if let Some( row ) = row -// { -// Some -// ( -// row -// .cells() -// .map( | ( key, _title ) | ( key.clone(), key ) ) -// .collect::< Vec< _ > >() -// .into_iter() -// ) -// } -// else -// { -// None -// } -// } -// -// } - - impl< 'a, Kind, Row, CellKey, Cell > Cells< 'a, CellKey, Cell, Kind > - for Row + impl< T, RowKey, Row, CellKey, CellRepr > TableHeader + for AsTable< '_, T, RowKey, Row, CellKey, CellRepr > where - Row : Fields< 'a, CellKey, MaybeAs< 'a, Cell, Kind > > + 'a, - MaybeAs< 'a, Cell, Kind > : Clone, - Cell : fmt::Debug + Clone + 'a, - Kind : Copy + 'static, + Self : TableRows< RowKey = RowKey, Row = Row, CellKey = CellKey, CellRepr = CellRepr >, + RowKey : table::RowKey, + Row : Cells< CellKey, CellRepr >, + CellKey : table::CellKey + ?Sized, + CellRepr : table::CellRepr, { + type CellKey = CellKey; - fn cells( &'a self ) -> impl IteratorTrait< Item = ( CellKey, MaybeAs< 'a, Cell, Kind > ) > + fn header( &self ) -> Option< impl IteratorTrait< Item = ( &Self::CellKey, &'_ str ) > > { - self.fields().map - ( - move | ( key, cell ) | - { - match cell.0 - { - Some( cell ) => ( key, cell.into() ), - None => ( key, MaybeAs::none() ) - } - } - ) + let mut rows = self.rows(); + let row = rows.next(); + if let Some( row ) = row + { + Some + ( + row + .cells() + // .map( | ( key, _title ) | ( key.clone(), Cow::Owned( format!( "{}", key ) ) ) ) + .map( | ( key, _title ) | ( key, key.borrow() ) ) + .collect::< Vec< _ > >() + .into_iter() + ) + } + else + { + None + } } } + // = + } #[ allow( unused_imports ) ] @@ -183,6 +306,15 @@ pub mod own use super::*; #[ doc( inline ) ] pub use orphan::*; + + #[ doc( inline ) ] + pub use private:: + { + RowKey, + CellKey, + CellRepr, + }; + } /// Orphan namespace of the module. @@ -199,14 +331,15 @@ pub mod orphan pub mod exposed { use super::*; + pub use super::super::table; #[ doc( inline ) ] pub use private:: { - TableSize, + Cells, TableRows, + TableSize, TableHeader, - Cells, }; } diff --git a/module/core/format_tools/src/format/to_string.rs b/module/core/format_tools/src/format/to_string.rs index ada0f1dcef..a6e32c3bc4 100644 --- a/module/core/format_tools/src/format/to_string.rs +++ b/module/core/format_tools/src/format/to_string.rs @@ -22,6 +22,10 @@ pub( crate ) mod private #[ derive( Debug, Default, Clone, Copy ) ] pub struct WithDebug; + /// Marker type for using Debug multiline formatting. + #[ derive( Debug, Default, Clone, Copy ) ] + pub struct WithDebugMultiline; + /// Marker type for using Display formatting. #[ derive( Debug, Default, Clone, Copy ) ] pub struct WithDisplay; @@ -49,6 +53,7 @@ pub( crate ) mod private #[ inline ] fn to_string_with< 's >( &'s self ) -> Cow< 's, str > { + // println!( " - WithRef" ); Cow::Borrowed( self.as_ref() ) } } @@ -62,11 +67,25 @@ pub( crate ) mod private #[ inline ] fn to_string_with< 's >( &'s self ) -> Cow< 's, str > { - println!( " - WithDebug Ref {:?}", self ); + // println!( " - WithDebug {:?}", self ); Cow::Owned( format!( "{:?}", self ) ) } } + impl< 'a, T > ToStringWith< WithDebugMultiline > for T + where + T : fmt::Debug, + T : ?Sized, + { + /// Converts the type to a string using Debug formatting. + #[ inline ] + fn to_string_with< 's >( &'s self ) -> Cow< 's, str > + { + // println!( " - WithDebugMultiline {:#?}", self ); + Cow::Owned( format!( "{:#?}", self ) ) + } + } + impl< 'a, T > ToStringWith< WithDisplay > for T where T : 'a, @@ -77,6 +96,7 @@ pub( crate ) mod private #[ inline ] fn to_string_with< 's >( &'s self ) -> Cow< 's, str > { + // println!( " - WithDisplay {}", self ); Cow::Owned( format!( "{}", self ) ) } } @@ -84,7 +104,6 @@ pub( crate ) mod private } mod aref; -// mod aref2; #[ doc( inline ) ] #[ allow( unused_imports ) ] @@ -115,6 +134,7 @@ pub mod orphan pub use private:: { WithDebug, + WithDebugMultiline, WithDisplay, WithRef, WithWell, diff --git a/module/core/format_tools/src/format/to_string/aref.rs b/module/core/format_tools/src/format/to_string/aref.rs index 1c5ed24ba1..fa1332734d 100644 --- a/module/core/format_tools/src/format/to_string/aref.rs +++ b/module/core/format_tools/src/format/to_string/aref.rs @@ -29,12 +29,12 @@ where impl< 'a, T, How > Ref< 'a, T, How > { - /// Just a constructor. - #[ inline( always ) ] - pub fn inner( self ) -> &'a T - { - self.0.0 - } + // /// Retrive inner value. + // #[ inline( always ) ] + // pub fn inner( self ) -> &'a T + // { + // self.0.0 + // } } diff --git a/module/core/format_tools/src/lib.rs b/module/core/format_tools/src/lib.rs index bfd4a579f8..88659a9813 100644 --- a/module/core/format_tools/src/lib.rs +++ b/module/core/format_tools/src/lib.rs @@ -72,6 +72,7 @@ pub mod exposed { Fields, IteratorTrait, + _IteratorTrait, }; } diff --git a/module/core/format_tools/tests/inc/fields_test.rs b/module/core/format_tools/tests/inc/fields_test.rs index b152846e01..42ce7eb74f 100644 --- a/module/core/format_tools/tests/inc/fields_test.rs +++ b/module/core/format_tools/tests/inc/fields_test.rs @@ -7,7 +7,6 @@ use the_module:: IteratorTrait, MaybeAs, WithRef, - ref_or_debug::field, }; use std:: @@ -27,12 +26,16 @@ pub struct TestObject pub tools : Option< Vec< HashMap< String, String > > >, } -impl< 'a > Fields< 'a, &'static str, MaybeAs< 'a, str, WithRef > > +impl Fields< &'static str, MaybeAs< '_, str, WithRef > > for TestObject { - fn fields( &'a self ) -> impl IteratorTrait< Item = ( &'static str, MaybeAs< 'a, str, WithRef > ) > + type Key< 'k > = &'static str; + type Val< 'v > = MaybeAs< 'v, str, WithRef >; + + fn fields( &self ) -> impl IteratorTrait< Item = ( &'static str, MaybeAs< '_, str, WithRef > ) > { - let mut dst : Vec< ( &'static str, MaybeAs< 'a, str, WithRef > ) > = Vec::new(); + use format_tools::ref_or_display_or_debug::field; + let mut dst : Vec< ( &'static str, MaybeAs< '_, str, WithRef > ) > = Vec::new(); dst.push( field!( &self.id ) ); dst.push( field!( &self.created_at ) ); @@ -74,7 +77,7 @@ fn basic_with_ref_display_debug() }; let fields : Vec< ( &str, MaybeAs< '_, str, WithRef > ) > = - Fields::< '_, &'static str, MaybeAs< '_, str, WithRef > >::fields( &test_object ).collect(); + Fields::< &'static str, MaybeAs< '_, str, WithRef > >::fields( &test_object ).collect(); // let fields : Vec< ( &str, MaybeAs< '_, str, WithRef > ) > = test_object.fields().collect(); @@ -124,7 +127,8 @@ fn test_vec_fields() }, ]; - let fields : Vec< _ > = test_objects.fields().collect(); + // let fields : Vec< _ > = test_objects.fields().collect(); + let fields : Vec< _ > = Fields::< usize, Option< _ > >::fields( &test_objects ).collect(); assert_eq!( fields.len(), 2 ); assert_eq!( fields[ 0 ].0, 0 ); assert_eq!( fields[ 1 ].0, 1 ); diff --git a/module/core/format_tools/tests/inc/md_math_test.rs b/module/core/format_tools/tests/inc/md_math_test.rs new file mode 100644 index 0000000000..ac8c8c3fbd --- /dev/null +++ b/module/core/format_tools/tests/inc/md_math_test.rs @@ -0,0 +1,17 @@ +#[ allow( unused_imports ) ] +use super::*; + +#[ test ] +fn md_offset_basic() +{ + use the_module::md_math::MdOffset; + + let md_size = [ 10, 100, 1000 ]; + let md_index = [ 2, 3, 4 ]; + let got = md_size.md_offset( md_index ); + let exp = 2 + 3 * 10 + 4 * 10 * 100; + assert_eq!( got, exp ); + + // 2 * 100'000 + +} diff --git a/module/core/format_tools/tests/inc/mod.rs b/module/core/format_tools/tests/inc/mod.rs index fa8a4a9ca3..c71b386681 100644 --- a/module/core/format_tools/tests/inc/mod.rs +++ b/module/core/format_tools/tests/inc/mod.rs @@ -9,8 +9,11 @@ mod fundamental use super::*; mod fields_test; + mod md_math_test; + mod print_test; + mod print_without_wrap; + mod string_test; mod to_string_test; mod to_string_with_fallback_test; - mod print_test; } diff --git a/module/core/format_tools/tests/inc/print_test.rs b/module/core/format_tools/tests/inc/print_test.rs index 9e7a0234d6..ac0b1537b5 100644 --- a/module/core/format_tools/tests/inc/print_test.rs +++ b/module/core/format_tools/tests/inc/print_test.rs @@ -3,6 +3,7 @@ use super::*; use the_module:: { + // print, Fields, IteratorTrait, AsTable, @@ -10,17 +11,15 @@ use the_module:: TableSize, TableRows, TableHeader, - // TableFormatter, Context, WithRef, - ref_or_debug::field, MaybeAs, }; use std:: { collections::HashMap, - borrow::Cow, + // borrow::Cow, }; /// Struct representing a test object with various fields. @@ -33,12 +32,17 @@ pub struct TestObject pub tools : Option< Vec< HashMap< String, String > > >, } -impl< 'a > Fields< 'a, &'static str, MaybeAs< 'a, str, WithRef > > +impl Fields< &'_ str, MaybeAs< '_, str, WithRef > > for TestObject { - fn fields( &'a self ) -> impl IteratorTrait< Item = ( &'static str, MaybeAs< 'a, str, WithRef > ) > + type Key< 'k > = &'k str; + type Val< 'v > = MaybeAs< 'v, str, WithRef >; + + fn fields( &self ) -> impl IteratorTrait< Item = ( &'_ str, MaybeAs< '_, str, WithRef > ) > { - let mut dst : Vec< ( &'static str, MaybeAs< 'a, str, WithRef > ) > = Vec::new(); + use format_tools::ref_or_display_or_debug_multiline::field; + // use format_tools::ref_or_display_or_debug::field; + let mut dst : Vec< ( &'_ str, MaybeAs< '_, str, WithRef > ) > = Vec::new(); dst.push( field!( &self.id ) ); dst.push( field!( &self.created_at ) ); @@ -55,40 +59,15 @@ for TestObject dst.into_iter() } + } -// impl< 'a > Fields< 'a, &'static str, Option< Cow< 'a, String > > > -// for TestObject -// { -// fn fields( &'a self ) -> impl IteratorTrait< Item = ( &'static str, Option< Cow< 'a, String > > ) > -// { -// let mut vec : Vec< ( &'static str, Option< Cow< 'a, String > > ) > = Vec::new(); -// -// vec.push( ( "id", Some( Cow::Borrowed( &self.id ) ) ) ); -// vec.push( ( "created_at", Some( Cow::Owned( self.created_at.to_string() ) ) ) ); -// vec.push( ( "file_ids", Some( Cow::Owned( format!( "{:?}", self.file_ids ) ) ) ) ); -// -// if let Some( tools ) = &self.tools -// { -// vec.push( ( "tools", Some( Cow::Owned( format!( "{:?}", tools ) ) ) ) ); -// } -// else -// { -// vec.push( ( "tools", None ) ); -// } // -// vec.into_iter() -// } -// } -#[ test ] -fn test_table_to_string() -// where - // for< 'a > AsTable< 'a, Vec< TestObject >, usize, TestObject, &'static str, String, &'static str > : TableFormatter< 'a >, +fn test_objects_gen() -> Vec< TestObject > { - use the_module::TableToString; - let test_objects = vec! + vec! [ TestObject { @@ -101,7 +80,7 @@ fn test_table_to_string() { id : "2".to_string(), created_at : 13, - file_ids : vec![ "file3".to_string(), "file4".to_string() ], + file_ids : vec![ "file3".to_string(), "file4\nmore details".to_string() ], tools : Some ( vec! @@ -119,40 +98,160 @@ fn test_table_to_string() ] ), }, - ]; - - let cells = Cells::< '_, &'static str, str, WithRef >::cells( &test_objects[ 0 ] ); - // assert_eq!( cells.len(), 4 ); - // let cells = Cells::< '_, &'static str, MaybeAs< '_, str, WithRef > >::cells( &test_objects[ 1 ] ); - // assert_eq!( cells.len(), 4 ); - // drop( cells ); - -// let as_table : AsTable< '_, Vec< TestObject >, usize, TestObject, &str, String, &str > = AsTable::new( &test_objects ); -// let size = TableSize::< '_ >::table_size( &as_table ); -// assert_eq!( size, [ 2, 4 ] ); -// let rows = TableRows::rows( &as_table ); -// assert_eq!( rows.len(), 2 ); -// // dbg!( rows.collect::< Vec< _ > >() ); -// let header = TableHeader::header( &as_table ); -// assert!( header.is_some() ); -// let header = header.unwrap(); -// assert_eq!( header.len(), 4 ); -// assert_eq!( header.collect::< Vec< _ > >(), vec![ ( "id", "id" ), ( "created_at", "created_at" ), ( "file_ids", "file_ids" ), ( "tools", "tools" ) ] ); -// // dbg!( header.collect::< Vec< _ > >() ); + ] + +} + +// + +#[ test ] +fn table_to_string() +// where + // for< 'a > AsTable< 'a, Vec< TestObject >, usize, TestObject, &'static str, String, &'static str > : TableFormatter< 'a >, +{ + use the_module::TableToString; + let test_objects = test_objects_gen(); + + let cells = Cells::< str, WithRef >::cells( &test_objects[ 0 ] ); + assert_eq!( cells.len(), 4 ); + let cells = Cells::< str, WithRef >::cells( &test_objects[ 1 ] ); + assert_eq!( cells.len(), 4 ); + drop( cells ); + + let as_table : AsTable< '_, Vec< TestObject >, usize, TestObject, str, WithRef > = AsTable::new( &test_objects ); + let mcells = TableSize::mcells( &as_table ); + assert_eq!( mcells, [ 4, 2 ] ); + let rows = TableRows::rows( &as_table ); + assert_eq!( rows.len(), 2 ); + dbg!( rows.collect::< Vec< _ > >() ); + let header = TableHeader::header( &as_table ); + assert!( header.is_some() ); + let header = header.unwrap(); + assert_eq!( header.len(), 4 ); + assert_eq!( header.clone().collect::< Vec< _ > >(), vec! + [ + ( "id", "id" ), + ( "created_at", "created_at" ), + ( "file_ids", "file_ids" ), + ( "tools", "tools" ), + ]); + dbg!( header.collect::< Vec< _ > >() ); + + let mut output = String::new(); + let mut context = Context::new( &mut output, Default::default() ); + // let mut context : Context< '_, print::All > = Context::new( &mut output, Default::default() ); + let got = the_module::TableFormatter::fmt( &as_table, &mut context ); + assert!( got.is_ok() ); + println!( "{}", &output ); + + // with explicit arguments + + let as_table : AsTable< '_, Vec< TestObject >, usize, TestObject, str, WithRef > = AsTable::new( &test_objects ); + let table_string = as_table.table_to_string(); + println!( "\ntable_string\n{table_string}" ); + assert!( table_string.contains( "id" ) ); + assert!( table_string.contains( "created_at" ) ); + assert!( table_string.contains( "file_ids" ) ); + assert!( table_string.contains( "tools" ) ); + + // without explicit arguments + + println!( "" ); + let as_table = AsTable::new( &test_objects ); + let table_string = as_table.table_to_string(); + assert!( table_string.contains( "id" ) ); + assert!( table_string.contains( "created_at" ) ); + assert!( table_string.contains( "file_ids" ) ); + assert!( table_string.contains( "tools" ) ); + println!( "\ntable_string\n{table_string}" ); + +} + // -// let mut output = String::new(); -// let mut formatter = Context::new( &mut output, Default::default() ); -// let got = the_module::TableFormatter::fmt( &as_table, &mut formatter ); -// assert!( got.is_ok() ); -// println!( "{}", &output ); + +#[ test ] +fn custom_formatter() +{ + // use the_module::TableToString; + let test_objects = test_objects_gen(); + + let mut output = String::new(); + let mut formatter = the_module::Styles::default(); + + formatter.cell_prefix = "( ".into(); + formatter.cell_postfix = " )".into(); + formatter.cell_separator = "|".into(); + + formatter.row_prefix = ">".into(); + formatter.row_postfix = "<".into(); + formatter.row_separator = "\n".into(); + + let as_table = AsTable::new( &test_objects ); + // let mut context : Context< '_, print::All > = Context::new( &mut output, formatter ); + let mut context = Context::new( &mut output, formatter ); + let result = the_module::TableFormatter::fmt( &as_table, &mut context ); + assert!( result.is_ok() ); + + println!( "\noutput\n{output}" ); + assert!( output.contains( "id" ) ); + assert!( output.contains( "created_at" ) ); + assert!( output.contains( "file_ids" ) ); + assert!( output.contains( "tools" ) ); + + let exp = r#">( id )|( created_at )|( file_ids )|( tools )< +>( 1 )|( 1627845583 )|( [ )|( )< +>( )|( )|( "file1", )|( )< +>( )|( )|( "file2", )|( )< +>( )|( )|( ] )|( )< +>( 2 )|( 13 )|( [ )|( [ )< +>( )|( )|( "file3", )|( { )< +>( )|( )|( "file4\nmore details", )|( "tool1": "value1", )< +>( )|( )|( ] )|( }, )< +>( )|( )|( )|( { )< +>( )|( )|( )|( "tool2": "value2", )< +>( )|( )|( )|( }, )< +>( )|( )|( )|( ] )<"#; + + a_id!( output.as_str(), exp ); + + +} + // -// let as_table : AsTable< '_, Vec< TestObject >, usize, TestObject, &str, String, &str > = AsTable::new( &test_objects ); -// let table_string = as_table.table_to_string(); -// assert!( table_string.contains( "id" ) ); -// assert!( table_string.contains( "created_at" ) ); -// assert!( table_string.contains( "file_ids" ) ); -// assert!( table_string.contains( "tools" ) ); + +#[ test ] +fn filter_col() +{ + let test_objects = test_objects_gen(); + + let mut output = String::new(); + let mut formatter = the_module::Styles::default(); + + // formatter.filter_col = print::No; + // xxx : implement + + let as_table = AsTable::new( &test_objects ); + // let mut context : Context< '_, _ > = Context::new( &mut output, formatter ); + let mut context = Context::new( &mut output, formatter ); + let result = the_module::TableFormatter::fmt( &as_table, &mut context ); + assert!( result.is_ok() ); + + let exp = r#"│ id │ created_at │ file_ids │ tools │ +│ 1 │ 1627845583 │ [ │ │ +│ │ │ "file1", │ │ +│ │ │ "file2", │ │ +│ │ │ ] │ │ +│ 2 │ 13 │ [ │ [ │ +│ │ │ "file3", │ { │ +│ │ │ "file4\nmore details", │ "tool1": "value1", │ +│ │ │ ] │ }, │ +│ │ │ │ { │ +│ │ │ │ "tool2": "value2", │ +│ │ │ │ }, │ +│ │ │ │ ] │"#; + + a_id!( output.as_str(), exp ); } -// xxx + diff --git a/module/core/format_tools/tests/inc/print_without_wrap.rs b/module/core/format_tools/tests/inc/print_without_wrap.rs new file mode 100644 index 0000000000..f6a0d474bb --- /dev/null +++ b/module/core/format_tools/tests/inc/print_without_wrap.rs @@ -0,0 +1,161 @@ +#[ allow( unused_imports ) ] +use super::*; + +use the_module:: +{ + // print, + Fields, + IteratorTrait, + AsTable, + IntoAsTable, + Cells, + TableSize, + TableRows, + TableHeader, + Context, + WithRef, + MaybeAs, +}; + +use std:: +{ + collections::HashMap, + // borrow::Cow, +}; + +/// Struct representing a test object with various fields. +#[ derive( Clone, Debug ) ] +pub struct TestObject +{ + pub id : String, + pub created_at : i64, + pub file_ids : Vec< String >, + pub tools : Option< Vec< HashMap< String, String > > >, +} + +impl Fields< &'_ str, MaybeAs< '_, str, WithRef > > +for TestObject +{ + type Key< 'k > = &'k str; + type Val< 'v > = MaybeAs< 'v, str, WithRef >; + + fn fields( &self ) -> impl IteratorTrait< Item = ( &'_ str, MaybeAs< '_, str, WithRef > ) > + { + use format_tools::ref_or_display_or_debug_multiline::field; + // use format_tools::ref_or_display_or_debug::field; + let mut dst : Vec< ( &'_ str, MaybeAs< '_, str, WithRef > ) > = Vec::new(); + + dst.push( field!( &self.id ) ); + dst.push( field!( &self.created_at ) ); + dst.push( field!( &self.file_ids ) ); + + if let Some( tools ) = &self.tools + { + dst.push( field!( tools ) ); + } + else + { + dst.push( ( "tools", MaybeAs::none() ) ); + } + + dst.into_iter() + } + +} + +// xxx : finish or remove + +// let as_table : AsTable< '_, Vec< TestObject >, usize, TestObject, str, WithRef > = AsTable::new( &test_objects ); + +// impl IntoAsTable +// for Vec< TestObject > +// { +// +// type Table = Self; +// type RowKey = usize; +// type Row = TestObject; +// type CellKey = str; +// type CellRepr = WithRef; +// +// fn as_table( &self ) -> AsTable< '_, Self::Table, Self::RowKey, Self::Row, Self::CellKey, Self::CellRepr > +// { +// *self +// } +// +// } + +// + +fn test_objects_gen() -> Vec< TestObject > +{ + + vec! + [ + TestObject + { + id : "1".to_string(), + created_at : 1627845583, + file_ids : vec![ "file1".to_string(), "file2".to_string() ], + tools : None + }, + TestObject + { + id : "2".to_string(), + created_at : 13, + file_ids : vec![ "file3".to_string(), "file4\nmore details".to_string() ], + tools : Some + ( + vec! + [ + { + let mut map = HashMap::new(); + map.insert( "tool1".to_string(), "value1".to_string() ); + map + }, + { + let mut map = HashMap::new(); + map.insert( "tool2".to_string(), "value2".to_string() ); + map + } + ] + ), + }, + ] + +} + +// + +#[ test ] +fn without_wrap() +{ + let test_objects = test_objects_gen(); + + let mut output = String::new(); + let formatter = the_module::Styles::default(); + + let as_table = AsTable::new( &test_objects ); + // let mut context : Context< '_, _ > = Context::new( &mut output, formatter ); + let mut context = Context::new( &mut output, formatter ); + let result = the_module::TableFormatter::fmt( &as_table, &mut context ); + assert!( result.is_ok() ); + + let exp = r#"│ id │ created_at │ file_ids │ tools │ +│ 1 │ 1627845583 │ [ │ │ +│ │ │ "file1", │ │ +│ │ │ "file2", │ │ +│ │ │ ] │ │ +│ 2 │ 13 │ [ │ [ │ +│ │ │ "file3", │ { │ +│ │ │ "file4\nmore details", │ "tool1": "value1", │ +│ │ │ ] │ }, │ +│ │ │ │ { │ +│ │ │ │ "tool2": "value2", │ +│ │ │ │ }, │ +│ │ │ │ ] │"#; + + a_id!( output.as_str(), exp ); + +} + +// diff --git a/module/core/format_tools/tests/inc/string_test.rs b/module/core/format_tools/tests/inc/string_test.rs new file mode 100644 index 0000000000..044cdc4b91 --- /dev/null +++ b/module/core/format_tools/tests/inc/string_test.rs @@ -0,0 +1,144 @@ +#[ allow( unused_imports ) ] +use super::*; + +#[ test ] +fn empty_string() +{ + use the_module::string; + let input = ""; + let exp = [ 0, 1 ]; + let got = string::size( input ); + assert_eq!( got, exp ); +} + +#[ test ] +fn single_line_no_newline() +{ + use the_module::string; + + let input = "Hello, World!"; + let exp = [ 13, 1 ]; + let got = string::size( input ); + assert_eq!( got, exp ); + + let input = "[\"file1\", \"file2\"]"; + let exp = [ 18, 1 ]; + let got = string::size( input ); + assert_eq!( got, exp ); + +} + +#[ test ] +fn single_line_with_newline() +{ + use the_module::string; + let input = "Hello, World!\n"; + let exp = [ 13, 2 ]; + let got = string::size( input ); + assert_eq!( got, exp ); +} + +#[ test ] +fn multiple_lines_varying_lengths() +{ + use the_module::string; + let input = "Hello\nWorld!\nThis is a test."; + let exp = [ 15, 3 ]; + let got = string::size( input ); + assert_eq!( got, exp ); +} + +#[ test ] +fn only_newlines() +{ + use the_module::string; + let input = "\n\n\n"; + let exp = [ 0, 4 ]; + let got = string::size( input ); + assert_eq!( got, exp ); +} + +#[ test ] +fn very_long_lines() +{ + use the_module::string; + let input = "a".repeat( 1000 ); + let exp = [ 1000, 1 ]; + let got = string::size( input ); + assert_eq!( got, exp ); +} + +#[ test ] +fn special_characters_whitespace() +{ + use the_module::string; + let input = " \t\n \t\n"; + let exp = [ 2, 3 ]; + let got = string::size( input ); + assert_eq!( got, exp ); +} + +#[ test ] +fn assumption_str_lines_skip_the_last_line() +{ + + let src = "abc"; + let got : Vec< &str > = src.lines().collect(); + let exp = vec![ "abc" ]; + assert_eq!( got, exp ); + + let src = ""; + let got : Vec< &str > = src.lines().collect(); + let exp : Vec< &str > = vec![]; + // let exp = vec![ "" ]; // should be + assert_eq!( got, exp ); + + let src = "\n"; + let got : Vec< &str > = src.lines().collect(); + let exp = vec![ "" ]; + // let exp = vec![ "", "" ]; // should be + assert_eq!( got, exp ); + + let src = "a\nb"; + let got : Vec< &str > = src.lines().collect(); + let exp = vec![ "a", "b" ]; + assert_eq!( got, exp ); + + let src = "\na\nb\n"; + let got : Vec< &str > = src.lines().collect(); + let exp = vec![ "", "a", "b" ]; + // let exp = vec![ "", "a", "b", "" ]; should be + assert_eq!( got, exp ); + +} + +#[ test ] +fn lines_basic() +{ + use the_module::string; + + let src = "abc"; + let got : Vec< &str > = string::lines( src ).collect(); + let exp = vec![ "abc" ]; + assert_eq!( got, exp ); + + let src = ""; + let got : Vec< &str > = string::lines( src ).collect(); + let exp = vec![ "" ]; + assert_eq!( got, exp ); + + let src = "\n"; + let got : Vec< &str > = string::lines( src ).collect(); + let exp = vec![ "", "" ]; + assert_eq!( got, exp ); + + let src = "a\nb"; + let got : Vec< &str > = string::lines( src ).collect(); + let exp = vec![ "a", "b" ]; + assert_eq!( got, exp ); + + let src = "\na\nb\n"; + let got : Vec< &str > = string::lines( src ).collect(); + let exp = vec![ "", "a", "b", "" ]; + assert_eq!( got, exp ); +} diff --git a/module/core/format_tools/tests/tests.rs b/module/core/format_tools/tests/tests.rs index df11f3457e..eae1668ea0 100644 --- a/module/core/format_tools/tests/tests.rs +++ b/module/core/format_tools/tests/tests.rs @@ -1,4 +1,4 @@ -#![ feature( trace_macros ) ] +// #![ feature( trace_macros ) ] #[ allow( unused_imports ) ] use format_tools as the_module; diff --git a/module/core/former/tests/inc/former_tests/name_collision_context.rs b/module/core/former/tests/inc/former_tests/name_collision_context.rs deleted file mode 100644 index ca4b73fc02..0000000000 --- a/module/core/former/tests/inc/former_tests/name_collision_context.rs +++ /dev/null @@ -1,19 +0,0 @@ -#![ allow( dead_code ) ] - -#[ allow( unused_imports ) ] -use super::*; - - -pub mod core {} -pub mod std {} -pub mod marker {} -pub trait CloneAny{} -// pub trait Context{} -pub trait Formed{} -pub trait OnEnd{} - -#[ derive( Clone, the_module::Former ) ] -pub struct Context -{ - inner : ::std::sync::Arc< ::core::cell::RefCell< dyn CloneAny > > -} diff --git a/module/core/former/tests/inc/former_tests/name_collision_core.rs b/module/core/former/tests/inc/former_tests/name_collision_core.rs deleted file mode 100644 index 8cdf38cb3f..0000000000 --- a/module/core/former/tests/inc/former_tests/name_collision_core.rs +++ /dev/null @@ -1,19 +0,0 @@ -#![ allow( dead_code ) ] -#![ allow( non_camel_case_types ) ] - -#[ allow( unused_imports ) ] -use super::*; - -// pub mod core {} -pub mod std {} -pub mod marker {} -pub trait CloneAny{} -pub trait Context{} -pub trait Formed{} -pub trait OnEnd{} - -#[ derive( Clone, the_module::Former ) ] -pub struct core -{ - inner : ::std::sync::Arc< ::core::cell::RefCell< dyn CloneAny > > -} diff --git a/module/core/former/tests/inc/former_tests/name_collision_end.rs b/module/core/former/tests/inc/former_tests/name_collision_end.rs deleted file mode 100644 index b998b6153c..0000000000 --- a/module/core/former/tests/inc/former_tests/name_collision_end.rs +++ /dev/null @@ -1,24 +0,0 @@ -#![ allow( dead_code ) ] - -#[ allow( unused_imports ) ] -use super::*; - -pub mod core {} -pub mod std {} -pub mod marker {} -pub trait CloneAny{} -pub trait Context{} -pub trait Formed{} -pub trait OnEnd{} - -#[ derive( Clone, the_module::Former ) ] -// #[ derive( Clone, the_module::Former ) ] #[ debug ] -// #[ derive( Clone ) ] -pub struct End -{ - inner : ::std::sync::Arc< ::core::cell::RefCell< dyn CloneAny > > -} - -// = begin_coercing of generated - -// == end of generated \ No newline at end of file diff --git a/module/core/former/tests/inc/former_tests/name_collision_former_hashmap_without_parameter.rs b/module/core/former/tests/inc/former_tests/name_collision_former_hashmap_without_parameter.rs index a1396f3ba2..8b32f55ce9 100644 --- a/module/core/former/tests/inc/former_tests/name_collision_former_hashmap_without_parameter.rs +++ b/module/core/former/tests/inc/former_tests/name_collision_former_hashmap_without_parameter.rs @@ -10,6 +10,8 @@ pub trait CloneAny{} pub trait Context{} pub trait Formed{} pub trait OnEnd{} +pub struct None{} +pub struct Some{} #[ derive( Debug, PartialEq ) ] struct HashMap< T > @@ -21,6 +23,7 @@ struct HashMap< T > pub struct Struct1 { f2 : HashMap< i32 >, + i : ::std::option::Option< i32 >, } tests_impls! @@ -31,7 +34,7 @@ tests_impls! { let got = Struct1::former().f2( HashMap { f1 : 3 } ).form(); - let expected = Struct1 { f2 : HashMap { f1 : 3 } }; + let expected = Struct1 { f2 : HashMap { f1 : 3 }, i : ::std::option::Option::None }; a_id!( got, expected ); } diff --git a/module/core/former/tests/inc/former_tests/name_collision_former_vector_without_parameter.rs b/module/core/former/tests/inc/former_tests/name_collision_former_vector_without_parameter.rs index 605bf9f4f4..52ccc33233 100644 --- a/module/core/former/tests/inc/former_tests/name_collision_former_vector_without_parameter.rs +++ b/module/core/former/tests/inc/former_tests/name_collision_former_vector_without_parameter.rs @@ -10,6 +10,8 @@ pub trait CloneAny{} pub trait Context{} pub trait Formed{} pub trait OnEnd{} +pub struct None{} +pub struct Some{} #[ derive( Debug, PartialEq ) ] struct Vec @@ -21,6 +23,7 @@ struct Vec pub struct Struct1 { f2 : Vec<>, + i : ::std::option::Option< i32 >, } tests_impls! @@ -31,7 +34,7 @@ tests_impls! { let got = Struct1::former().f2( Vec { f1 : 3 } ).form(); - let expected = Struct1 { f2 : Vec { f1 : 3 } }; + let expected = Struct1 { f2 : Vec { f1 : 3 }, i : ::std::option::Option::None }; a_id!( got, expected ); } diff --git a/module/core/former/tests/inc/former_tests/name_collision_on_end.rs b/module/core/former/tests/inc/former_tests/name_collision_on_end.rs deleted file mode 100644 index 3645d92588..0000000000 --- a/module/core/former/tests/inc/former_tests/name_collision_on_end.rs +++ /dev/null @@ -1,18 +0,0 @@ -#![ allow( dead_code ) ] - -#[ allow( unused_imports ) ] -use super::*; - -pub mod core {} -pub mod std {} -pub mod marker {} -pub trait CloneAny{} -pub trait Context{} -pub trait Formed{} -// pub trait OnEnd{} - -#[ derive( Clone, the_module::Former ) ] -pub struct OnEnd -{ - inner : ::std::sync::Arc< ::core::cell::RefCell< dyn CloneAny > > -} diff --git a/module/core/former/tests/inc/former_tests/name_collisions.rs b/module/core/former/tests/inc/former_tests/name_collisions.rs index 2dba7ce21f..94f6aa388d 100644 --- a/module/core/former/tests/inc/former_tests/name_collisions.rs +++ b/module/core/former/tests/inc/former_tests/name_collisions.rs @@ -1,46 +1,108 @@ #![ allow( dead_code ) ] +#![ allow( non_camel_case_types ) ] +#![ allow( non_snake_case ) ] #[ allow( unused_imports ) ] use super::*; -pub mod core {} -pub mod std {} -pub trait CloneAny{} -pub trait Context{} -pub trait Formed{} -pub trait OnEnd{} - -#[ allow( dead_code ) ] -type Option = (); -#[ allow( dead_code ) ] -type Some = (); -#[ allow( dead_code ) ] -type None = (); -#[ allow( dead_code ) ] -type Result = (); -#[ allow( dead_code ) ] -type Ok = (); -#[ allow( dead_code ) ] -type Err = (); -#[ allow( dead_code ) ] -type Box = (); -#[ allow( dead_code ) ] -type Default = (); -#[ allow( dead_code ) ] -type HashSet = (); -#[ allow( dead_code ) ] -type HashMap = (); - -#[ derive( Debug, PartialEq, the_module::Former ) ] -// #[ derive( Debug, PartialEq ) ] +// #[ allow( dead_code ) ] +// type Option = (); +// #[ allow( dead_code ) ] +// type Some = (); +// #[ allow( dead_code ) ] +// type None = (); +// #[ allow( dead_code ) ] +// type Result = (); +// #[ allow( dead_code ) ] +// type Ok = (); +// #[ allow( dead_code ) ] +// type Err = (); +// #[ allow( dead_code ) ] +// type Box = (); +// #[ allow( dead_code ) ] +// type Default = (); +// #[ allow( dead_code ) ] +// type HashSet = (); +// #[ allow( dead_code ) ] +// type HashMap = (); + +// pub mod core {} +// pub mod std {} +// pub mod marker {} + +pub struct core{} +pub struct std{} +pub struct marker{} +pub struct CloneAny{} +pub struct Context{} +pub struct Formed{} +pub struct OnEnd{} +pub struct Option{} +pub struct None{} +pub struct Some{} +pub struct Into{} +pub struct From{} +pub struct Default{} +pub struct Vec{} +pub struct HashSet{} +pub struct HashMap{} + +pub fn std(){} +pub fn marker(){} +pub fn CloneAny(){} +pub fn Context(){} +pub fn Formed(){} +pub fn OnEnd(){} +pub fn Option(){} +pub fn None(){} +pub fn Some(){} +pub fn Into(){} +pub fn From(){} +pub fn Default(){} +pub fn Vec(){} +pub fn HashSet(){} +pub fn HashMap(){} + +// // #[ derive( Clone ) ] +// #[ derive( Clone, the_module::Former ) ] +// #[ debug ] +// pub struct core +// { +// inner : ::std::sync::Arc< ::core::cell::RefCell< dyn ::core::convert::AsRef< i32 > > >, +// i : ::std::option::Option< i32 >, +// } + +#[ derive( PartialEq, Debug, the_module::Former ) ] // #[ debug ] pub struct Struct1 { - vec_1 : Vec< String >, + vec_1 : collection_tools::Vec< String >, hashmap_1 : collection_tools::HashMap< String, String >, hashset_1 : collection_tools::HashSet< String >, + // inner : ::std::sync::Arc< ::core::cell::RefCell< dyn ::core::convert::AsRef< i32 > > >, + i : ::core::option::Option< i32 >, } -// +#[ test ] +fn test_vector() +{ + + // test.case( "vector : construction" ); -include!( "./only_test/collections_without_subformer.rs" ); + let command = Struct1::former() + .vec_1( ::collection_tools::vec![ "ghi".to_string(), "klm".to_string() ] ) + // .inner() + .form() + ; + // dbg!( &command ); + + let expected = Struct1 + { + vec_1 : ::collection_tools::vec![ "ghi".to_string(), "klm".to_string() ], + hashmap_1 : ::collection_tools::hmap!{}, + hashset_1 : ::collection_tools::hset!{}, + // inner : ::std::sync::Arc::new( ::core::cell::RefCell::new( &0 ) ), + i : ::core::option::Option::None, + }; + a_id!( command, expected ); +} diff --git a/module/core/former/tests/inc/former_tests/only_test/collections_without_subformer.rs b/module/core/former/tests/inc/former_tests/only_test/collections_without_subformer.rs index 4b68747c33..ab587769c1 100644 --- a/module/core/former/tests/inc/former_tests/only_test/collections_without_subformer.rs +++ b/module/core/former/tests/inc/former_tests/only_test/collections_without_subformer.rs @@ -23,19 +23,19 @@ tests_impls! a_id!( ::std::mem::size_of_val( &former ), ::std::mem::size_of_val( &former2 ) ); let command = Struct1::former().form(); - a_id!( command.vec_1, Vec::< String >::new() ); - a_id!( command.hashmap_1, collection_tools::hmap!{} ); - a_id!( command.hashset_1, collection_tools::hset![] ); + a_id!( command.vec_1, ::collection_tools::Vec::< String >::new() ); + a_id!( command.hashmap_1, ::collection_tools::hmap!{} ); + a_id!( command.hashset_1, ::collection_tools::hset![] ); let command = Struct1::former().perform(); - a_id!( command.vec_1, Vec::< String >::new() ); - a_id!( command.hashmap_1, collection_tools::hmap!{} ); - a_id!( command.hashset_1, collection_tools::hset![] ); + a_id!( command.vec_1, ::collection_tools::Vec::< String >::new() ); + a_id!( command.hashmap_1, ::collection_tools::hmap!{} ); + a_id!( command.hashset_1, ::collection_tools::hset![] ); let command = Struct1::former().end(); - a_id!( command.vec_1, Vec::< String >::new() ); - a_id!( command.hashmap_1, collection_tools::hmap!{} ); - a_id!( command.hashset_1, collection_tools::hset![] ); + a_id!( command.vec_1, ::collection_tools::Vec::< String >::new() ); + a_id!( command.hashmap_1, ::collection_tools::hmap!{} ); + a_id!( command.hashset_1, ::collection_tools::hset![] ); } @@ -47,16 +47,16 @@ tests_impls! // test.case( "vector : construction" ); let command = Struct1::former() - .vec_1( collection_tools::vec![ "ghi".to_string(), "klm".to_string() ] ) + .vec_1( ::collection_tools::vec![ "ghi".to_string(), "klm".to_string() ] ) .form() ; // dbg!( &command ); let expected = Struct1 { - vec_1 : collection_tools::vec![ "ghi".to_string(), "klm".to_string() ], - hashmap_1 : collection_tools::hmap!{}, - hashset_1 : collection_tools::hset!{}, + vec_1 : ::collection_tools::vec![ "ghi".to_string(), "klm".to_string() ], + hashmap_1 : ::collection_tools::hmap!{}, + hashset_1 : ::collection_tools::hset!{}, }; a_id!( command, expected ); } @@ -69,16 +69,16 @@ tests_impls! // test.case( "construction" ); let command = Struct1::former() - .hashmap_1( collection_tools::hmap!{ "k1".to_string() => "v1".to_string(), "k2".to_string() => "v2".to_string() } ) + .hashmap_1( ::collection_tools::hmap!{ "k1".to_string() => "v1".to_string(), "k2".to_string() => "v2".to_string() } ) .form() ; // dbg!( &command ); let expected = Struct1 { - vec_1 : collection_tools::vec![], - hashmap_1 : collection_tools::hmap!{ "k1".to_string() => "v1".to_string(), "k2".to_string() => "v2".to_string() }, - hashset_1 : collection_tools::hset!{}, + vec_1 : ::collection_tools::vec![], + hashmap_1 : ::collection_tools::hmap!{ "k1".to_string() => "v1".to_string(), "k2".to_string() => "v2".to_string() }, + hashset_1 : ::collection_tools::hset!{}, }; a_id!( command, expected ); } @@ -90,16 +90,16 @@ tests_impls! // test.case( "construction" ); let command = Struct1::former() - .hashset_1( collection_tools::hset!{ "v1".to_string(), "v2".to_string() } ) + .hashset_1( ::collection_tools::hset!{ "v1".to_string(), "v2".to_string() } ) .form() ; // dbg!( &command ); let expected = Struct1 { - vec_1 : collection_tools::vec![], - hashmap_1 : collection_tools::hmap!{}, - hashset_1 : collection_tools::hset!{ "v1".to_string(), "v2".to_string() }, + vec_1 : ::collection_tools::vec![], + hashmap_1 : ::collection_tools::hmap!{}, + hashset_1 : ::collection_tools::hset!{ "v1".to_string(), "v2".to_string() }, }; a_id!( command, expected ); } @@ -114,9 +114,9 @@ tests_impls! let expected = Struct1 { - vec_1 : collection_tools::vec![], - hashmap_1 : collection_tools::hmap!{}, - hashset_1 : collection_tools::hset!{}, + vec_1 : ::collection_tools::vec![], + hashmap_1 : ::collection_tools::hmap!{}, + hashset_1 : ::collection_tools::hset!{}, }; a_id!( command, expected ); } @@ -126,16 +126,16 @@ tests_impls! fn test_complex() { let command = Struct1::former() - .vec_1( collection_tools::vec![ "ghi".to_string(), "klm".to_string() ] ) - .hashmap_1( collection_tools::hmap!{ "k1".to_string() => "v1".to_string(), "k2".to_string() => "v2".to_string() } ) + .vec_1( ::collection_tools::vec![ "ghi".to_string(), "klm".to_string() ] ) + .hashmap_1( ::collection_tools::hmap!{ "k1".to_string() => "v1".to_string(), "k2".to_string() => "v2".to_string() } ) .form(); // dbg!( &command ); let expected = Struct1 { - vec_1 : collection_tools::vec![ "ghi".to_string(), "klm".to_string() ], - hashmap_1 : collection_tools::hmap!{ "k1".to_string() => "v1".to_string(), "k2".to_string() => "v2".to_string() }, - hashset_1 : collection_tools::hset!{}, + vec_1 : ::collection_tools::vec![ "ghi".to_string(), "klm".to_string() ], + hashmap_1 : ::collection_tools::hmap!{ "k1".to_string() => "v1".to_string(), "k2".to_string() => "v2".to_string() }, + hashset_1 : ::collection_tools::hset!{}, }; a_id!( command, expected ); diff --git a/module/core/former/tests/inc/mod.rs b/module/core/former/tests/inc/mod.rs index 30bcd81c0f..05c699d362 100644 --- a/module/core/former/tests/inc/mod.rs +++ b/module/core/former/tests/inc/mod.rs @@ -45,12 +45,11 @@ mod former_tests mod name_collision_former_hashmap_without_parameter; mod name_collision_former_vector_without_parameter; - #[ cfg( any( not( feature = "no_std" ), feature = "use_alloc" ) ) ] mod name_collisions; - mod name_collision_context; - mod name_collision_end; - mod name_collision_on_end; - mod name_collision_core; + // mod name_collision_context; + // mod name_collision_end; + // mod name_collision_on_end; + // mod name_collision_core; // = parametrization diff --git a/module/core/former_meta/src/derive_former.rs b/module/core/former_meta/src/derive_former.rs index d79534fb02..b4d163608e 100644 --- a/module/core/former_meta/src/derive_former.rs +++ b/module/core/former_meta/src/derive_former.rs @@ -556,7 +556,7 @@ specific needs of the broader forming context. It mandates the implementation of #[ inline( always ) ] pub fn new( on_end : Definition::End ) -> Self { - Self::begin_coercing( None, None, on_end ) + Self::begin_coercing( ::core::option::Option::None, ::core::option::Option::None, on_end ) } /// @@ -565,12 +565,12 @@ specific needs of the broader forming context. It mandates the implementation of #[ inline( always ) ] pub fn new_coercing< IntoEnd >( end : IntoEnd ) -> Self where - IntoEnd : Into< Definition::End >, + IntoEnd : ::core::convert::Into< Definition::End >, { Self::begin_coercing ( - None, - None, + ::core::option::Option::None, + ::core::option::Option::None, end, ) } @@ -589,7 +589,7 @@ specific needs of the broader forming context. It mandates the implementation of { if storage.is_none() { - storage = Some( ::core::default::Default::default() ); + storage = ::core::option::Option::Some( ::core::default::Default::default() ); } Self { @@ -614,7 +614,7 @@ specific needs of the broader forming context. It mandates the implementation of { if storage.is_none() { - storage = Some( ::core::default::Default::default() ); + storage = ::core::option::Option::Some( ::core::default::Default::default() ); } Self { @@ -715,7 +715,7 @@ specific needs of the broader forming context. It mandates the implementation of -> Self { debug_assert!( storage.is_none() ); - Self::begin( None, context, on_end ) + Self::begin( ::core::option::Option::None, context, on_end ) } } diff --git a/module/core/former_meta/src/derive_former/field.rs b/module/core/former_meta/src/derive_former/field.rs index 7135d9370a..089470ea84 100644 --- a/module/core/former_meta/src/derive_former/field.rs +++ b/module/core/former_meta/src/derive_former/field.rs @@ -468,10 +468,10 @@ where #[ inline ] pub fn {field_ident}< Src >( mut self, src : Src ) -> Self where - Src : ::core::convert::Into< {0} >, + Src : Into< {0} >, {{ debug_assert!( self.storage.{field_ident}.is_none() ); - self.storage.{field_ident} = ::core::option::Option::Some( ::core::convert::Into::into( src ) ); + self.storage.{field_ident} = Some( Into::into( src ) ); self }} }} @@ -610,7 +610,12 @@ field : {field_ident}"#, End = #subform_collection_end< Definition >, >, { - Former2::former_begin( None, Some( self ), #subform_collection_end::< Definition >::default() ) + Former2::former_begin + ( + ::core::option::Option::None, + ::core::option::Option::Some( self ), + #subform_collection_end::< Definition >::default(), + ) } // #[ inline( always ) ] @@ -786,7 +791,7 @@ with the new content generated during the subforming process. _phantom : core::marker::PhantomData< ( Definition, ) >, } - impl< Definition > Default + impl< Definition > ::core::default::Default for #subform_collection_end< Definition > { @@ -934,7 +939,12 @@ allowing for dynamic and flexible construction of the `{item}` entity's {field_i >, Former2 : former::FormerBegin< Definition2 >, { - Former2::former_begin( None, Some( self ), #subform_entry_end::default() ) + Former2::former_begin + ( + ::core::option::Option::None, + ::core::option::Option::Some( self ), + #subform_entry_end::default(), + ) } }; @@ -1070,7 +1080,7 @@ formation process of the `{item}`. _phantom : core::marker::PhantomData< fn( Definition ) >, } - impl< Definition > Default + impl< Definition > ::core::default::Default for #subform_entry_end< Definition > { #[ inline( always ) ] @@ -1110,9 +1120,9 @@ formation process of the `{item}`. let mut super_former = super_former.unwrap(); if super_former.storage.#field_ident.is_none() { - super_former.storage.#field_ident = Some( Default::default() ); + super_former.storage.#field_ident = ::core::option::Option::Some( ::core::default::Default::default() ); } - if let Some( ref mut field ) = super_former.storage.#field_ident + if let ::core::option::Option::Some( ref mut field ) = super_former.storage.#field_ident { former::CollectionAdd::add ( @@ -1228,7 +1238,12 @@ generics, providing a cleaner interface for initiating subform operations on sca >, Former2 : former::FormerBegin< Definition2 >, { - Former2::former_begin( None, Some( self ), #subform_scalar_end::default() ) + Former2::former_begin + ( + ::core::option::Option::None, + ::core::option::Option::Some( self ), + #subform_scalar_end::default(), + ) } // #[ inline( always ) ] @@ -1372,7 +1387,7 @@ Essentially, this end action integrates the individually formed scalar value bac _phantom : core::marker::PhantomData< fn( Definition ) >, } - impl< Definition > Default + impl< Definition > ::core::default::Default for #subform_scalar_end< Definition > { #[ inline( always ) ] @@ -1411,7 +1426,7 @@ Essentially, this end action integrates the individually formed scalar value bac { let mut super_former = super_former.unwrap(); debug_assert!( super_former.storage.#field_ident.is_none() ); - super_former.storage.#field_ident = Some( ::core::convert::Into::into( former::StoragePreform::preform( substorage ) ) ); + super_former.storage.#field_ident = ::core::option::Option::Some( ::core::convert::Into::into( former::StoragePreform::preform( substorage ) ) ); super_former } } @@ -1421,7 +1436,7 @@ Essentially, this end action integrates the individually formed scalar value bac // _phantom : core::marker::PhantomData< fn( Definition ) >, // } // -// impl< Definition > Default +// impl< Definition > ::core::default::Default // for ParentFormerSubformScalarChildEnd< Definition > // { // #[ inline( always ) ] diff --git a/module/core/reflect_tools/src/reflect.rs b/module/core/reflect_tools/src/reflect.rs index 48c84ffc45..1d4f293585 100644 --- a/module/core/reflect_tools/src/reflect.rs +++ b/module/core/reflect_tools/src/reflect.rs @@ -66,8 +66,6 @@ pub mod entity_hashset; pub mod primitive; pub mod fields; -// pub mod to_string; -// pub mod to_string_with_fallback; pub mod wrapper; #[ doc( inline ) ] @@ -107,12 +105,6 @@ pub mod own #[ doc( inline ) ] #[ allow( unused_imports ) ] pub use super::fields::orphan::*; - // #[ doc( inline ) ] - // #[ allow( unused_imports ) ] - // pub use super::to_string::orphan::*; - // #[ doc( inline ) ] - // #[ allow( unused_imports ) ] - // pub use super::to_string_with_fallback::orphan::*; #[ doc( inline ) ] #[ allow( unused_imports ) ] pub use super::wrapper::orphan::*; @@ -163,12 +155,6 @@ pub mod exposed #[ doc( inline ) ] #[ allow( unused_imports ) ] pub use super::fields::exposed::*; - // #[ doc( inline ) ] - // #[ allow( unused_imports ) ] - // pub use super::to_string::exposed::*; - // #[ doc( inline ) ] - // #[ allow( unused_imports ) ] - // pub use super::to_string_with_fallback::exposed::*; #[ doc( inline ) ] #[ allow( unused_imports ) ] pub use super::wrapper::exposed::*; @@ -206,12 +192,6 @@ pub mod prelude #[ doc( inline ) ] #[ allow( unused_imports ) ] pub use super::fields::prelude::*; - // #[ doc( inline ) ] - // #[ allow( unused_imports ) ] - // pub use super::to_string::prelude::*; - // #[ doc( inline ) ] - // #[ allow( unused_imports ) ] - // pub use super::to_string_with_fallback::prelude::*; #[ doc( inline ) ] #[ allow( unused_imports ) ] pub use super::wrapper::prelude::*; diff --git a/module/core/reflect_tools/src/reflect/fields.rs b/module/core/reflect_tools/src/reflect/fields.rs index 3410fcf5a1..f44b73de45 100644 --- a/module/core/reflect_tools/src/reflect/fields.rs +++ b/module/core/reflect_tools/src/reflect/fields.rs @@ -36,19 +36,59 @@ pub( crate ) mod private } /// - /// A trait for iterating over all fields convertible into a specified type within an entity. + /// A trait for iterating over fields convertible to a specified type within an entity. + /// + /// This trait provides a mechanism for accessing fields in collections or entities, converting + /// them into a desired type for iteration. /// /// # Type Parameters /// - /// - `K`: The key type. - /// - `V`: The value type. + /// - `K`: The key type, typically representing the index or identifier of each field. + /// - `V`: The value type that fields are converted into during iteration. /// - pub trait Fields< 'a, K, V > - where - V : Clone + 'a, + /// # Associated Types + /// + /// - `Val<'v>`: The type of value yielded by the iterator, parameterized by a lifetime `'v`. + /// This ensures the values' lifetimes are tied to the entity being iterated over. + /// + /// # Example + /// + /// ```rust + /// use reflect_tools::{ Fields, IteratorTrait }; + /// + /// struct MyCollection< V > + /// { + /// data : Vec< V >, + /// } + /// + /// impl< V > Fields< usize, &V > for MyCollection< V > + /// { + /// type Key< 'k > = usize where V : 'k; + /// type Val< 'v > = & 'v V where Self : 'v; + /// + /// fn fields( & self ) -> impl IteratorTrait< Item = ( usize, Self::Val< '_ > ) > + /// { + /// self.data.iter().enumerate() + /// } + /// } + /// ``` + /// + /// This example shows `MyCollection` implementing `Fields`, allowing iteration over its elements + /// with both index and value. + pub trait Fields< K, V > { - /// Returns an iterator over all fields of the specified type within the entity. - fn fields( &'a self ) -> impl IteratorTrait< Item = ( K, V ) >; + + /// The type of key yielded by the iterator, parameterized by a lifetime `'k`. + /// This ensures the values' lifetimes are tied to the entity being iterated over. + type Key< 'k > where Self : 'k; + + /// The type of value yielded by the iterator, parameterized by a lifetime `'v`. + /// This ensures the values' lifetimes are tied to the entity being iterated over. + type Val< 'v > where Self : 'v; + + /// Returns an iterator over fields of the specified type within the entity. + fn fields( &self ) -> impl IteratorTrait< Item = ( Self::Key< '_ >, Self::Val< '_ > ) >; + } /// Trait returning name of type of variable. @@ -71,13 +111,56 @@ pub( crate ) mod private // == implementations for collections - impl< 'a, T > Fields< 'a, usize, Option< Cow< 'a, T > > > for Vec< T > + impl< V > Fields< usize, &'_ V > for Vec< V > where - T : Clone + V : std::borrow::ToOwned, { - fn fields( &'a self ) -> impl IteratorTrait< Item = ( usize, Option< Cow< 'a, T > > ) > + + type Key< 'k > = usize + where Self : 'k, usize : 'k; + + type Val< 'v > = &'v V + where Self : 'v, V : 'v; + + fn fields( &self ) -> impl IteratorTrait< Item = ( Self::Key< '_ >, Self::Val< '_ > ) > + { + self.into_iter().enumerate().map( move | ( key, val ) | ( key, val ) ) + } + + } + + impl< V > Fields< usize, Option< Cow< '_, V > > > for Vec< V > + where + V : std::borrow::ToOwned, + { + + type Key< 'k > = usize + where Self : 'k, usize : 'k; + + type Val< 'v > = Option< Cow< 'v, V > > + where Self : 'v; + + fn fields( &self ) -> impl IteratorTrait< Item = ( Self::Key< '_ >, Self::Val< '_ > ) > + { + self.iter().enumerate().map( move | ( key, val ) | ( key, Some( Cow::Borrowed( val ) ) ) ) + } + } + + impl< V, Marker > Fields< usize, crate::MaybeAs< '_, V, Marker > > for Vec< V > + where + V : std::borrow::ToOwned, + Marker : Clone + Copy + 'static, + { + + type Key< 'k > = usize + where Self : 'k, usize : 'k; + + type Val< 'v > = crate::MaybeAs< 'v, V, Marker > + where Self : 'v; + + fn fields( &self ) -> impl IteratorTrait< Item = ( Self::Key< '_ >, Self::Val< '_ > ) > { - self.iter().enumerate().map( | ( key, val ) | ( key, Some( Cow::Borrowed( val ) ) ) ) + self.iter().enumerate().map( move | ( key, val ) | ( key, crate::MaybeAs::from( Cow::Borrowed( val ) ) ) ) } } diff --git a/module/core/reflect_tools/src/reflect/wrapper/maybe_as.rs b/module/core/reflect_tools/src/reflect/wrapper/maybe_as.rs index fc2b9dfe19..9b1a76e23f 100644 --- a/module/core/reflect_tools/src/reflect/wrapper/maybe_as.rs +++ b/module/core/reflect_tools/src/reflect/wrapper/maybe_as.rs @@ -11,14 +11,12 @@ use core::ops::{ Deref }; pub struct MaybeAs< 'a, T, Marker >( pub Option< Cow< 'a, T > >, ::core::marker::PhantomData< fn() -> Marker > ) where T : std::borrow::ToOwned + ?Sized, - // < T as std::borrow::ToOwned >::Owned : Clone, Marker : Clone + Copy + 'static, ; impl< 'a, T, Marker > MaybeAs< 'a, T, Marker > where T : std::borrow::ToOwned + ?Sized, - // < T as std::borrow::ToOwned >::Owned : Clone, Marker : Clone + Copy + 'static, { @@ -82,10 +80,25 @@ where } +// impl< 'a, T, Marker > std::borrow::ToOwned for MaybeAs< 'a, T, Marker > +// where +// T : std::borrow::ToOwned + ?Sized, +// { +// type Owned = MaybeAs< 'static, T::Owned, Marker >; +// +// fn to_owned( &self ) -> Self::Owned +// { +// MaybeAs +// ( +// self.0.as_ref().map( | cow | Cow::Owned( cow.to_owned() ) ), +// std::marker::PhantomData +// ) +// } +// } + impl< 'a, T, Marker > Clone for MaybeAs< 'a, T, Marker > where T : std::borrow::ToOwned + ?Sized, - // < T as std::borrow::ToOwned >::Owned : Clone, Marker : Clone + Copy + 'static, { fn clone( &self ) -> Self @@ -97,9 +110,7 @@ where impl< 'a, T, Marker > AsRef< Option< Cow< 'a, T > > > for MaybeAs< 'a, T, Marker > where T : std::borrow::ToOwned + ?Sized, - // < T as std::borrow::ToOwned >::Owned : Clone, Marker : Clone + Copy + 'static, - // Self : 'a, { fn as_ref( &self ) -> &Option< Cow< 'a, T > > { @@ -110,8 +121,6 @@ where impl< 'a, T, Marker > Deref for MaybeAs< 'a, T, Marker > where T : std::borrow::ToOwned + ?Sized, - // < T as std::borrow::ToOwned >::Owned : Clone, - // T : Clone, Marker : Clone + Copy + 'static, { type Target = Option< Cow< 'a, T > >; @@ -125,7 +134,6 @@ impl< 'a, T, Marker > From< Cow< 'a, T > > for MaybeAs< 'a, T, Marker > where T : std::borrow::ToOwned + ?Sized, - // < T as std::borrow::ToOwned >::Owned : Clone, Marker : Clone + Copy + 'static, { fn from( src : Cow< 'a, T > ) -> Self @@ -138,7 +146,6 @@ impl< 'a, T, Marker > From< Option< Cow< 'a, T > > > for MaybeAs< 'a, T, Marker > where T : std::borrow::ToOwned + ?Sized, - // < T as std::borrow::ToOwned >::Owned : Clone, Marker : Clone + Copy + 'static, { fn from( src : Option< Cow< 'a, T > > ) -> Self @@ -151,7 +158,6 @@ impl< 'a, T, Marker > From< &'a T > for MaybeAs< 'a, T, Marker > where T : std::borrow::ToOwned + ?Sized, - // < T as std::borrow::ToOwned >::Owned : Clone, Marker : Clone + Copy + 'static, { fn from( src : &'a T ) -> Self @@ -163,7 +169,6 @@ where impl< 'a, T, Marker > Default for MaybeAs< 'a, T, Marker > where T : std::borrow::ToOwned + ?Sized, - // < T as std::borrow::ToOwned >::Owned : Clone, < T as std::borrow::ToOwned >::Owned : Default, Marker : Clone + Copy + 'static, { @@ -176,7 +181,6 @@ where impl< 'a, T, Marker > fmt::Debug for MaybeAs< 'a, T, Marker > where T : std::borrow::ToOwned + ?Sized, - // < T as std::borrow::ToOwned >::Owned : Clone, < T as std::borrow::ToOwned >::Owned : fmt::Debug, Marker : Clone + Copy + 'static, T : fmt::Debug, @@ -192,7 +196,6 @@ where impl< 'a, T, Marker > PartialEq for MaybeAs< 'a, T, Marker > where T : std::borrow::ToOwned + ?Sized, - // < T as std::borrow::ToOwned >::Owned : Clone, Marker : Clone + Copy + 'static, T : PartialEq, { @@ -205,7 +208,6 @@ where impl< 'a, T, Marker > Eq for MaybeAs< 'a, T, Marker > where T : std::borrow::ToOwned + ?Sized, - // < T as std::borrow::ToOwned >::Owned : Clone, Marker : Clone + Copy + 'static, T : Eq, { diff --git a/module/core/reflect_tools/tests/inc/fundamental/fields_test.rs b/module/core/reflect_tools/tests/inc/fundamental/fields_test.rs index 3a2667fe64..28694154f5 100644 --- a/module/core/reflect_tools/tests/inc/fundamental/fields_test.rs +++ b/module/core/reflect_tools/tests/inc/fundamental/fields_test.rs @@ -29,12 +29,15 @@ pub struct TestObject pub tools : Option< Vec< HashMap< String, String > > >, } -impl< 'a > Fields< 'a, &'static str, MaybeAs< 'a, String, () > > +impl Fields< &'static str, MaybeAs< '_, String, () > > for TestObject { - fn fields( &'a self ) -> impl IteratorTrait< Item = ( &'static str, MaybeAs< 'a, String, () > ) > + type Key< 'k > = &'static str; + type Val< 'v > = MaybeAs< 'v, String, () >; + + fn fields( &self ) -> impl IteratorTrait< Item = ( &'static str, MaybeAs< '_, String, () > ) > { - let mut dst : Vec< ( &'static str, MaybeAs< 'a, String, () > ) > = Vec::new(); + let mut dst : Vec< ( &'static str, MaybeAs< '_, String, () > ) > = Vec::new(); dst.push( ( "id", Some( Cow::Borrowed( &self.id ) ).into() ) ); dst.push( ( "created_at", Some( Cow::Owned( self.created_at.to_string() ) ).into() ) ); @@ -137,8 +140,13 @@ fn test_vec_fields() }, ]; - let fields : Vec< _ > = test_objects.fields().collect(); + // let fields : Vec< _ > = test_objects.fields().collect(); + // let fields : Vec< ( usize, Option< Cow< '_, TestObject > > ) > = test_objects.fields().collect(); + let fields : Vec< _ > = Fields::< usize, Option< _ > >::fields( &test_objects ).collect(); assert_eq!( fields.len(), 2 ); assert_eq!( fields[ 0 ].0, 0 ); assert_eq!( fields[ 1 ].0, 1 ); + + // let x = Cow::Borrowed( ); + } diff --git a/module/core/reflect_tools_meta/src/lib.rs b/module/core/reflect_tools_meta/src/lib.rs index 50fd69165c..f6a8a78b64 100644 --- a/module/core/reflect_tools_meta/src/lib.rs +++ b/module/core/reflect_tools_meta/src/lib.rs @@ -5,8 +5,8 @@ // #![ allow( non_upper_case_globals ) ] #![ doc = include_str!( concat!( env!( "CARGO_MANIFEST_DIR" ), "/", "Readme.md" ) ) ] -#[ cfg( feature = "enabled" ) ] -use macro_tools::prelude::*; +// #[ cfg( feature = "enabled" ) ] +// use macro_tools::prelude::*; #[ cfg( feature = "enabled" ) ] mod implementation