From 35f8b309bc2519a6e29ddeae76cac5148946cb52 Mon Sep 17 00:00:00 2001 From: Sakapoi Date: Thu, 29 Aug 2024 13:27:57 +0300 Subject: [PATCH 01/67] added impl and tests --- .../core/reflect_tools/src/reflect/fields.rs | 4 + .../reflect_tools/src/reflect/fields/bset.rs | 65 +++++++++++++++++ .../reflect_tools/src/reflect/fields/deque.rs | 65 +++++++++++++++++ .../reflect_tools/src/reflect/fields/hset.rs | 65 +++++++++++++++++ .../reflect_tools/src/reflect/fields/llist.rs | 65 +++++++++++++++++ .../tests/inc/fundamental/fields_bset.rs | 61 ++++++++++++++++ .../tests/inc/fundamental/fields_deque.rs | 73 +++++++++++++++++++ .../tests/inc/fundamental/fields_hset.rs | 60 +++++++++++++++ .../tests/inc/fundamental/fields_llist.rs | 73 +++++++++++++++++++ module/core/reflect_tools/tests/inc/mod.rs | 4 + 10 files changed, 535 insertions(+) create mode 100644 module/core/reflect_tools/src/reflect/fields/bset.rs create mode 100644 module/core/reflect_tools/src/reflect/fields/deque.rs create mode 100644 module/core/reflect_tools/src/reflect/fields/hset.rs create mode 100644 module/core/reflect_tools/src/reflect/fields/llist.rs create mode 100644 module/core/reflect_tools/tests/inc/fundamental/fields_bset.rs create mode 100644 module/core/reflect_tools/tests/inc/fundamental/fields_deque.rs create mode 100644 module/core/reflect_tools/tests/inc/fundamental/fields_hset.rs create mode 100644 module/core/reflect_tools/tests/inc/fundamental/fields_llist.rs diff --git a/module/core/reflect_tools/src/reflect/fields.rs b/module/core/reflect_tools/src/reflect/fields.rs index 428c95237b..edbdfbc9b4 100644 --- a/module/core/reflect_tools/src/reflect/fields.rs +++ b/module/core/reflect_tools/src/reflect/fields.rs @@ -112,6 +112,10 @@ mod private mod vec; mod hmap; mod bmap; +mod hset; +mod bset; +mod deque; +mod llist; #[ doc( inline ) ] #[ allow( unused_imports ) ] diff --git a/module/core/reflect_tools/src/reflect/fields/bset.rs b/module/core/reflect_tools/src/reflect/fields/bset.rs new file mode 100644 index 0000000000..e68d4d7e4b --- /dev/null +++ b/module/core/reflect_tools/src/reflect/fields/bset.rs @@ -0,0 +1,65 @@ +//! +//! Implement fields for BTreeSet. +//! + +use crate::*; +use std::borrow::Cow; +use collection_tools::BTreeSet; + +impl< V, Borrowed > Fields< usize, &'_ Borrowed > for BTreeSet< V > +where + Borrowed : std::borrow::ToOwned + 'static + ?Sized, + V : std::borrow::Borrow< Borrowed >, +{ + + type Key< 'k > = usize + where Self : 'k, usize : 'k; + + type Val< 'v > = &'v Borrowed + where Self : 'v, V : 'v; + + fn fields< 's >( &'s self ) -> impl IteratorTrait< Item = ( Self::Key< 's >, Self::Val< 's > ) > + { + self.iter().enumerate().map( move | ( key, val ) | ( key, val.borrow() ) ) + } + +} + +impl< V, Borrowed > Fields< usize, Option< Cow< '_, Borrowed > > > for BTreeSet< V > +where + Borrowed : std::borrow::ToOwned + 'static + ?Sized, + V : std::borrow::Borrow< Borrowed >, +{ + + type Key< 'k > = usize + where Self : 'k, usize : 'k; + + type Val< 'v > = Option< Cow< 'v, Borrowed > > + where Self : 'v; + + fn fields< 's >( &'s self ) -> impl IteratorTrait< Item = ( Self::Key< 's >, Self::Val< 's > ) > + { + self.iter().enumerate().map( move | ( key, val ) | ( key, Some( Cow::Borrowed( val.borrow() ) ) ) ) + } + +} + +impl< V, Borrowed, Marker > Fields< usize, OptionalCow< '_, Borrowed, Marker > > for BTreeSet< V > +where + Borrowed : std::borrow::ToOwned + 'static + ?Sized, + V : std::borrow::Borrow< Borrowed >, + Marker : Clone + Copy + 'static, +{ + + type Key< 'k > = usize + where Self : 'k, usize : 'k; + + type Val< 'v > = OptionalCow< 'v, Borrowed, Marker > + where Self : 'v; + + fn fields< 's >( &'s self ) -> impl IteratorTrait< Item = ( Self::Key< 's >, Self::Val< 's > ) > + { + self.iter().enumerate().map( move | ( key, val ) | ( key, OptionalCow::from( val.borrow() ) ) ) + } + +} diff --git a/module/core/reflect_tools/src/reflect/fields/deque.rs b/module/core/reflect_tools/src/reflect/fields/deque.rs new file mode 100644 index 0000000000..734255ad1a --- /dev/null +++ b/module/core/reflect_tools/src/reflect/fields/deque.rs @@ -0,0 +1,65 @@ +//! +//! Implement fields for Deque. +//! + +use crate::*; +use std::borrow::Cow; +use collection_tools::VecDeque; + +impl< V, Borrowed > Fields< usize, &'_ Borrowed > for VecDeque< V > +where + Borrowed : std::borrow::ToOwned + 'static + ?Sized, + V : std::borrow::Borrow< Borrowed >, +{ + + type Key< 'k > = usize + where Self : 'k, usize : 'k; + + type Val< 'v > = &'v Borrowed + where Self : 'v, V : 'v; + + fn fields< 's >( &'s self ) -> impl IteratorTrait< Item = ( Self::Key< 's >, Self::Val< 's > ) > + { + self.iter().enumerate().map( move | ( key, val ) | ( key, val.borrow() ) ) + } + +} + +impl< V, Borrowed > Fields< usize, Option< Cow< '_, Borrowed > > > for VecDeque< V > +where + Borrowed : std::borrow::ToOwned + 'static + ?Sized, + V : std::borrow::Borrow< Borrowed >, +{ + + type Key< 'k > = usize + where Self : 'k, usize : 'k; + + type Val< 'v > = Option< Cow< 'v, Borrowed > > + where Self : 'v; + + fn fields< 's >( &'s self ) -> impl IteratorTrait< Item = ( Self::Key< 's >, Self::Val< 's > ) > + { + self.iter().enumerate().map( move | ( key, val ) | ( key, Some( Cow::Borrowed( val.borrow() ) ) ) ) + } + +} + +impl< V, Borrowed, Marker > Fields< usize, OptionalCow< '_, Borrowed, Marker > > for VecDeque< V > +where + Borrowed : std::borrow::ToOwned + 'static + ?Sized, + V : std::borrow::Borrow< Borrowed >, + Marker : Clone + Copy + 'static, +{ + + type Key< 'k > = usize + where Self : 'k, usize : 'k; + + type Val< 'v > = OptionalCow< 'v, Borrowed, Marker > + where Self : 'v; + + fn fields< 's >( &'s self ) -> impl IteratorTrait< Item = ( Self::Key< 's >, Self::Val< 's > ) > + { + self.iter().enumerate().map( move | ( key, val ) | ( key, OptionalCow::from( val.borrow() ) ) ) + } + +} diff --git a/module/core/reflect_tools/src/reflect/fields/hset.rs b/module/core/reflect_tools/src/reflect/fields/hset.rs new file mode 100644 index 0000000000..cfc01be06e --- /dev/null +++ b/module/core/reflect_tools/src/reflect/fields/hset.rs @@ -0,0 +1,65 @@ +//! +//! Implement fields for HashSet. +//! + +use crate::*; +use std::borrow::Cow; +use std::collections::HashSet; + +impl< V, Borrowed > Fields< usize, &'_ Borrowed > for HashSet< V > +where + Borrowed : std::borrow::ToOwned + 'static + ?Sized, + V : std::borrow::Borrow< Borrowed >, +{ + + type Key< 'k > = usize + where Self : 'k, usize : 'k; + + type Val< 'v > = &'v Borrowed + where Self : 'v, V : 'v; + + fn fields< 's >( &'s self ) -> impl IteratorTrait< Item = ( Self::Key< 's >, Self::Val< 's > ) > + { + self.iter().enumerate().map( move | ( key, val ) | ( key, val.borrow() ) ) + } + +} + +impl< V, Borrowed > Fields< usize, Option< Cow< '_, Borrowed > > > for HashSet< V > +where + Borrowed : std::borrow::ToOwned + 'static + ?Sized, + V : std::borrow::Borrow< Borrowed >, +{ + + type Key< 'k > = usize + where Self : 'k, usize : 'k; + + type Val< 'v > = Option< Cow< 'v, Borrowed > > + where Self : 'v; + + fn fields< 's >( &'s self ) -> impl IteratorTrait< Item = ( Self::Key< 's >, Self::Val< 's > ) > + { + self.iter().enumerate().map( move | ( key, val ) | ( key, Some( Cow::Borrowed( val.borrow() ) ) ) ) + } + +} + +impl< V, Borrowed, Marker > Fields< usize, OptionalCow< '_, Borrowed, Marker > > for HashSet< V > +where + Borrowed : std::borrow::ToOwned + 'static + ?Sized, + V : std::borrow::Borrow< Borrowed >, + Marker : Clone + Copy + 'static, +{ + + type Key< 'k > = usize + where Self : 'k, usize : 'k; + + type Val< 'v > = OptionalCow< 'v, Borrowed, Marker > + where Self : 'v; + + fn fields< 's >( &'s self ) -> impl IteratorTrait< Item = ( Self::Key< 's >, Self::Val< 's > ) > + { + self.iter().enumerate().map( move | ( key, val ) | ( key, OptionalCow::from( val.borrow() ) ) ) + } + +} diff --git a/module/core/reflect_tools/src/reflect/fields/llist.rs b/module/core/reflect_tools/src/reflect/fields/llist.rs new file mode 100644 index 0000000000..40ca1ced98 --- /dev/null +++ b/module/core/reflect_tools/src/reflect/fields/llist.rs @@ -0,0 +1,65 @@ +//! +//! Implement fields for LinkedList. +//! + +use crate::*; +use std::borrow::Cow; +use collection_tools::LinkedList; + +impl< V, Borrowed > Fields< usize, &'_ Borrowed > for LinkedList< V > +where + Borrowed : std::borrow::ToOwned + 'static + ?Sized, + V : std::borrow::Borrow< Borrowed >, +{ + + type Key< 'k > = usize + where Self : 'k, usize : 'k; + + type Val< 'v > = &'v Borrowed + where Self : 'v, V : 'v; + + fn fields< 's >( &'s self ) -> impl IteratorTrait< Item = ( Self::Key< 's >, Self::Val< 's > ) > + { + self.iter().enumerate().map( move | ( key, val ) | ( key, val.borrow() ) ) + } + +} + +impl< V, Borrowed > Fields< usize, Option< Cow< '_, Borrowed > > > for LinkedList< V > +where + Borrowed : std::borrow::ToOwned + 'static + ?Sized, + V : std::borrow::Borrow< Borrowed >, +{ + + type Key< 'k > = usize + where Self : 'k, usize : 'k; + + type Val< 'v > = Option< Cow< 'v, Borrowed > > + where Self : 'v; + + fn fields< 's >( &'s self ) -> impl IteratorTrait< Item = ( Self::Key< 's >, Self::Val< 's > ) > + { + self.iter().enumerate().map( move | ( key, val ) | ( key, Some( Cow::Borrowed( val.borrow() ) ) ) ) + } + +} + +impl< V, Borrowed, Marker > Fields< usize, OptionalCow< '_, Borrowed, Marker > > for LinkedList< V > +where + Borrowed : std::borrow::ToOwned + 'static + ?Sized, + V : std::borrow::Borrow< Borrowed >, + Marker : Clone + Copy + 'static, +{ + + type Key< 'k > = usize + where Self : 'k, usize : 'k; + + type Val< 'v > = OptionalCow< 'v, Borrowed, Marker > + where Self : 'v; + + fn fields< 's >( &'s self ) -> impl IteratorTrait< Item = ( Self::Key< 's >, Self::Val< 's > ) > + { + self.iter().enumerate().map( move | ( key, val ) | ( key, OptionalCow::from( val.borrow() ) ) ) + } + +} diff --git a/module/core/reflect_tools/tests/inc/fundamental/fields_bset.rs b/module/core/reflect_tools/tests/inc/fundamental/fields_bset.rs new file mode 100644 index 0000000000..5a811ad569 --- /dev/null +++ b/module/core/reflect_tools/tests/inc/fundamental/fields_bset.rs @@ -0,0 +1,61 @@ +#[ allow( unused_imports ) ] +use super::*; + +use the_module:: +{ + Fields, + +}; + +// xxx : implement for other collections + +use std:: +{ + borrow::Cow, +}; + +#[ test ] +fn bset_string_fields() +{ + let collection : BTreeSet< String > = bset! + [ + "a".to_string(), + "b".to_string(), + ]; + + // k, v + let got : BTreeSet< _ > = Fields::< usize, &str >::fields( &collection ).collect(); + assert_eq!( got.len(), 2 ); + let exp = bset![ ( 0, "a" ), ( 1, "b" ) ]; + assert_eq!( got, exp ); + + // k, Option< Cow< '_, str > > + let got : BTreeSet< _ > = Fields::< usize, Option< Cow< '_, str > > >::fields( &collection ).collect(); + assert_eq!( got.len(), 2 ); + let exp = bset![ ( 0, Some( Cow::Borrowed( "a" ) ) ), ( 1, Some( Cow::Borrowed( "b" ) ) ) ]; + assert_eq!( got, exp ); + +} + +#[ test ] +fn bset_str_fields() +{ + let collection : BTreeSet< &str > = bset! + [ + "a", + "b", + ]; + + // k, v + let got : BTreeSet< _ > = Fields::< usize, &str >::fields( &collection ).collect(); + assert_eq!( got.len(), 2 ); + let exp = bset![ ( 0, "a" ), ( 1, "b" ) ]; + assert_eq!( got, exp ); + + // k, Option< Cow< '_, str > > + let got : BTreeSet< _ > = Fields::< usize, Option< Cow< '_, str > > >::fields( &collection ).collect(); + assert_eq!( got.len(), 2 ); + let exp = bset![ ( 0, Some( Cow::Borrowed( "a" ) ) ), ( 1, Some( Cow::Borrowed( "b" ) ) ) ]; + assert_eq!( got, exp ); + +} diff --git a/module/core/reflect_tools/tests/inc/fundamental/fields_deque.rs b/module/core/reflect_tools/tests/inc/fundamental/fields_deque.rs new file mode 100644 index 0000000000..190d8fc57b --- /dev/null +++ b/module/core/reflect_tools/tests/inc/fundamental/fields_deque.rs @@ -0,0 +1,73 @@ +#[ allow( unused_imports ) ] +use super::*; + +use the_module:: +{ + Fields, + OptionalCow, +}; + +// xxx : implement for other collections + +use std:: +{ + borrow::Cow, +}; + +#[ test ] +fn deque_string_fields() +{ + let collection = deque! + [ + "a".to_string(), + "b".to_string(), + ]; + + // k, v + let got : VecDeque< _ > = Fields::< usize, &str >::fields( &collection ).collect(); + assert_eq!( got.len(), 2 ); + let exp = deque![ ( 0, "a" ), ( 1, "b" ) ]; + assert_eq!( got, exp ); + + // k, Option< Cow< '_, str > > + let got : VecDeque< _ > = Fields::< usize, Option< Cow< '_, str > > >::fields( &collection ).collect(); + assert_eq!( got.len(), 2 ); + let exp = deque![ ( 0, Some( Cow::Borrowed( "a" ) ) ), ( 1, Some( Cow::Borrowed( "b" ) ) ) ]; + assert_eq!( got, exp ); + + // k, OptionalCow< '_, str, () > + let got : VecDeque< _ > = Fields::< usize, OptionalCow< '_, str, () > >::fields( &collection ).collect(); + assert_eq!( got.len(), 2 ); + let exp = deque![ ( 0, OptionalCow::from( "a" ) ), ( 1, OptionalCow::from( "b" ) ) ]; + assert_eq!( got, exp ); + +} + +#[ test ] +fn deque_str_fields() +{ + let collection = deque! + [ + "a", + "b", + ]; + + // k, v + let got : VecDeque< _ > = Fields::< usize, &str >::fields( &collection ).collect(); + assert_eq!( got.len(), 2 ); + let exp = deque![ ( 0, "a" ), ( 1, "b" ) ]; + assert_eq!( got, exp ); + + // k, Option< Cow< '_, str > > + let got : VecDeque< _ > = Fields::< usize, Option< Cow< '_, str > > >::fields( &collection ).collect(); + assert_eq!( got.len(), 2 ); + let exp = deque![ ( 0, Some( Cow::Borrowed( "a" ) ) ), ( 1, Some( Cow::Borrowed( "b" ) ) ) ]; + assert_eq!( got, exp ); + + // k, OptionalCow< '_, str, () > + let got : VecDeque< _ > = Fields::< usize, OptionalCow< '_, str, () > >::fields( &collection ).collect(); + assert_eq!( got.len(), 2 ); + let exp = deque![ ( 0, OptionalCow::from( "a" ) ), ( 1, OptionalCow::from( "b" ) ) ]; + assert_eq!( got, exp ); + +} diff --git a/module/core/reflect_tools/tests/inc/fundamental/fields_hset.rs b/module/core/reflect_tools/tests/inc/fundamental/fields_hset.rs new file mode 100644 index 0000000000..fddc44dc94 --- /dev/null +++ b/module/core/reflect_tools/tests/inc/fundamental/fields_hset.rs @@ -0,0 +1,60 @@ +#[ allow( unused_imports ) ] +use super::*; + +use the_module:: +{ + Fields, +}; + +// xxx : implement for other collections + +use std:: +{ + borrow::Cow, +}; + +#[ test ] +fn hset_string_fields() +{ + let collection : HashSet< String > = hset! + [ + "a".to_string(), + "b".to_string(), + ]; + + // k, v + let got : HashSet< _ > = Fields::< usize, &str >::fields( &collection ).collect(); + assert_eq!( got.len(), 2 ); + assert!( got.contains(&( 0, "a" ) ) || got.contains(&( 1, "a" ) ) ); + assert!( got.contains(&( 0, "b" ) ) || got.contains(&( 1, "b" ) ) ); + + // k, Option< Cow< '_, str > > + let got : HashSet< _ > = Fields::< usize, Option< Cow< '_, str > > >::fields( &collection ).collect(); + assert_eq!( got.len(), 2 ); + assert!( got.contains(&( 0, Some( Cow::Borrowed( "a" ) ) ) ) || got.contains(&( 1, Some( Cow::Borrowed( "a" ) ) ) ) ); + assert!( got.contains(&( 0, Some( Cow::Borrowed( "b" ) ) ) ) || got.contains(&( 1, Some( Cow::Borrowed( "b" ) ) ) ) ); + +} + +#[ test ] +fn hset_str_fields() +{ + let collection : HashSet< &str > = hset! + [ + "a", + "b", + ]; + + // k, v + let got : HashSet< _ > = Fields::< usize, &str >::fields( &collection ).collect(); + assert_eq!( got.len(), 2 ); + assert!( got.contains(&( 0, "a" ) ) || got.contains(&( 1, "a" ) ) ); + assert!( got.contains(&( 0, "b" ) ) || got.contains(&( 1, "b" ) ) ); + + // k, Option< Cow< '_, str > > + let got : HashSet< _ > = Fields::< usize, Option< Cow< '_, str > > >::fields( &collection ).collect(); + assert_eq!( got.len(), 2 ); + assert!( got.contains(&( 0, Some( Cow::Borrowed( "a" ) ) ) ) || got.contains(&( 1, Some( Cow::Borrowed( "a" ) ) ) ) ); + assert!( got.contains(&( 0, Some( Cow::Borrowed( "b" ) ) ) ) || got.contains(&( 1, Some( Cow::Borrowed( "b" ) ) ) ) ); + +} diff --git a/module/core/reflect_tools/tests/inc/fundamental/fields_llist.rs b/module/core/reflect_tools/tests/inc/fundamental/fields_llist.rs new file mode 100644 index 0000000000..dc93d87c0a --- /dev/null +++ b/module/core/reflect_tools/tests/inc/fundamental/fields_llist.rs @@ -0,0 +1,73 @@ +#[ allow( unused_imports ) ] +use super::*; + +use the_module:: +{ + Fields, + OptionalCow, +}; + +// xxx : implement for other collections + +use std:: +{ + borrow::Cow, +}; + +#[ test ] +fn llist_string_fields() +{ + let collection = llist! + [ + "a".to_string(), + "b".to_string(), + ]; + + // k, v + let got : LinkedList< _ > = Fields::< usize, &str >::fields( &collection ).collect(); + assert_eq!( got.len(), 2 ); + let exp = llist![ ( 0, "a" ), ( 1, "b" ) ]; + assert_eq!( got, exp ); + + // k, Option< Cow< '_, str > > + let got : LinkedList< _ > = Fields::< usize, Option< Cow< '_, str > > >::fields( &collection ).collect(); + assert_eq!( got.len(), 2 ); + let exp = llist![ ( 0, Some( Cow::Borrowed( "a" ) ) ), ( 1, Some( Cow::Borrowed( "b" ) ) ) ]; + assert_eq!( got, exp ); + + // k, OptionalCow< '_, str, () > + let got : LinkedList< _ > = Fields::< usize, OptionalCow< '_, str, () > >::fields( &collection ).collect(); + assert_eq!( got.len(), 2 ); + let exp = llist![ ( 0, OptionalCow::from( "a" ) ), ( 1, OptionalCow::from( "b" ) ) ]; + assert_eq!( got, exp ); + +} + +#[ test ] +fn llist_str_fields() +{ + let collection = llist! + [ + "a", + "b", + ]; + + // k, v + let got : LinkedList< _ > = Fields::< usize, &str >::fields( &collection ).collect(); + assert_eq!( got.len(), 2 ); + let exp = llist![ ( 0, "a" ), ( 1, "b" ) ]; + assert_eq!( got, exp ); + + // k, Option< Cow< '_, str > > + let got : LinkedList< _ > = Fields::< usize, Option< Cow< '_, str > > >::fields( &collection ).collect(); + assert_eq!( got.len(), 2 ); + let exp = llist![ ( 0, Some( Cow::Borrowed( "a" ) ) ), ( 1, Some( Cow::Borrowed( "b" ) ) ) ]; + assert_eq!( got, exp ); + + // k, OptionalCow< '_, str, () > + let got : LinkedList< _ > = Fields::< usize, OptionalCow< '_, str, () > >::fields( &collection ).collect(); + assert_eq!( got.len(), 2 ); + let exp = llist![ ( 0, OptionalCow::from( "a" ) ), ( 1, OptionalCow::from( "b" ) ) ]; + assert_eq!( got, exp ); + +} diff --git a/module/core/reflect_tools/tests/inc/mod.rs b/module/core/reflect_tools/tests/inc/mod.rs index de4b0b494e..d0ec8fff41 100644 --- a/module/core/reflect_tools/tests/inc/mod.rs +++ b/module/core/reflect_tools/tests/inc/mod.rs @@ -12,6 +12,10 @@ mod fundamental mod fields_vec; mod fields_hmap; mod fields_bmap; + mod fields_bset; + mod fields_deque; + mod fields_hset; + mod fields_llist; } From 1258bccd211b85223a577856c7b0b2fa41395b7b Mon Sep 17 00:00:00 2001 From: Sakapoi Date: Fri, 30 Aug 2024 04:31:59 +0300 Subject: [PATCH 02/67] fixed tests --- module/core/reflect_tools_meta/src/implementation/reflect.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/module/core/reflect_tools_meta/src/implementation/reflect.rs b/module/core/reflect_tools_meta/src/implementation/reflect.rs index 34d239b8d9..04799d0a5a 100644 --- a/module/core/reflect_tools_meta/src/implementation/reflect.rs +++ b/module/core/reflect_tools_meta/src/implementation/reflect.rs @@ -1,7 +1,7 @@ // use macro_tools::proc_macro2::TokenStream; use crate::*; -use macro_tools::{ Result, attr, diag }; +use macro_tools::{ Result, attr, diag, qt, proc_macro2, syn }; // From d8b26ed5bdf1f7746f68c755be22edf485b67ddd Mon Sep 17 00:00:00 2001 From: Sakapoi <97370653+Sakapoi@users.noreply.github.com> Date: Fri, 30 Aug 2024 19:31:34 +0300 Subject: [PATCH 03/67] READY(format_tools): added tests for new collections (#1449) added tests for new collections --- .../format_tools/tests/inc/collection_test.rs | 216 ++++++++++++++++++ .../format_tools/tests/inc/test_object.rs | 71 ++++++ 2 files changed, 287 insertions(+) diff --git a/module/core/format_tools/tests/inc/collection_test.rs b/module/core/format_tools/tests/inc/collection_test.rs index 941f9a498b..3c973c94e2 100644 --- a/module/core/format_tools/tests/inc/collection_test.rs +++ b/module/core/format_tools/tests/inc/collection_test.rs @@ -185,4 +185,220 @@ fn bmap_basic() } +#[ test ] +fn bset_basic() +{ + + let data : collection_tools::Bset< TestObject > = bset! + { + 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 + } + ] + ), + }, + }; + + use the_module::TableFormatter; + let _as_table : AsTable< '_, BTreeSet< TestObject >, &str, TestObject, str, WithRef > = AsTable::new( &data ); + let as_table = AsTable::new( &data ); + + let rows = TableRows::rows( &as_table ); + assert_eq!( rows.len(), 2 ); + + let mut output = String::new(); + let mut context = print::Context::new( &mut output, Default::default() ); + let _got = the_module::TableFormatter::fmt( &as_table, &mut context ); + let got = as_table.table_to_string(); + assert!( got.contains( "│ id │ created_at │ file_ids │ tools │" ) ); + assert!( got.contains( "│ 13 │ [ │ [ │" ) ); + assert!( got.contains( "│ 1627845583 │ [ │ │" ) ); + +} + +#[ test ] +fn deque_basic() +{ + + let data : collection_tools::Deque< TestObject > = deque! + { + 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 + } + ] + ), + }, + }; + + use the_module::TableFormatter; + let _as_table : AsTable< '_, VecDeque< TestObject >, &str, TestObject, str, WithRef > = AsTable::new( &data ); + let as_table = AsTable::new( &data ); + + let rows = TableRows::rows( &as_table ); + assert_eq!( rows.len(), 2 ); + + let mut output = String::new(); + let mut context = print::Context::new( &mut output, Default::default() ); + let _got = the_module::TableFormatter::fmt( &as_table, &mut context ); + let got = as_table.table_to_string(); + assert!( got.contains( "│ id │ created_at │ file_ids │ tools │" ) ); + assert!( got.contains( "│ 13 │ [ │ [ │" ) ); + assert!( got.contains( "│ 1627845583 │ [ │ │" ) ); + +} + +#[ test ] +fn hset_basic() +{ + + let data : collection_tools::Hset< TestObject > = hset! + { + 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 + } + ] + ), + }, + }; + + use the_module::TableFormatter; + let _as_table : AsTable< '_, HashSet< TestObject >, &str, TestObject, str, WithRef > = AsTable::new( &data ); + let as_table = AsTable::new( &data ); + + let rows = TableRows::rows( &as_table ); + assert_eq!( rows.len(), 2 ); + + let mut output = String::new(); + let mut context = print::Context::new( &mut output, Default::default() ); + let _got = the_module::TableFormatter::fmt( &as_table, &mut context ); + let got = as_table.table_to_string(); + assert!( got.contains( "│ id │ created_at │ file_ids │ tools │" ) ); + assert!( got.contains( "│ 13 │ [ │ [ │" ) ); + assert!( got.contains( "│ 1627845583 │ [ │ │" ) ); + +} + +#[ test ] +fn llist_basic() +{ + + let data : collection_tools::Llist< TestObject > = llist! + { + 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 + } + ] + ), + }, + }; + + use the_module::TableFormatter; + let _as_table : AsTable< '_, LinkedList< TestObject >, &str, TestObject, str, WithRef > = AsTable::new( &data ); + let as_table = AsTable::new( &data ); + + let rows = TableRows::rows( &as_table ); + assert_eq!( rows.len(), 2 ); + + let mut output = String::new(); + let mut context = print::Context::new( &mut output, Default::default() ); + let _got = the_module::TableFormatter::fmt( &as_table, &mut context ); + let got = as_table.table_to_string(); + assert!( got.contains( "│ id │ created_at │ file_ids │ tools │" ) ); + assert!( got.contains( "│ 13 │ [ │ [ │" ) ); + assert!( got.contains( "│ 1627845583 │ [ │ │" ) ); + +} + // qqq : xxx : implement for other containers diff --git a/module/core/format_tools/tests/inc/test_object.rs b/module/core/format_tools/tests/inc/test_object.rs index 729a96fa85..6bb7555aa8 100644 --- a/module/core/format_tools/tests/inc/test_object.rs +++ b/module/core/format_tools/tests/inc/test_object.rs @@ -13,6 +13,9 @@ use the_module:: use std:: { collections::HashMap, + hash::Hasher, + hash::Hash, + cmp::Ordering // borrow::Cow, }; @@ -58,6 +61,74 @@ for TestObject } +impl Hash for TestObject +{ + + fn hash< H: Hasher >( &self, state: &mut H ) + { + self.id.hash( state ); + self.created_at.hash( state ); + self.file_ids.hash( state ); + + if let Some( tools ) = &self.tools + { + for tool in tools + { + for ( key, value ) in tool + { + key.hash( state ); + value.hash( state ); + } + } + } + else + { + state.write_u8( 0 ); + } + } + +} + +impl PartialEq for TestObject +{ + + fn eq( &self, other: &Self ) -> bool + { + self.id == other.id && + self.created_at == other.created_at && + self.file_ids == other.file_ids && + self.tools == other.tools + } + +} + +impl Eq for TestObject +{ +} + +impl PartialOrd for TestObject +{ + + fn partial_cmp( &self, other: &Self ) -> Option< Ordering > + { + Some( self.cmp( other ) ) + } + +} + +impl Ord for TestObject +{ + + fn cmp( &self, other: &Self ) -> Ordering + { + self.id + .cmp( &other.id ) + .then_with( | | self.created_at.cmp( &other.created_at ) ) + .then_with( | | self.file_ids.cmp( &other.file_ids ) ) + } + +} + // pub fn test_objects_gen() -> Vec< TestObject > From c903936e56dc1d7b93e42f8ebfaab3eef1a3f175 Mon Sep 17 00:00:00 2001 From: SRetip <56289352+SRetip@users.noreply.github.com> Date: Fri, 30 Aug 2024 19:35:41 +0300 Subject: [PATCH 04/67] READY: add fail fast to `checkmate` stage of CI/CD (#1442) * feat: add fail fast to `checkmate` stage of CI/CD --- .github/workflows/standard_rust_push.yml | 6 +++++- module/move/willbe/template/workflow/standard_rust_push.yml | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/.github/workflows/standard_rust_push.yml b/.github/workflows/standard_rust_push.yml index d2fd96bae2..310c7fa765 100644 --- a/.github/workflows/standard_rust_push.yml +++ b/.github/workflows/standard_rust_push.yml @@ -77,7 +77,8 @@ jobs : continue-on-error: true - name: Check the modules dependencies run: cargo +nightly udeps --all-targets --manifest-path ${{ inputs.manifest_path }} - continue-on-error: true +# This part means that all previous steps will be executed and workflow will not stop if they fail, but not this one. + continue-on-error: false # release: # if: contains( inputs.commit_message, '+test' ) || contains( inputs.commit_message, 'merge' ) @@ -125,6 +126,9 @@ jobs : # run: cargo miri test --manifest-path ${{ inputs.manifest_path }} will_test : +# This section ensures that `job` `will_test` will only be executed after `checkmate` and if `checkmate` fails, no tests will be run. + needs: + - checkmate if : contains( inputs.commit_message, '+test' ) || inputs.commiter_username == 'web-flow' || startsWith( inputs.commit_message, 'merge' ) concurrency : group : standard_rust_push_${{ inputs.module_name }}_${{ github.ref }}_${{ matrix.os }} diff --git a/module/move/willbe/template/workflow/standard_rust_push.yml b/module/move/willbe/template/workflow/standard_rust_push.yml index d2fd96bae2..2f6103ebc2 100644 --- a/module/move/willbe/template/workflow/standard_rust_push.yml +++ b/module/move/willbe/template/workflow/standard_rust_push.yml @@ -77,7 +77,8 @@ jobs : continue-on-error: true - name: Check the modules dependencies run: cargo +nightly udeps --all-targets --manifest-path ${{ inputs.manifest_path }} - continue-on-error: true + # This part means that all previous steps will be executed and workflow will not stop if they fail, but not this one. + continue-on-error: false # release: # if: contains( inputs.commit_message, '+test' ) || contains( inputs.commit_message, 'merge' ) @@ -125,6 +126,9 @@ jobs : # run: cargo miri test --manifest-path ${{ inputs.manifest_path }} will_test : +# This section ensures that `job` `will_test` will only be executed after `checkmate` and if `checkmate` fails, no tests will be run. + needs : + - checkmate if : contains( inputs.commit_message, '+test' ) || inputs.commiter_username == 'web-flow' || startsWith( inputs.commit_message, 'merge' ) concurrency : group : standard_rust_push_${{ inputs.module_name }}_${{ github.ref }}_${{ matrix.os }} From 58fec056ba3fc0593ffdfd383e2ad8deaa2d7b8f Mon Sep 17 00:00:00 2001 From: Sakapoi <97370653+Sakapoi@users.noreply.github.com> Date: Fri, 30 Aug 2024 19:36:23 +0300 Subject: [PATCH 05/67] fixed tests (#1444) --- module/core/collection_tools/src/lib.rs | 4 ++-- module/core/collection_tools/tests/inc/deque.rs | 4 ++-- module/core/collection_tools/tests/inc/vec.rs | 4 +++- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/module/core/collection_tools/src/lib.rs b/module/core/collection_tools/src/lib.rs index 5d0c5976a4..e447f16f85 100644 --- a/module/core/collection_tools/src/lib.rs +++ b/module/core/collection_tools/src/lib.rs @@ -64,7 +64,7 @@ pub mod exposed pub use prelude::*; #[ doc( inline ) ] - #[ cfg( feature = "collection_constructors" ) ] + #[ cfg( any( feature = "use_alloc", all( feature = "collection_constructors", not( feature = "no_std" ) ) ) ) ] pub use crate:: { vec as dlist, @@ -77,7 +77,7 @@ pub mod exposed }; #[ doc( inline ) ] - #[ cfg( feature = "collection_into_constructors" ) ] + #[ cfg( any( feature = "use_alloc", all( feature = "collection_into_constructors", not( feature = "no_std" ) ) ) ) ] pub use crate:: { into_vec, diff --git a/module/core/collection_tools/tests/inc/deque.rs b/module/core/collection_tools/tests/inc/deque.rs index 41c3d323b1..98ab6498bd 100644 --- a/module/core/collection_tools/tests/inc/deque.rs +++ b/module/core/collection_tools/tests/inc/deque.rs @@ -50,8 +50,8 @@ fn into_constructor() exp.push_front( 3 ); assert_eq!( got, exp ); - let _got : DequeList< &str > = the_module::deque!( "b" ); - let _got : DequeList< &str > = the_module::exposed::deque!( "b" ); + let _got = the_module::deque!( "b" ); + let _got = the_module::exposed::deque!( "b" ); } diff --git a/module/core/collection_tools/tests/inc/vec.rs b/module/core/collection_tools/tests/inc/vec.rs index ff9480659f..c1a5f66804 100644 --- a/module/core/collection_tools/tests/inc/vec.rs +++ b/module/core/collection_tools/tests/inc/vec.rs @@ -1,6 +1,7 @@ use super::*; #[ test ] +#[ cfg( any( not( feature = "no_std" ), feature = "use_alloc" ) ) ] fn reexport() { @@ -12,7 +13,8 @@ fn reexport() let got = vec1.last().unwrap().clone(); assert_eq!( got, 2 ); - let mut vec2 : the_module::DynList< i32 > = the_module::DynList::new(); + use std::vec::Vec as DynList; + let mut vec2 : DynList< i32 > = DynList::new(); vec2.push( 1 ); vec2.push( 2 ); let got = vec2.first().unwrap().clone(); From 47408ccbf1d2bcd3ab2420885cffb7fa38c53960 Mon Sep 17 00:00:00 2001 From: Sakapoi <97370653+Sakapoi@users.noreply.github.com> Date: Fri, 30 Aug 2024 19:38:00 +0300 Subject: [PATCH 06/67] READY(strs_tools): fixed tests (#1426) * fixed tests * fixed global import --- .../core/strs_tools/tests/inc/number_test.rs | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/module/core/strs_tools/tests/inc/number_test.rs b/module/core/strs_tools/tests/inc/number_test.rs index cc8bf03006..2c03f223d1 100644 --- a/module/core/strs_tools/tests/inc/number_test.rs +++ b/module/core/strs_tools/tests/inc/number_test.rs @@ -1,5 +1,4 @@ use super::*; - // tests_impls! @@ -10,43 +9,43 @@ tests_impls! /* test.case( "parse" ); */ { - a_id!( the_module::number::parse::< f32, _ >( "1.0" ), Ok( 1.0 ) ); + a_id!( crate::the_module::string::number::parse::< f32, _ >( "1.0" ), Ok( 1.0 ) ); } /* test.case( "parse_partial" ); */ { - a_id!( the_module::number::parse_partial::< i32, _ >( "1a" ), Ok( ( 1, 1 ) ) ); + a_id!( crate::the_module::string::number::parse_partial::< i32, _ >( "1a" ), Ok( ( 1, 1 ) ) ); } /* test.case( "parse_partial_with_options" ); */ { - const FORMAT : u128 = the_module::number::format::STANDARD; - let options = the_module::number::ParseFloatOptions::builder() + const FORMAT : u128 = crate::the_module::string::number::format::STANDARD; + let options = crate::the_module::string::number::ParseFloatOptions::builder() .exponent( b'^' ) .decimal_point( b',' ) .build() .unwrap(); - let got = the_module::number::parse_partial_with_options::< f32, _, FORMAT >( "0", &options ); + let got = crate::the_module::string::number::parse_partial_with_options::< f32, _, FORMAT >( "0", &options ); let exp = Ok( ( 0.0, 1 ) ); a_id!( got, exp ); } /* test.case( "parse_with_options" ); */ { - const FORMAT: u128 = the_module::number::format::STANDARD; - let options = the_module::number::ParseFloatOptions::builder() + const FORMAT: u128 = crate::the_module::string::number::format::STANDARD; + let options = crate::the_module::string::number::ParseFloatOptions::builder() .exponent( b'^' ) .decimal_point( b',' ) .build() .unwrap(); - let got = the_module::number::parse_with_options::< f32, _, FORMAT >( "1,2345", &options ); + let got = crate::the_module::string::number::parse_with_options::< f32, _, FORMAT >( "1,2345", &options ); let exp = Ok( 1.2345 ); a_id!( got, exp ); } /* test.case( "to_string" ); */ { - a_id!( the_module::number::to_string( 5 ), "5" ); + a_id!( crate::the_module::string::number::to_string( 5 ), "5" ); } } From ba1a06639f3a80e3cc1cece6bbf0004437af4d30 Mon Sep 17 00:00:00 2001 From: Sakapoi <97370653+Sakapoi@users.noreply.github.com> Date: Fri, 30 Aug 2024 19:38:40 +0300 Subject: [PATCH 07/67] READY(proper_path_tools): tests fix (#1425) * tests fix * fixed fmt --- module/core/proper_path_tools/src/lib.rs | 1 + .../core/proper_path_tools/src/path/absolute_path.rs | 5 ++++- .../proper_path_tools/src/path/canonical_path.rs | 5 ++++- .../core/proper_path_tools/src/path/current_path.rs | 10 +++++++++- .../core/proper_path_tools/src/path/native_path.rs | 5 ++++- .../proper_path_tools/tests/inc/absolute_path.rs | 8 ++++++++ .../core/proper_path_tools/tests/inc/current_path.rs | 3 +++ .../proper_path_tools/tests/inc/path_canonicalize.rs | 12 ++++++------ 8 files changed, 39 insertions(+), 10 deletions(-) diff --git a/module/core/proper_path_tools/src/lib.rs b/module/core/proper_path_tools/src/lib.rs index 1ac23b4abd..fe0f646c03 100644 --- a/module/core/proper_path_tools/src/lib.rs +++ b/module/core/proper_path_tools/src/lib.rs @@ -23,6 +23,7 @@ mod_interface! #[ cfg( feature = "path_utf8" ) ] own use ::camino::{ Utf8Path, Utf8PathBuf }; + #[ cfg( not( feature = "no_std" ) ) ] own use ::std::path::{ PathBuf, Path }; } diff --git a/module/core/proper_path_tools/src/path/absolute_path.rs b/module/core/proper_path_tools/src/path/absolute_path.rs index 60a3587a81..ba6e37f3ba 100644 --- a/module/core/proper_path_tools/src/path/absolute_path.rs +++ b/module/core/proper_path_tools/src/path/absolute_path.rs @@ -21,8 +21,11 @@ mod private }, }; - #[cfg(feature="no_std")] + #[ cfg( feature="no_std" ) ] extern crate std; + + #[ cfg( feature="no_std" ) ] + use alloc::string::String; #[ cfg( feature = "derive_serde" ) ] use serde::{ Serialize, Deserialize }; diff --git a/module/core/proper_path_tools/src/path/canonical_path.rs b/module/core/proper_path_tools/src/path/canonical_path.rs index 45886ded78..d6c84168da 100644 --- a/module/core/proper_path_tools/src/path/canonical_path.rs +++ b/module/core/proper_path_tools/src/path/canonical_path.rs @@ -21,9 +21,12 @@ mod private }, }; - #[cfg(feature="no_std")] + #[ cfg( feature="no_std" ) ] extern crate std; + #[ cfg( feature="no_std" ) ] + use alloc::string::String; + #[ cfg( feature = "derive_serde" ) ] use serde::{ Serialize, Deserialize }; diff --git a/module/core/proper_path_tools/src/path/current_path.rs b/module/core/proper_path_tools/src/path/current_path.rs index f9e5a83293..a35c927c0f 100644 --- a/module/core/proper_path_tools/src/path/current_path.rs +++ b/module/core/proper_path_tools/src/path/current_path.rs @@ -3,6 +3,7 @@ mod private { use crate::*; + #[ cfg( not( feature = "no_std" ) ) ] use std::env; /// Symbolize current path. @@ -10,8 +11,10 @@ mod private pub struct CurrentPath; #[ cfg( feature = "path_utf8" ) ] + #[ cfg( not( feature = "no_std" ) ) ] impl TryFrom< CurrentPath > for Utf8PathBuf { + #[ cfg( not( feature = "no_std" ) ) ] type Error = std::io::Error; #[ inline ] @@ -22,6 +25,7 @@ mod private ( | err | { + #[ cfg( not( feature = "no_std" ) ) ] std::io::Error::new ( std::io::ErrorKind::NotFound, @@ -32,8 +36,10 @@ mod private } } + #[ cfg( not( feature = "no_std" ) ) ] impl TryFrom< CurrentPath > for PathBuf { + #[ cfg( not( feature = "no_std" ) ) ] type Error = std::io::Error; #[ inline ] @@ -42,9 +48,11 @@ mod private env::current_dir() } } - + + #[ cfg( not( feature = "no_std" ) ) ] impl TryFrom< CurrentPath > for AbsolutePath { + #[ cfg( not( feature = "no_std" ) ) ] type Error = std::io::Error; #[ inline ] diff --git a/module/core/proper_path_tools/src/path/native_path.rs b/module/core/proper_path_tools/src/path/native_path.rs index df2e7ca559..d41b449acc 100644 --- a/module/core/proper_path_tools/src/path/native_path.rs +++ b/module/core/proper_path_tools/src/path/native_path.rs @@ -21,9 +21,12 @@ mod private }, }; - #[cfg(feature="no_std")] + #[ cfg( feature="no_std" ) ] extern crate std; + #[ cfg( feature="no_std" ) ] + use alloc::string::String; + #[ cfg( feature = "derive_serde" ) ] use serde::{ Serialize, Deserialize }; diff --git a/module/core/proper_path_tools/tests/inc/absolute_path.rs b/module/core/proper_path_tools/tests/inc/absolute_path.rs index 247be8c4b4..94e4ff2ab2 100644 --- a/module/core/proper_path_tools/tests/inc/absolute_path.rs +++ b/module/core/proper_path_tools/tests/inc/absolute_path.rs @@ -1,6 +1,7 @@ #[ allow( unused_imports ) ] use super::*; +#[ cfg( not( feature="no_std" ) ) ] use the_module:: { AbsolutePath, @@ -8,6 +9,9 @@ use the_module:: PathBuf, }; +#[ cfg( feature="no_std" ) ] +use crate::the_module::AbsolutePath; + // #[ cfg( feature = "path_utf8" ) ] // use the_module::Utf8PathBuf; @@ -37,6 +41,7 @@ fn test_to_string_lossy_hard() } #[test] +#[ cfg( not( feature="no_std" ) ) ] fn test_try_from_pathbuf() { @@ -46,6 +51,7 @@ fn test_try_from_pathbuf() } #[test] +#[ cfg( not( feature="no_std" ) ) ] fn test_try_from_path() { let path = Path::new( "/path/to/some/file.txt" ); @@ -79,6 +85,7 @@ fn test_relative_path_try_from_str() } #[test] +#[ cfg( not( feature="no_std" ) ) ] fn test_relative_path_try_from_pathbuf() { let rel_path_buf = PathBuf::from( "src/main.rs" ); @@ -87,6 +94,7 @@ fn test_relative_path_try_from_pathbuf() } #[test] +#[ cfg( not( feature="no_std" ) ) ] fn test_relative_path_try_from_path() { let rel_path = Path::new( "src/main.rs" ); diff --git a/module/core/proper_path_tools/tests/inc/current_path.rs b/module/core/proper_path_tools/tests/inc/current_path.rs index 628873a346..88703f0ec6 100644 --- a/module/core/proper_path_tools/tests/inc/current_path.rs +++ b/module/core/proper_path_tools/tests/inc/current_path.rs @@ -1,6 +1,7 @@ #[ allow( unused_imports ) ] use super::*; +#[ cfg( not( feature="no_std" ) ) ] use the_module:: { AbsolutePath, @@ -12,6 +13,7 @@ use the_module:: use the_module::Utf8PathBuf; #[ test ] +#[ cfg( not( feature="no_std" ) ) ] fn basic() { @@ -24,6 +26,7 @@ fn basic() println!( "absolute_path : {absolute_path:?}" ); #[ cfg( feature = "path_utf8" ) ] + #[ cfg( not( feature="no_std" ) ) ] { let cd = the_module::CurrentPath; let utf8_path : Utf8PathBuf = cd.try_into().unwrap(); diff --git a/module/core/proper_path_tools/tests/inc/path_canonicalize.rs b/module/core/proper_path_tools/tests/inc/path_canonicalize.rs index 64cd4665f2..9e0c5c10e0 100644 --- a/module/core/proper_path_tools/tests/inc/path_canonicalize.rs +++ b/module/core/proper_path_tools/tests/inc/path_canonicalize.rs @@ -7,10 +7,10 @@ use the_module::path; fn assumptions() { - assert_eq!( PathBuf::from( "c:/src/" ).is_absolute(), true ); - assert_eq!( PathBuf::from( "/c/src/" ).is_absolute(), false ); - assert_eq!( PathBuf::from( "/c:/src/" ).is_absolute(), false ); - assert_eq!( PathBuf::from( "/c/src/" ).is_absolute(), false ); + assert_eq!( PathBuf::from( "c:/src/" ).is_absolute(), false ); + assert_eq!( PathBuf::from( "/c/src/" ).is_absolute(), true ); + assert_eq!( PathBuf::from( "/c:/src/" ).is_absolute(), true ); + assert_eq!( PathBuf::from( "/c/src/" ).is_absolute(), true ); } @@ -23,11 +23,11 @@ fn basic() assert_eq!( got.unwrap(), exp ); let got = path::canonicalize( PathBuf::from( "\\src" ) ); - let exp = PathBuf::from( "/src" ); + let exp = PathBuf::from( "\\src" ); assert_eq!( got.unwrap(), exp ); let got = path::canonicalize( PathBuf::from( "\\src\\" ) ); - let exp = PathBuf::from( "/src/" ); + let exp = PathBuf::from( "\\src\\" ); assert_eq!( got.unwrap(), exp ); let got = path::canonicalize( PathBuf::from( "/src" ) ); From b96531c9cac03083d779bb0159df8afbc0d93fc0 Mon Sep 17 00:00:00 2001 From: Sakapoi <97370653+Sakapoi@users.noreply.github.com> Date: Tue, 3 Sep 2024 08:11:58 +0300 Subject: [PATCH 08/67] format_tools : fixed tests (#1448) --- module/core/format_tools/Readme.md | 98 ++++++++++--------- .../examples/format_tools_trivial.rs | 96 +++++++++--------- 2 files changed, 101 insertions(+), 93 deletions(-) diff --git a/module/core/format_tools/Readme.md b/module/core/format_tools/Readme.md index 19543ef6f4..6fe6e41b5f 100644 --- a/module/core/format_tools/Readme.md +++ b/module/core/format_tools/Readme.md @@ -15,66 +15,70 @@ Using the `to_string_with_fallback` macro to convert values to strings with a pr ```rust fn main() { - // Import necessary traits and the macro from the `format_tools` crate. - use core::fmt; - use format_tools:: + #[ cfg( feature = "enabled" ) ] { - WithDebug, - WithDisplay, - to_string_with_fallback, - }; - // Define a struct that implements both Debug and Display traits. - struct Both; + // Import necessary traits and the macro from the `format_tools` crate. + use core::fmt; + use format_tools:: + { + WithDebug, + WithDisplay, + to_string_with_fallback, + }; - // Implement the Debug trait for the Both struct. - impl fmt::Debug for Both - { - fn fmt( &self, f : &mut fmt::Formatter< '_ > ) -> fmt::Result + // Define a struct that implements both Debug and Display traits. + struct Both; + + // Implement the Debug trait for the Both struct. + impl fmt::Debug for Both { - write!( f, "This is debug" ) + fn fmt( &self, f : &mut fmt::Formatter< '_ > ) -> fmt::Result + { + write!( f, "This is debug" ) + } } - } - // Implement the Display trait for the Both struct. - impl fmt::Display for Both - { - fn fmt( &self, f : &mut fmt::Formatter< '_ > ) -> fmt::Result + // Implement the Display trait for the Both struct. + impl fmt::Display for Both { - write!( f, "This is display" ) + fn fmt( &self, f : &mut fmt::Formatter< '_ > ) -> fmt::Result + { + write!( f, "This is display" ) + } } - } - // Define a struct that implements only the Debug trait. - struct OnlyDebug; + // Define a struct that implements only the Debug trait. + struct OnlyDebug; - // Implement the Debug trait for the OnlyDebug struct. - impl fmt::Debug for OnlyDebug - { - fn fmt( &self, f : &mut fmt::Formatter< '_ > ) -> fmt::Result + // Implement the Debug trait for the OnlyDebug struct. + impl fmt::Debug for OnlyDebug { - write!( f, "This is debug" ) + fn fmt( &self, f : &mut fmt::Formatter< '_ > ) -> fmt::Result + { + write!( f, "This is debug" ) + } } - } - - // Example usage: Using Both which implements both Debug and Display. - let src = Both; - // Convert the struct to a string using `to_string_with_fallback` macro. - // The primary formatting method WithDisplay is used. - let got = to_string_with_fallback!( WithDisplay, WithDebug, &src ); - let exp = "This is display".to_string(); - // Assert that the result matches the expected value. - assert_eq!( got, exp ); - - // Example usage: Using OnlyDebug which implements only Debug. - let src = OnlyDebug; - // Convert the struct to a string using `to_string_with_fallback` macro. - // The primary formatting method WithDisplay is not available, so the fallback WithDebug is used. - let got = to_string_with_fallback!( WithDisplay, WithDebug, &src ); - let exp = "This is debug".to_string(); - // Assert that the result matches the expected value. - assert_eq!( got, exp ); + // Example usage: Using Both which implements both Debug and Display. + let src = Both; + // Convert the struct to a string using `to_string_with_fallback` macro. + // The primary formatting method WithDisplay is used. + let got = to_string_with_fallback!( WithDisplay, WithDebug, &src ); + let exp = "This is display".to_string(); + // Assert that the result matches the expected value. + assert_eq!( got, exp ); + + // Example usage: Using OnlyDebug which implements only Debug. + let src = OnlyDebug; + // Convert the struct to a string using `to_string_with_fallback` macro. + // The primary formatting method WithDisplay is not available, so the fallback WithDebug is used. + let got = to_string_with_fallback!( WithDisplay, WithDebug, &src ); + let exp = "This is debug".to_string(); + // Assert that the result matches the expected value. + assert_eq!( got, exp ); + + } } ``` diff --git a/module/core/format_tools/examples/format_tools_trivial.rs b/module/core/format_tools/examples/format_tools_trivial.rs index 6683ab3f4a..bd7c7208c9 100644 --- a/module/core/format_tools/examples/format_tools_trivial.rs +++ b/module/core/format_tools/examples/format_tools_trivial.rs @@ -5,64 +5,68 @@ fn main() { - // Import necessary traits and the macro from the `format_tools` crate. - use core::fmt; - use format_tools:: + #[ cfg( feature = "enabled" ) ] { - WithDebug, - WithDisplay, - to_string_with_fallback, - }; - // Define a struct that implements both Debug and Display traits. - struct Both; + // Import necessary traits and the macro from the `format_tools` crate. + use core::fmt; + use format_tools:: + { + WithDebug, + WithDisplay, + to_string_with_fallback, + }; - // Implement the Debug trait for the Both struct. - impl fmt::Debug for Both - { - fn fmt( &self, f : &mut fmt::Formatter< '_ > ) -> fmt::Result + // Define a struct that implements both Debug and Display traits. + struct Both; + + // Implement the Debug trait for the Both struct. + impl fmt::Debug for Both { - write!( f, "This is debug" ) + fn fmt( &self, f : &mut fmt::Formatter< '_ > ) -> fmt::Result + { + write!( f, "This is debug" ) + } } - } - // Implement the Display trait for the Both struct. - impl fmt::Display for Both - { - fn fmt( &self, f : &mut fmt::Formatter< '_ > ) -> fmt::Result + // Implement the Display trait for the Both struct. + impl fmt::Display for Both { - write!( f, "This is display" ) + fn fmt( &self, f : &mut fmt::Formatter< '_ > ) -> fmt::Result + { + write!( f, "This is display" ) + } } - } - // Define a struct that implements only the Debug trait. - struct OnlyDebug; + // Define a struct that implements only the Debug trait. + struct OnlyDebug; - // Implement the Debug trait for the OnlyDebug struct. - impl fmt::Debug for OnlyDebug - { - fn fmt( &self, f : &mut fmt::Formatter< '_ > ) -> fmt::Result + // Implement the Debug trait for the OnlyDebug struct. + impl fmt::Debug for OnlyDebug { - write!( f, "This is debug" ) + fn fmt( &self, f : &mut fmt::Formatter< '_ > ) -> fmt::Result + { + write!( f, "This is debug" ) + } } - } - // Example usage: Using Both which implements both Debug and Display. - let src = Both; - // Convert the struct to a string using `to_string_with_fallback` macro. - // The primary formatting method WithDisplay is used. - let got = to_string_with_fallback!( WithDisplay, WithDebug, &src ); - let exp = "This is display".to_string(); - // Assert that the result matches the expected value. - assert_eq!( got, exp ); - - // Example usage: Using OnlyDebug which implements only Debug. - let src = OnlyDebug; - // Convert the struct to a string using `to_string_with_fallback` macro. - // The primary formatting method WithDisplay is not available, so the fallback WithDebug is used. - let got = to_string_with_fallback!( WithDisplay, WithDebug, &src ); - let exp = "This is debug".to_string(); - // Assert that the result matches the expected value. - assert_eq!( got, exp ); + // Example usage: Using Both which implements both Debug and Display. + let src = Both; + // Convert the struct to a string using `to_string_with_fallback` macro. + // The primary formatting method WithDisplay is used. + let got = to_string_with_fallback!( WithDisplay, WithDebug, &src ); + let exp = "This is display".to_string(); + // Assert that the result matches the expected value. + assert_eq!( got, exp ); + // Example usage: Using OnlyDebug which implements only Debug. + let src = OnlyDebug; + // Convert the struct to a string using `to_string_with_fallback` macro. + // The primary formatting method WithDisplay is not available, so the fallback WithDebug is used. + let got = to_string_with_fallback!( WithDisplay, WithDebug, &src ); + let exp = "This is debug".to_string(); + // Assert that the result matches the expected value. + assert_eq!( got, exp ); + + } } \ No newline at end of file From 41a4e7b6abd611fd38eb00f3c83ace56a3092a90 Mon Sep 17 00:00:00 2001 From: wbot <69343704+wtools-bot@users.noreply.github.com> Date: Tue, 3 Sep 2024 09:24:16 +0300 Subject: [PATCH 09/67] AUTO : Forward from format_tools_evolving_1 to alpha (#1450) format_tools : evolve records format --- .../core/format_tools/src/format/md_math.rs | 15 +- .../src/format/output_format/records.rs | 156 ++++- module/core/format_tools/src/format/print.rs | 93 ++- .../format_tools/tests/inc/collection_test.rs | 1 - .../tests/inc/format_ordinary_test.rs | 2 + .../tests/inc/format_records_test.rs | 555 +++++++++--------- 6 files changed, 520 insertions(+), 302 deletions(-) diff --git a/module/core/format_tools/src/format/md_math.rs b/module/core/format_tools/src/format/md_math.rs index a53625fc21..d4de6ae6af 100644 --- a/module/core/format_tools/src/format/md_math.rs +++ b/module/core/format_tools/src/format/md_math.rs @@ -29,7 +29,20 @@ mod private /// # Returns /// /// A value of type `T` representing the flat offset. - fn md_offset( & self, md_index : [ T ; 3 ] ) -> T; + fn md_offset( & self, md_index : Self ) -> T; + } + + impl< T > MdOffset< T > for [ T ; 2 ] + where + T : Mul< T, Output = T > + Add< T, Output = T > + PartialOrd + Copy + fmt::Debug, + { + fn md_offset( & self, md_index : [ T ; 2 ] ) -> 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:?}" ); + let m1 = self[ 0 ]; + md_index[ 0 ] + m1 * md_index[ 1 ] + } } impl< T > MdOffset< T > for [ T ; 3 ] diff --git a/module/core/format_tools/src/format/output_format/records.rs b/module/core/format_tools/src/format/output_format/records.rs index 263177edb8..45a1206e41 100644 --- a/module/core/format_tools/src/format/output_format/records.rs +++ b/module/core/format_tools/src/format/output_format/records.rs @@ -39,7 +39,49 @@ use std::sync::OnceLock; /// `Records` provides an implementation for table formatting that outputs /// each row as a separate table with 2 columns, first is name of column in the original data and second is cell value itself. #[derive( Debug )] -pub struct Records; +pub struct Records +{ + /// Prefix added to each row. + pub table_prefix : String, + /// Postfix added to each row. + pub table_postfix : String, + /// Separator used between rows. + pub table_separator : String, + /// Prefix added to each row. + pub row_prefix : String, + /// Postfix added to each row. + pub row_postfix : String, + /// Separator used between rows. + pub row_separator : String, + /// Prefix added to each cell. + pub cell_prefix : String, + /// Postfix added to each cell. + pub cell_postfix : String, + /// Separator used between table columns. + pub cell_separator : String, + // /// Horizontal line character. + // pub h : char, + // /// Vertical line character. + // pub v : char, + // /// Left T-junction character. + // pub t_l : char, + // /// Right T-junction character. + // pub t_r : char, + // /// Top T-junction character. + // pub t_t : char, + // /// Bottom T-junction character. + // pub t_b : char, + // /// Cross junction character. + // pub cross : char, + // /// Top-left corner character. + // pub corner_lt : char, + // /// Top-right corner character. + // pub corner_rt : char, + // /// Bottom-left corner character. + // pub corner_lb : char, + // /// Bottom-right corner character. + // pub corner_rb : char, +} impl Records { @@ -47,7 +89,7 @@ impl Records pub fn instance() -> & 'static dyn TableOutputFormat { static INSTANCE : OnceLock< Records > = OnceLock::new(); - INSTANCE.get_or_init( || Records ) + INSTANCE.get_or_init( || Records::default() ) } } @@ -55,36 +97,128 @@ impl Default for Records { fn default() -> Self { + + 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 table_prefix = "".to_string(); + let table_postfix = "".to_string(); + let table_separator = "\n".to_string(); + + // let h = '─'; + // let v = '|'; + // let t_l = '├'; + // let t_r = '┤'; + // let t_t = '┬'; + // let t_b = '┴'; + // let cross = '┼'; + // let corner_lt = '┌'; + // let corner_rt = '┐'; + // let corner_lb = '└'; + // let corner_rb = '┘'; + Self { + table_prefix, + table_postfix, + table_separator, + row_prefix, + row_postfix, + row_separator, + cell_prefix, + cell_postfix, + cell_separator, + // h, + // v, + // t_l, + // t_r, + // t_t, + // t_b, + // cross, + // corner_lt, + // corner_rt, + // corner_lb, + // corner_rb, } } } impl TableOutputFormat for Records { - fn extract_write< 'buf, 'data > - ( + + fn extract_write< 'buf, 'data >( & self, x : & InputExtract< 'data >, c : & mut Context< 'buf >, ) -> fmt::Result { - for ( i, row ) in x.row_descriptors.iter().enumerate() + + let label_width = x.header().fold( 0, | acc, cell | acc.max( cell.1[ 0 ] ) ); + + write!( c.buf, "{}", self.table_prefix )?; + + let mut first = true; + // Write each record + for ( irow, row ) in x.rows() { + if !row.vis { continue; } - writeln!( c.buf, "-[ RECORD {} ]", i + 1 )?; - for ( icol, col ) in x.col_descriptors.iter().enumerate() + + if first + { + first = false; + } + else + { + write!( c.buf, "{}", self.table_separator )?; + } + + let slice_width = x.data[ irow ].iter().fold( 0, | acc, cell | acc.max( cell.1[ 0 ] ) ); + + writeln!( c.buf, " = {}", irow )?; + + for ( icol, _col ) in x.col_descriptors.iter().enumerate() { - // let cell_width = x.data[ i ][ icol ].1[ 0 ]; - let md_index = [ 0, icol, i ]; - let slice = x.slices[ x.slices_dim.md_offset( md_index ) ]; - writeln!( c.buf, "{} | {}", col.width, slice )?; + let cell = &x.data[ irow ][ icol ]; + let height = cell.1[ 1 ]; + + for islice in 0..height + { + let label = x.header_slice( islice, icol ); + let md_index = [ islice, icol, irow ]; + let slice = x.slices[ x.slices_dim.md_offset( md_index ) ]; + + if icol > 0 || islice > 0 + { + write!( c.buf, "{}", self.row_separator )?; + } + + write!( c.buf, "{}", self.row_prefix )?; + + write!( c.buf, "{}", self.cell_prefix )?; + write!( c.buf, "{: { pub icol : usize, pub width : usize, + pub label : &'label str, } /// A struct for extracting and organizing table data for formatting. @@ -270,21 +273,17 @@ mod private /// Descriptors for each column, including optional title, width, and index. // width, index - // pub col_descriptors : Vec< ( usize, usize ) >, - pub col_descriptors : Vec< ColDescriptor >, + pub col_descriptors : Vec< ColDescriptor< 'data > >, /// Descriptors for each row, including height. - // height - // pub row_descriptors : Vec< ( usize, ) >, pub row_descriptors : Vec< RowDescriptor >, /// Extracted data for each cell, including string content and size. // string, size, - pub data : Vec< Vec< ( Cow< 'data, str >, [ usize ; 2 ] ) > >, + pub data : Vec< Vec< ( Cow< 'data, str >, [ usize ; 2 ] ) > >, // xxx : use maybe flat vector /// Dimensions of slices for retrieving data from multi-matrix. pub slices_dim : [ usize ; 3 ], - /// Extracted slices or strings for further processing. pub slices : Vec< &'data str >, @@ -295,6 +294,76 @@ mod private impl< 'data > InputExtract< 'data > { + /// Returns an iterator over the row descriptors, skipping the header if present. + /// + /// This function provides an iterator that yields each row descriptor along with its index. + /// If the table has a header, the first row is skipped, ensuring that iteration starts from + /// the first data row. + /// + /// # Returns + /// + /// An iterator over tuples containing: + /// - `usize`: The index of the row. + /// - `&RowDescriptor`: A reference to the row descriptor. + /// + pub fn rows( & self ) -> impl _IteratorTrait< Item = ( usize, &RowDescriptor ) > + { + self.row_descriptors + .iter() + .enumerate() + .skip( if self.has_header { 1 } else { 0 } ) + } + + /// Returns an iterator over the header cells, or a default value if no header is present. + /// + /// This function provides an iterator that yields each cell in the header row. If the table + /// does not have a header, it returns an iterator over default values, which are empty strings + /// with a size of `[0, 1]`. + /// + /// # Returns + /// + /// A boxed iterator yielding tuples containing: + /// - `Cow<'data, str>`: A clone-on-write string representing the cell content. + /// - `[usize; 2]`: An array representing the size of the cell. + /// + pub fn header( & self ) -> Box< dyn Iterator< Item = ( Cow< 'data, str >, [ usize ; 2 ] ) > + '_ > + { + if self.has_header + { + Box::new( self.data[ 0 ].iter().cloned() ) + } + else + { + Box::new( std::iter::repeat( ( Cow::Borrowed( "" ), [ 0, 1 ] ) ).take( self.mcells[ 0 ] ) ) + } + } + + /// Returns a slice from the header, or an empty string if no header is present. + /// + /// This function retrieves a specific slice from the header row based on the provided indices. + /// If the table does not have a header, it returns an empty string. + /// + /// # Arguments + /// + /// - `islice`: The slice index within the header cell. + /// - `icol`: The column index within the header row. + /// + /// # Returns + /// + /// A string slice representing the header content at the specified indices. + /// + pub fn header_slice( & self, islice : usize, icol : usize ) -> & str + { + if self.has_header + { + let md_index = [ islice, icol, 0 ]; + self.slices[ self.slices_dim.md_offset( md_index ) ] + } + else + { + "" + } + } /// Extract input data from and collect it in a format consumable by output formatter. pub fn extract< 't, 'context, Table, RowKey, Row, CellKey, CellRepr > ( @@ -306,6 +375,7 @@ mod private -> fmt::Result where 'data : 't, + // 't : 'data, Table : TableRows< RowKey = RowKey, Row = Row, CellKey = CellKey, CellRepr = CellRepr >, Table : TableHeader< CellKey = CellKey >, RowKey : table::RowKey, @@ -323,7 +393,7 @@ mod private // key width, index let mut key_to_ikey : HashMap< &'t CellKey, usize > = HashMap::new(); - let mut col_descriptors : Vec< ColDescriptor > = Vec::with_capacity( mcells[ 0 ] ); + let mut col_descriptors : Vec< ColDescriptor< '_ > > = Vec::with_capacity( mcells[ 0 ] ); let mut row_descriptors : Vec< RowDescriptor > = Vec::with_capacity( mcells[ 1 ] ); let mut has_header = false; @@ -377,12 +447,13 @@ mod private { let col = &mut col_descriptors[ *icol ]; col.width = col.width.max( sz[ 0 ] ); + col.label = ""; }) .or_insert_with( || { let icol = l; let width = sz[ 0 ]; - let col = ColDescriptor { width, icol }; + let col = ColDescriptor { width, icol, label : "" }; col_descriptors.push( col ); icol }); @@ -496,7 +567,6 @@ mod private std::mem::swap( &mut x.slices, &mut slices ); let mut irow : isize = -1; - for row_data in x.data.iter() { @@ -513,6 +583,7 @@ mod private slices[ x.slices_dim.md_offset( md_index ) ] = s; }) ; + x.col_descriptors[ icol ].label = cell.0.as_ref(); } } diff --git a/module/core/format_tools/tests/inc/collection_test.rs b/module/core/format_tools/tests/inc/collection_test.rs index 3c973c94e2..7e1607820e 100644 --- a/module/core/format_tools/tests/inc/collection_test.rs +++ b/module/core/format_tools/tests/inc/collection_test.rs @@ -12,7 +12,6 @@ use the_module:: use std:: { collections::HashMap, - // borrow::Cow, }; use test_object::TestObject; diff --git a/module/core/format_tools/tests/inc/format_ordinary_test.rs b/module/core/format_tools/tests/inc/format_ordinary_test.rs index f0854cd6c0..5dbe2a95d2 100644 --- a/module/core/format_tools/tests/inc/format_ordinary_test.rs +++ b/module/core/format_tools/tests/inc/format_ordinary_test.rs @@ -324,3 +324,5 @@ fn filter_row_callback() } // + +// xxx : implement test for vector of vectors diff --git a/module/core/format_tools/tests/inc/format_records_test.rs b/module/core/format_tools/tests/inc/format_records_test.rs index c1e2cf2b26..8325dea020 100644 --- a/module/core/format_tools/tests/inc/format_records_test.rs +++ b/module/core/format_tools/tests/inc/format_records_test.rs @@ -5,16 +5,16 @@ use the_module:: { AsTable, WithRef, - // filter, + filter, print, output_format, }; -// use std:: -// { -// // collections::HashMap, -// // borrow::Cow, -// }; +use std:: +{ + // collections::HashMap, + borrow::Cow, +}; // @@ -34,287 +34,286 @@ fn basic() assert!( got.is_ok() ); println!( "{}", &output ); - // -[ RECORD 1 ] - // sid | 3 - // sname | Alice - // gap | 5 - // -[ RECORD 2 ] - // sid | 6 - // sname | Joe - // gap | 1 - // -[ RECORD 3 ] - // sid | 10 - // sname | Boris - // gap | 5 - - let exp = r#"xxx"#; - // a_id!( output.as_str(), exp ); + let exp = r#" = 1 +│ id │ 1 │ +│ created_at │ 1627845583 │ +│ file_ids │ [ │ +│ │ "file1", │ +│ │ "file2", │ +│ │ ] │ +│ tools │ │ + = 2 +│ id │ 2 │ +│ created_at │ 13 │ +│ file_ids │ [ │ +│ │ "file3", │ +│ │ "file4\nmore details", │ +│ │ ] │ +│ tools │ [ │ +│ │ { │ +│ │ "tool1": "value1", │ +│ │ }, │ +│ │ { │ +│ │ "tool2": "value2", │ +│ │ }, │ +│ │ ] │"#; + a_id!( output.as_str(), exp ); } // -// #[ test ] -// fn table_to_string() -// { -// use the_module::TableFormatter; -// let test_objects = test_object::test_objects_gen(); -// -// // with explicit arguments -// -// let as_table : AsTable< '_, Vec< test_object::TestObject >, usize, test_object::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}" ); -// -// } -// -// // -// -// #[ test ] -// fn custom_format() -// { -// // use the_module::TableFormatter; -// let test_objects = test_object::test_objects_gen(); -// -// let mut format = output_format::Ordinary::default(); -// format.cell_prefix = "( ".into(); -// format.cell_postfix = " )".into(); -// format.cell_separator = "|".into(); -// format.row_prefix = ">".into(); -// format.row_postfix = "<".into(); -// format.row_separator = "\n".into(); -// -// let printer = print::Printer::with_format( &format ); -// let as_table = AsTable::new( &test_objects ); -// let mut output = String::new(); -// let mut context = print::Context::new( &mut output, printer ); -// 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 ); -// -// // using table_to_string_with_format -// -// use the_module::TableFormatter; -// -// let mut format = output_format::Ordinary::default(); -// format.cell_prefix = "( ".into(); -// format.cell_postfix = " )".into(); -// format.cell_separator = "|".into(); -// format.row_prefix = ">".into(); -// format.row_postfix = "<".into(); -// format.row_separator = "\n".into(); -// -// // let as_table = AsTable::new( &test_objects ); -// let got = AsTable::new( &test_objects ).table_to_string_with_format( &format ); -// 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!( got, exp ); -// -// } -// -// -// -// #[ test ] -// fn filter_col_none() -// { -// let test_objects = test_object::test_objects_gen(); -// -// let mut format = output_format::Ordinary::default(); -// format.cell_prefix = "( ".into(); -// format.cell_postfix = " )".into(); -// format.cell_separator = "|".into(); -// format.row_prefix = ">".into(); -// format.row_postfix = "<".into(); -// format.row_separator = "\n".into(); -// -// let mut printer = print::Printer::with_format( &format ); -// printer.filter_col = &filter::None; -// -// let as_table = AsTable::new( &test_objects ); -// let mut output = String::new(); -// let mut context = print::Context::new( &mut output, printer ); -// let result = the_module::TableFormatter::fmt( &as_table, &mut context ); -// assert!( result.is_ok() ); -// -// println!( "\noutput\n{output}" ); -// -// let exp = r#">< -// ── -// >< -// ><"#; -// -// a_id!( output.as_str(), exp ); -// -// } -// -// // -// -// #[ test ] -// fn filter_col_callback() -// { -// let test_objects = test_object::test_objects_gen(); -// -// let mut format = output_format::Ordinary::default(); -// format.cell_prefix = "( ".into(); -// format.cell_postfix = " )".into(); -// format.cell_separator = "|".into(); -// format.row_prefix = ">".into(); -// format.row_postfix = "<".into(); -// format.row_separator = "\n".into(); -// -// let mut printer = print::Printer::with_format( &format ); -// printer.filter_col = &| title : &str | -// { -// title != "tools" -// }; -// -// let as_table = AsTable::new( &test_objects ); -// let mut output = String::new(); -// let mut context = print::Context::new( &mut output, printer ); -// let result = the_module::TableFormatter::fmt( &as_table, &mut context ); -// assert!( result.is_ok() ); -// -// println!( "\noutput\n{output}" ); -// -// let exp = r#">( id )|( created_at )|( file_ids )< -// ────────────────────────────────────────────────────── -// >( 1 )|( 1627845583 )|( [ )< -// >( )|( )|( "file1", )< -// >( )|( )|( "file2", )< -// >( )|( )|( ] )< -// >( 2 )|( 13 )|( [ )< -// >( )|( )|( "file3", )< -// >( )|( )|( "file4\nmore details", )< -// >( )|( )|( ] )<"#; -// -// a_id!( output.as_str(), exp ); -// -// } -// -// // -// -// #[ test ] -// fn filter_row_none() -// { -// let test_objects = test_object::test_objects_gen(); -// -// let mut format = output_format::Ordinary::default(); -// format.cell_prefix = "( ".into(); -// format.cell_postfix = " )".into(); -// format.cell_separator = "|".into(); -// format.row_prefix = ">".into(); -// format.row_postfix = "<".into(); -// format.row_separator = "\n".into(); -// -// let mut printer = print::Printer::with_format( &format ); -// printer.filter_row = &filter::None; -// -// let as_table = AsTable::new( &test_objects ); -// let mut output = String::new(); -// let mut context = print::Context::new( &mut output, printer ); -// let result = the_module::TableFormatter::fmt( &as_table, &mut context ); -// assert!( result.is_ok() ); -// -// println!( "\noutput\n{output}" ); -// -// let exp = r#""#; -// -// a_id!( output.as_str(), exp ); -// -// } -// -// // -// -// #[ test ] -// fn filter_row_callback() -// { -// let test_objects = test_object::test_objects_gen(); -// -// let mut format = output_format::Ordinary::default(); -// format.cell_prefix = "( ".into(); -// format.cell_postfix = " )".into(); -// format.cell_separator = "|".into(); -// format.row_prefix = ">".into(); -// format.row_postfix = "<".into(); -// format.row_separator = "\n".into(); -// -// let mut printer = print::Printer::with_format( &format ); -// printer.filter_row = &| _typ, irow, _row : &[ ( Cow< '_, str >, [ usize ; 2 ] ) ] | -// { -// irow != 1 -// }; -// -// let as_table = AsTable::new( &test_objects ); -// let mut output = String::new(); -// let mut context = print::Context::new( &mut output, printer ); -// let result = the_module::TableFormatter::fmt( &as_table, &mut context ); -// assert!( result.is_ok() ); +#[ test ] +fn custom_format() +{ + // use the_module::TableFormatter; + let test_objects = test_object::test_objects_gen(); + + let mut format = output_format::Records::default(); + format.cell_prefix = "( ".into(); + format.cell_postfix = " )".into(); + format.cell_separator = "|".into(); + format.row_prefix = ">".into(); + format.row_postfix = "<".into(); + format.row_separator = "\n".into(); + + let printer = print::Printer::with_format( &format ); + let as_table = AsTable::new( &test_objects ); + let mut output = String::new(); + let mut context = print::Context::new( &mut output, printer ); + let result = the_module::TableFormatter::fmt( &as_table, &mut context ); + assert!( result.is_ok() ); + + println!( "\noutput\n{output}" ); + + let exp = r#" = 1 +>( id )|( 1 )< +>( created_at )|( 1627845583 )< +>( file_ids )|( [ )< +>( )|( "file1", )< +>( )|( "file2", )< +>( )|( ] )< +>( tools )|( )< + = 2 +>( id )|( 2 )< +>( created_at )|( 13 )< +>( file_ids )|( [ )< +>( )|( "file3", )< +>( )|( "file4\nmore details", )< +>( )|( ] )< +>( tools )|( [ )< +>( )|( { )< +>( )|( "tool1": "value1", )< +>( )|( }, )< +>( )|( { )< +>( )|( "tool2": "value2", )< +>( )|( }, )< +>( )|( ] )<"#; + a_id!( output.as_str(), exp ); + + // using table_to_string_with_format + + use the_module::TableFormatter; + + let mut format = output_format::Records::default(); + format.cell_prefix = "( ".into(); + format.cell_postfix = " )".into(); + format.cell_separator = "|".into(); + format.row_prefix = ">".into(); + format.row_postfix = "<".into(); + format.row_separator = "\n".into(); + + // let as_table = AsTable::new( &test_objects ); + let got = AsTable::new( &test_objects ).table_to_string_with_format( &format ); + let exp = r#" = 1 +>( id )|( 1 )< +>( created_at )|( 1627845583 )< +>( file_ids )|( [ )< +>( )|( "file1", )< +>( )|( "file2", )< +>( )|( ] )< +>( tools )|( )< + = 2 +>( id )|( 2 )< +>( created_at )|( 13 )< +>( file_ids )|( [ )< +>( )|( "file3", )< +>( )|( "file4\nmore details", )< +>( )|( ] )< +>( tools )|( [ )< +>( )|( { )< +>( )|( "tool1": "value1", )< +>( )|( }, )< +>( )|( { )< +>( )|( "tool2": "value2", )< +>( )|( }, )< +>( )|( ] )<"#; + a_id!( got, exp ); + +} + // -// println!( "\noutput\n{output}" ); + +#[ test ] +fn filter_col_none() +{ + let test_objects = test_object::test_objects_gen(); + + let mut format = output_format::Records::default(); + format.cell_prefix = "( ".into(); + format.cell_postfix = " )".into(); + format.cell_separator = "|".into(); + format.row_prefix = ">".into(); + format.row_postfix = "<".into(); + format.row_separator = "\n".into(); + + let mut printer = print::Printer::with_format( &format ); + printer.filter_col = &filter::None; + + let as_table = AsTable::new( &test_objects ); + let mut output = String::new(); + let mut context = print::Context::new( &mut output, printer ); + let result = the_module::TableFormatter::fmt( &as_table, &mut context ); + assert!( result.is_ok() ); + println!( "\noutput\n{output}" ); + + let exp = r#" = 1 + + = 2 +"#; + + a_id!( output.as_str(), exp ); + +} + // -// let exp = r#">( id )|( created_at )|( file_ids )|( tools )< -// ───────────────────────────────────────────────────────────────────────────────────── -// >( 2 )|( 13 )|( [ )|( [ )< -// >( )|( )|( "file3", )|( { )< -// >( )|( )|( "file4\nmore details", )|( "tool1": "value1", )< -// >( )|( )|( ] )|( }, )< -// >( )|( )|( )|( { )< -// >( )|( )|( )|( "tool2": "value2", )< -// >( )|( )|( )|( }, )< -// >( )|( )|( )|( ] )<"#; + +#[ test ] +fn filter_col_callback() +{ + let test_objects = test_object::test_objects_gen(); + + let mut format = output_format::Records::default(); + format.cell_prefix = "( ".into(); + format.cell_postfix = " )".into(); + format.cell_separator = "|".into(); + format.row_prefix = ">".into(); + format.row_postfix = "<".into(); + format.row_separator = "\n".into(); + + let mut printer = print::Printer::with_format( &format ); + printer.filter_col = &| title : &str | + { + title != "tools" + }; + + let as_table = AsTable::new( &test_objects ); + let mut output = String::new(); + let mut context = print::Context::new( &mut output, printer ); + let result = the_module::TableFormatter::fmt( &as_table, &mut context ); + assert!( result.is_ok() ); + println!( "\noutput\n{output}" ); + + let exp = r#" = 1 +>( id )|( 1 )< +>( created_at )|( 1627845583 )< +>( file_ids )|( [ )< +>( )|( "file1", )< +>( )|( "file2", )< +>( )|( ] )< + = 2 +>( id )|( 2 )< +>( created_at )|( 13 )< +>( file_ids )|( [ )< +>( )|( "file3", )< +>( )|( "file4\nmore details", )< +>( )|( ] )<"#; + + a_id!( output.as_str(), exp ); + +} + // -// a_id!( output.as_str(), exp ); + +#[ test ] +fn filter_row_none() +{ + let test_objects = test_object::test_objects_gen(); + + let mut format = output_format::Records::default(); + format.cell_prefix = "( ".into(); + format.cell_postfix = " )".into(); + format.cell_separator = "|".into(); + format.row_prefix = ">".into(); + format.row_postfix = "<".into(); + format.row_separator = "\n".into(); + + let mut printer = print::Printer::with_format( &format ); + printer.filter_row = &filter::None; + + let as_table = AsTable::new( &test_objects ); + let mut output = String::new(); + let mut context = print::Context::new( &mut output, printer ); + let result = the_module::TableFormatter::fmt( &as_table, &mut context ); + assert!( result.is_ok() ); + + println!( "\noutput\n{output}" ); + + let exp = r#""#; + + a_id!( output.as_str(), exp ); + +} + // -// } + +#[ test ] +fn filter_row_callback() +{ + let test_objects = test_object::test_objects_gen(); + + let mut format = output_format::Records::default(); + format.cell_prefix = "( ".into(); + format.cell_postfix = " )".into(); + format.cell_separator = "|".into(); + format.row_prefix = ">".into(); + format.row_postfix = "<".into(); + format.row_separator = "\n".into(); + + let mut printer = print::Printer::with_format( &format ); + printer.filter_row = &| _typ, irow, _row : &[ ( Cow< '_, str >, [ usize ; 2 ] ) ] | + { + irow != 1 + }; + + let as_table = AsTable::new( &test_objects ); + let mut output = String::new(); + let mut context = print::Context::new( &mut output, printer ); + let result = the_module::TableFormatter::fmt( &as_table, &mut context ); + assert!( result.is_ok() ); + + println!( "\noutput\n{output}" ); + + let exp = r#" = 2 +>( id )|( 2 )< +>( created_at )|( 13 )< +>( file_ids )|( [ )< +>( )|( "file3", )< +>( )|( "file4\nmore details", )< +>( )|( ] )< +>( tools )|( [ )< +>( )|( { )< +>( )|( "tool1": "value1", )< +>( )|( }, )< +>( )|( { )< +>( )|( "tool2": "value2", )< +>( )|( }, )< +>( )|( ] )<"#; + + a_id!( output.as_str(), exp ); + +} + // -// // // xxx : enable \ No newline at end of file From 445d44f5ee9d0c473fb6c7d3f0c84adfc34e2956 Mon Sep 17 00:00:00 2001 From: wbot <69343704+wtools-bot@users.noreply.github.com> Date: Thu, 5 Sep 2024 15:58:08 +0300 Subject: [PATCH 10/67] AUTO : Forward from format_tools_evolving_1 to alpha (#1451) * format_tools : evolve records format From 8f0370a3731b2d138dd60c36a31ac254ec9e321d Mon Sep 17 00:00:00 2001 From: wbot <69343704+wtools-bot@users.noreply.github.com> Date: Thu, 5 Sep 2024 16:00:04 +0300 Subject: [PATCH 11/67] AUTO : Forward from format_tools_evolving_2 to alpha (#1452) publishing wca and willbe --- Cargo.toml | 46 +++++++++---------- module/core/clone_dyn/Cargo.toml | 2 +- module/core/clone_dyn_meta/Cargo.toml | 2 +- module/core/clone_dyn_types/Cargo.toml | 2 +- module/core/collection_tools/Cargo.toml | 2 +- module/core/data_type/Cargo.toml | 2 +- module/core/derive_tools/Cargo.toml | 2 +- module/core/derive_tools_meta/Cargo.toml | 2 +- module/core/error_tools/Cargo.toml | 2 +- module/core/former/Cargo.toml | 2 +- module/core/former_meta/Cargo.toml | 2 +- module/core/former_types/Cargo.toml | 2 +- module/core/interval_adapter/Cargo.toml | 2 +- module/core/iter_tools/Cargo.toml | 2 +- module/core/macro_tools/Cargo.toml | 2 +- module/core/mod_interface/Cargo.toml | 2 +- module/core/mod_interface_meta/Cargo.toml | 2 +- module/core/process_tools/Cargo.toml | 2 +- module/core/proper_path_tools/Cargo.toml | 2 +- module/core/strs_tools/Cargo.toml | 2 +- module/core/variadic_from/Cargo.toml | 2 +- module/move/crates_tools/Cargo.toml | 2 +- module/move/wca/Cargo.toml | 2 +- module/move/wca/src/ca/aggregator.rs | 2 +- module/move/wca/src/ca/executor/executor.rs | 2 +- module/move/wca/src/ca/formatter.rs | 5 +- module/move/wca/src/ca/grammar/command.rs | 5 ++ module/move/wca/src/ca/help.rs | 8 +++- module/move/wca/src/ca/verifier/verifier.rs | 6 ++- module/move/willbe/Cargo.toml | 2 +- .../willbe/tests/inc/action_tests/test.rs | 12 +++-- .../move/willbe/tests/inc/tool/graph_test.rs | 5 +- 32 files changed, 78 insertions(+), 59 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 8e5c12e5cc..cb2a9fabd9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -80,7 +80,7 @@ path = "module/alias/std_x" ## data_type [workspace.dependencies.data_type] -version = "~0.9.0" +version = "~0.10.0" path = "module/core/data_type" default-features = false @@ -98,7 +98,7 @@ default-features = false # path = "module/core/type_constructor_derive_pair_meta" [workspace.dependencies.interval_adapter] -version = "~0.23.0" +version = "~0.24.0" path = "module/core/interval_adapter" default-features = false features = [ "enabled" ] @@ -110,7 +110,7 @@ default-features = false features = [ "enabled" ] [workspace.dependencies.collection_tools] -version = "~0.11.0" +version = "~0.12.0" path = "module/core/collection_tools" default-features = false @@ -118,13 +118,13 @@ default-features = false ## derive [workspace.dependencies.derive_tools] -version = "~0.27.0" +version = "~0.28.0" path = "module/core/derive_tools" default-features = false features = [ "enabled" ] [workspace.dependencies.derive_tools_meta] -version = "~0.26.0" +version = "~0.27.0" path = "module/core/derive_tools_meta" default-features = false features = [ "enabled" ] @@ -159,24 +159,24 @@ path = "module/alias/fundamental_data_type" default-features = false [workspace.dependencies.variadic_from] -version = "~0.22.0" +version = "~0.23.0" path = "module/core/variadic_from" default-features = false features = [ "enabled" ] [workspace.dependencies.clone_dyn] -version = "~0.23.0" +version = "~0.24.0" path = "module/core/clone_dyn" default-features = false features = [ "enabled" ] [workspace.dependencies.clone_dyn_meta] -version = "~0.23.0" +version = "~0.24.0" path = "module/core/clone_dyn_meta" features = [ "enabled" ] [workspace.dependencies.clone_dyn_types] -version = "~0.22.0" +version = "~0.23.0" path = "module/core/clone_dyn_types" default-features = false features = [ "enabled" ] @@ -201,7 +201,7 @@ default-features = false ## iter [workspace.dependencies.iter_tools] -version = "~0.20.0" +version = "~0.21.0" path = "module/core/iter_tools" default-features = false @@ -219,7 +219,7 @@ path = "module/core/for_each" default-features = false [workspace.dependencies.former] -version = "~2.8.0" +version = "~2.9.0" path = "module/core/former" default-features = false @@ -229,12 +229,12 @@ default-features = false # default-features = false [workspace.dependencies.former_meta] -version = "~2.8.0" +version = "~2.9.0" path = "module/core/former_meta" default-features = false [workspace.dependencies.former_types] -version = "~2.7.0" +version = "~2.8.0" path = "module/core/former_types" default-features = false @@ -248,12 +248,12 @@ version = "~0.7.0" path = "module/core/impls_index_meta" [workspace.dependencies.mod_interface] -version = "~0.23.0" +version = "~0.24.0" path = "module/core/mod_interface" default-features = false [workspace.dependencies.mod_interface_meta] -version = "~0.23.0" +version = "~0.24.0" path = "module/core/mod_interface_meta" default-features = false @@ -279,7 +279,7 @@ default-features = false ## macro tools [workspace.dependencies.macro_tools] -version = "~0.39.0" +version = "~0.40.0" path = "module/core/macro_tools" default-features = false @@ -333,7 +333,7 @@ default-features = false ## error [workspace.dependencies.error_tools] -version = "~0.16.0" +version = "~0.17.0" path = "module/core/error_tools" default-features = false @@ -345,7 +345,7 @@ path = "module/alias/werror" ## string tools [workspace.dependencies.strs_tools] -version = "~0.16.0" +version = "~0.17.0" path = "module/core/strs_tools" default-features = false @@ -367,7 +367,7 @@ path = "module/alias/file_tools" default-features = false [workspace.dependencies.proper_path_tools] -version = "~0.9.0" +version = "~0.10.0" path = "module/core/proper_path_tools" default-features = false @@ -375,7 +375,7 @@ default-features = false ## process tools [workspace.dependencies.process_tools] -version = "~0.8.0" +version = "~0.9.0" path = "module/core/process_tools" default-features = false @@ -422,7 +422,7 @@ default-features = false ## ca [workspace.dependencies.wca] -version = "~0.20.0" +version = "~0.21.0" path = "module/move/wca" @@ -436,7 +436,7 @@ path = "module/move/wcensor" ## willbe [workspace.dependencies.willbe] -version = "~0.14.0" +version = "~0.15.0" path = "module/move/willbe" @@ -476,7 +476,7 @@ version = "~0.5.0" path = "module/move/deterministic_rand" [workspace.dependencies.crates_tools] -version = "~0.12.0" +version = "~0.13.0" path = "module/move/crates_tools" diff --git a/module/core/clone_dyn/Cargo.toml b/module/core/clone_dyn/Cargo.toml index 922f03fc69..df7631e07c 100644 --- a/module/core/clone_dyn/Cargo.toml +++ b/module/core/clone_dyn/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clone_dyn" -version = "0.23.0" +version = "0.24.0" edition = "2021" authors = [ "Kostiantyn Wandalen ", diff --git a/module/core/clone_dyn_meta/Cargo.toml b/module/core/clone_dyn_meta/Cargo.toml index 35a9783798..2359fb19c7 100644 --- a/module/core/clone_dyn_meta/Cargo.toml +++ b/module/core/clone_dyn_meta/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clone_dyn_meta" -version = "0.23.0" +version = "0.24.0" edition = "2021" authors = [ "Kostiantyn Wandalen ", diff --git a/module/core/clone_dyn_types/Cargo.toml b/module/core/clone_dyn_types/Cargo.toml index 9c5db44b4a..b9c3e6ee14 100644 --- a/module/core/clone_dyn_types/Cargo.toml +++ b/module/core/clone_dyn_types/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clone_dyn_types" -version = "0.22.0" +version = "0.23.0" edition = "2021" authors = [ "Kostiantyn Wandalen ", diff --git a/module/core/collection_tools/Cargo.toml b/module/core/collection_tools/Cargo.toml index 303edaa2df..58c96881f9 100644 --- a/module/core/collection_tools/Cargo.toml +++ b/module/core/collection_tools/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "collection_tools" -version = "0.11.0" +version = "0.12.0" edition = "2021" authors = [ "Kostiantyn Wandalen ", diff --git a/module/core/data_type/Cargo.toml b/module/core/data_type/Cargo.toml index 0328fc7e83..f42fae69bd 100644 --- a/module/core/data_type/Cargo.toml +++ b/module/core/data_type/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "data_type" -version = "0.9.0" +version = "0.10.0" edition = "2021" authors = [ "Kostiantyn Wandalen ", diff --git a/module/core/derive_tools/Cargo.toml b/module/core/derive_tools/Cargo.toml index 3d8f47c0cc..d41086762e 100644 --- a/module/core/derive_tools/Cargo.toml +++ b/module/core/derive_tools/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "derive_tools" -version = "0.27.0" +version = "0.28.0" edition = "2021" authors = [ "Kostiantyn Wandalen ", diff --git a/module/core/derive_tools_meta/Cargo.toml b/module/core/derive_tools_meta/Cargo.toml index 3ab4e3998c..b931c9806a 100644 --- a/module/core/derive_tools_meta/Cargo.toml +++ b/module/core/derive_tools_meta/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "derive_tools_meta" -version = "0.26.0" +version = "0.27.0" edition = "2021" authors = [ "Kostiantyn Wandalen ", diff --git a/module/core/error_tools/Cargo.toml b/module/core/error_tools/Cargo.toml index 1ea9fe9535..62f8ec7e88 100644 --- a/module/core/error_tools/Cargo.toml +++ b/module/core/error_tools/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "error_tools" -version = "0.16.0" +version = "0.17.0" edition = "2021" authors = [ "Kostiantyn Wandalen ", diff --git a/module/core/former/Cargo.toml b/module/core/former/Cargo.toml index 255f72c486..6ccdadb0b4 100644 --- a/module/core/former/Cargo.toml +++ b/module/core/former/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "former" -version = "2.8.0" +version = "2.9.0" edition = "2021" authors = [ "Kostiantyn Wandalen ", diff --git a/module/core/former_meta/Cargo.toml b/module/core/former_meta/Cargo.toml index 537a170711..1612da9af5 100644 --- a/module/core/former_meta/Cargo.toml +++ b/module/core/former_meta/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "former_meta" -version = "2.8.0" +version = "2.9.0" edition = "2021" authors = [ "Kostiantyn Wandalen ", diff --git a/module/core/former_types/Cargo.toml b/module/core/former_types/Cargo.toml index 75ae5bce43..6baa5017e7 100644 --- a/module/core/former_types/Cargo.toml +++ b/module/core/former_types/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "former_types" -version = "2.7.0" +version = "2.8.0" edition = "2021" authors = [ "Kostiantyn Wandalen ", diff --git a/module/core/interval_adapter/Cargo.toml b/module/core/interval_adapter/Cargo.toml index 40b7639448..cf0fd7fe6c 100644 --- a/module/core/interval_adapter/Cargo.toml +++ b/module/core/interval_adapter/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "interval_adapter" -version = "0.23.0" +version = "0.24.0" edition = "2021" authors = [ "Kostiantyn Wandalen ", diff --git a/module/core/iter_tools/Cargo.toml b/module/core/iter_tools/Cargo.toml index ea0b221df3..68cafe52bf 100644 --- a/module/core/iter_tools/Cargo.toml +++ b/module/core/iter_tools/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "iter_tools" -version = "0.20.0" +version = "0.21.0" edition = "2021" authors = [ "Kostiantyn Wandalen ", diff --git a/module/core/macro_tools/Cargo.toml b/module/core/macro_tools/Cargo.toml index cb1e7be33e..1213064721 100644 --- a/module/core/macro_tools/Cargo.toml +++ b/module/core/macro_tools/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "macro_tools" -version = "0.39.0" +version = "0.40.0" edition = "2021" authors = [ "Kostiantyn Wandalen ", diff --git a/module/core/mod_interface/Cargo.toml b/module/core/mod_interface/Cargo.toml index 38f56efef2..438141b1e3 100644 --- a/module/core/mod_interface/Cargo.toml +++ b/module/core/mod_interface/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "mod_interface" -version = "0.23.0" +version = "0.24.0" edition = "2021" authors = [ "Kostiantyn Wandalen ", diff --git a/module/core/mod_interface_meta/Cargo.toml b/module/core/mod_interface_meta/Cargo.toml index 05f0bbf285..b3de1c7f95 100644 --- a/module/core/mod_interface_meta/Cargo.toml +++ b/module/core/mod_interface_meta/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "mod_interface_meta" -version = "0.23.0" +version = "0.24.0" edition = "2021" authors = [ "Kostiantyn Wandalen ", diff --git a/module/core/process_tools/Cargo.toml b/module/core/process_tools/Cargo.toml index 8b43896373..308881a600 100644 --- a/module/core/process_tools/Cargo.toml +++ b/module/core/process_tools/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "process_tools" -version = "0.8.0" +version = "0.9.0" edition = "2021" authors = [ "Kostiantyn Wandalen ", diff --git a/module/core/proper_path_tools/Cargo.toml b/module/core/proper_path_tools/Cargo.toml index a9de7e5f46..5c6387502b 100644 --- a/module/core/proper_path_tools/Cargo.toml +++ b/module/core/proper_path_tools/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "proper_path_tools" -version = "0.9.0" +version = "0.10.0" edition = "2021" authors = [ "Kostiantyn Wandalen ", diff --git a/module/core/strs_tools/Cargo.toml b/module/core/strs_tools/Cargo.toml index 507791fffb..77dd2eeab5 100644 --- a/module/core/strs_tools/Cargo.toml +++ b/module/core/strs_tools/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "strs_tools" -version = "0.16.0" +version = "0.17.0" edition = "2021" authors = [ "Kostiantyn Wandalen ", diff --git a/module/core/variadic_from/Cargo.toml b/module/core/variadic_from/Cargo.toml index 616b747a6c..e3d1871e12 100644 --- a/module/core/variadic_from/Cargo.toml +++ b/module/core/variadic_from/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "variadic_from" -version = "0.22.0" +version = "0.23.0" edition = "2021" authors = [ "Kostiantyn Wandalen ", diff --git a/module/move/crates_tools/Cargo.toml b/module/move/crates_tools/Cargo.toml index 3bc5d48dab..d36a25f270 100644 --- a/module/move/crates_tools/Cargo.toml +++ b/module/move/crates_tools/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "crates_tools" -version = "0.12.0" +version = "0.13.0" edition = "2021" authors = [ "Kostiantyn Wandalen ", diff --git a/module/move/wca/Cargo.toml b/module/move/wca/Cargo.toml index 2d69a8aaff..eab9096bd4 100644 --- a/module/move/wca/Cargo.toml +++ b/module/move/wca/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wca" -version = "0.20.0" +version = "0.21.0" edition = "2021" authors = [ "Kostiantyn Wandalen ", diff --git a/module/move/wca/src/ca/aggregator.rs b/module/move/wca/src/ca/aggregator.rs index 05575b9136..60668ad4a0 100644 --- a/module/move/wca/src/ca/aggregator.rs +++ b/module/move/wca/src/ca/aggregator.rs @@ -5,7 +5,7 @@ mod private { Verifier, Executor, - grammar::command::private:: + grammar::command:: { CommandFormer, CommandAsSubformer, diff --git a/module/move/wca/src/ca/executor/executor.rs b/module/move/wca/src/ca/executor/executor.rs index fe1cbff998..224aacd489 100644 --- a/module/move/wca/src/ca/executor/executor.rs +++ b/module/move/wca/src/ca/executor/executor.rs @@ -4,7 +4,7 @@ mod private // use wtools::error::Result; use error::return_err; - use ca::help::private::{ HelpGeneratorOptions, LevelOfDetail, generate_help_content }; + use ca::help::{ HelpGeneratorOptions, generate_help_content, LevelOfDetail }; // aaa : for Bohdan : how is it useful? where is it used? // aaa : `ExecutorType` has been removed diff --git a/module/move/wca/src/ca/formatter.rs b/module/move/wca/src/ca/formatter.rs index 59fb4b31ff..d528ef7f6d 100644 --- a/module/move/wca/src/ca/formatter.rs +++ b/module/move/wca/src/ca/formatter.rs @@ -3,7 +3,7 @@ mod private use crate::*; use iter_tools::Itertools; - use ca::aggregator::private::Order; + use ca::aggregator::Order; /// - #[ derive( Debug, Clone, PartialEq ) ] @@ -91,5 +91,6 @@ mod private crate::mod_interface! { - + own use HelpFormat; + own use md_generator; } \ No newline at end of file diff --git a/module/move/wca/src/ca/grammar/command.rs b/module/move/wca/src/ca/grammar/command.rs index 3bfbd7a695..ad34d5ef85 100644 --- a/module/move/wca/src/ca/grammar/command.rs +++ b/module/move/wca/src/ca/grammar/command.rs @@ -249,6 +249,11 @@ crate::mod_interface! exposed use Command; exposed use CommandFormer; own use ValueDescription; + + own use CommandAsSubformer; + own use CommandAsSubformerEnd; + own use CommandFormerStorage; + } // qqq : use orphan instead of exposed for ALL files in the folder, dont use prelude for structs \ No newline at end of file diff --git a/module/move/wca/src/ca/help.rs b/module/move/wca/src/ca/help.rs index b7d0593634..bfc8edbfe1 100644 --- a/module/move/wca/src/ca/help.rs +++ b/module/move/wca/src/ca/help.rs @@ -6,7 +6,7 @@ mod private Command, Routine, Type, - formatter::private:: + formatter:: { HelpFormat, md_generator @@ -64,7 +64,7 @@ mod private } // qqq : for Barsik : make possible to change properties order - pub( crate ) fn generate_help_content( dictionary : &Dictionary, o : HelpGeneratorOptions< '_ > ) -> String + pub fn generate_help_content( dictionary : &Dictionary, o : HelpGeneratorOptions< '_ > ) -> String { struct Row { @@ -418,5 +418,9 @@ crate::mod_interface! { own use HelpGeneratorFn; own use HelpGeneratorOptions; + own use LevelOfDetail; + own use generate_help_content; + prelude use HelpVariants; + } diff --git a/module/move/wca/src/ca/verifier/verifier.rs b/module/move/wca/src/ca/verifier/verifier.rs index 6fc13fc28e..a595521755 100644 --- a/module/move/wca/src/ca/verifier/verifier.rs +++ b/module/move/wca/src/ca/verifier/verifier.rs @@ -8,7 +8,7 @@ mod private use indexmap::IndexMap; // use wtools::{ error, error::Result, err }; use error::err; - use ca::help::private::{ HelpGeneratorOptions, LevelOfDetail, generate_help_content }; + use ca::help::{ HelpGeneratorOptions, LevelOfDetail, generate_help_content }; // xxx /// Converts a `ParsedCommand` to a `VerifiedCommand` by performing validation and type casting on values. @@ -249,4 +249,8 @@ mod private crate::mod_interface! { exposed use Verifier; + + // own use LevelOfDetail; + // own use generate_help_content; + } diff --git a/module/move/willbe/Cargo.toml b/module/move/willbe/Cargo.toml index d87621762c..a1cd2b21d8 100644 --- a/module/move/willbe/Cargo.toml +++ b/module/move/willbe/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "willbe" -version = "0.14.0" +version = "0.15.0" edition = "2021" authors = [ "Kostiantyn Wandalen ", diff --git a/module/move/willbe/tests/inc/action_tests/test.rs b/module/move/willbe/tests/inc/action_tests/test.rs index 6d7d7898d9..16a4e8cd6a 100644 --- a/module/move/willbe/tests/inc/action_tests/test.rs +++ b/module/move/willbe/tests/inc/action_tests/test.rs @@ -1,6 +1,7 @@ use super::*; -use the_module::*; +// use the_module::*; +// qqq : for Bohdan : bad. don't import the_module::* use inc::helper:: { ProjectBuilder, @@ -17,12 +18,15 @@ use collection::BTreeSet; // use path::{ Path, PathBuf }; use assert_fs::TempDir; -use action::test::{ test, TestsCommandOptions }; -use channel::*; -use optimization::*; +use the_module::action::test::{ test, TestsCommandOptions }; +use the_module::channel::*; +// use the_module::optimization::*; +use the_module::optimization::{ self, Optimization }; +use the_module::AbsolutePath; // qqq : for Petro : no astersisks import use willbe::test::TestVariant; + #[ test ] // if the test fails => the report is returned as an error ( Err(Report) ) fn fail_test() diff --git a/module/move/willbe/tests/inc/tool/graph_test.rs b/module/move/willbe/tests/inc/tool/graph_test.rs index d6f5c38986..75a2b29db3 100644 --- a/module/move/willbe/tests/inc/tool/graph_test.rs +++ b/module/move/willbe/tests/inc/tool/graph_test.rs @@ -1,7 +1,8 @@ use super::*; -use the_module::*; -use graph::toposort; +// qqq : for Bohdan : bad. don't import the_module::* +// use the_module::*; +use the_module::graph::toposort; use collection::HashMap; use petgraph::Graph; use willbe::graph::topological_sort_with_grouping; From 82fb76f230825b81520b61ecd12d014f084da532 Mon Sep 17 00:00:00 2001 From: Viktor Dudnik <37380849+0x07C0@users.noreply.github.com> Date: Fri, 6 Sep 2024 17:07:19 +0300 Subject: [PATCH 12/67] fix: change hetzner server type to a supported one --- module/move/willbe/template/deploy/deploy/hetzner/main.tf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/module/move/willbe/template/deploy/deploy/hetzner/main.tf b/module/move/willbe/template/deploy/deploy/hetzner/main.tf index da3118ecef..b0f2f60194 100644 --- a/module/move/willbe/template/deploy/deploy/hetzner/main.tf +++ b/module/move/willbe/template/deploy/deploy/hetzner/main.tf @@ -27,7 +27,7 @@ resource "hcloud_primary_ip" "primary_ip" { resource "hcloud_server" "uaconf" { name = "uaconf-2024" image = "ubuntu-22.04" - server_type = "cx11" + server_type = "cx22" datacenter = "hel1-dc2" public_net { From 38bfed5854dac76cf7861363bc6023ab60957598 Mon Sep 17 00:00:00 2001 From: wbot <69343704+wtools-bot@users.noreply.github.com> Date: Mon, 9 Sep 2024 13:21:00 +0300 Subject: [PATCH 13/67] AUTO : Forward from format_tools_evolving_3 to alpha (#1455) publishing --- Cargo.toml | 8 ++-- module/core/clone_dyn/Cargo.toml | 8 ++-- module/core/clone_dyn/Readme.md | 4 +- .../clone_dyn/examples/clone_dyn_trivial.rs | 4 +- module/core/clone_dyn/src/lib.rs | 4 +- module/core/clone_dyn/tests/inc/mod.rs | 6 +-- module/core/derive_tools/Cargo.toml | 4 +- module/core/derive_tools/src/lib.rs | 41 +++++++++++++++++++ .../tests/inc/new/basic_manual_test.rs | 19 +++++---- .../derive_tools/tests/inc/new/basic_test.rs | 8 +++- .../inc/new/multiple_named_manual_test.rs | 25 ++++++----- .../tests/inc/new/multiple_named_test.rs | 16 +++++--- .../inc/new/multiple_unnamed_manual_test.rs | 19 +++++---- .../tests/inc/new/multiple_unnamed_test.rs | 10 ++++- .../tests/inc/new/named_manual_test.rs | 23 +++++++---- .../derive_tools/tests/inc/new/named_test.rs | 12 ++++-- .../tests/inc/new/only_test/basic.rs | 9 ++-- .../tests/inc/new/only_test/multiple_named.rs | 6 ++- .../inc/new/only_test/multiple_unnamed.rs | 6 ++- .../tests/inc/new/only_test/named.rs | 6 ++- .../tests/inc/new/only_test/unit.rs | 6 ++- .../tests/inc/new/unit_manual_test.rs | 19 +++++---- .../derive_tools/tests/inc/new/unit_test.rs | 10 ++++- module/core/derive_tools_meta/Cargo.toml | 2 +- .../core/derive_tools_meta/src/derive/new.rs | 22 +++++----- module/core/variadic_from/Cargo.toml | 2 +- 26 files changed, 201 insertions(+), 98 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index cb2a9fabd9..359913746a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -118,13 +118,13 @@ default-features = false ## derive [workspace.dependencies.derive_tools] -version = "~0.28.0" +version = "~0.29.0" path = "module/core/derive_tools" default-features = false features = [ "enabled" ] [workspace.dependencies.derive_tools_meta] -version = "~0.27.0" +version = "~0.28.0" path = "module/core/derive_tools_meta" default-features = false features = [ "enabled" ] @@ -159,13 +159,13 @@ path = "module/alias/fundamental_data_type" default-features = false [workspace.dependencies.variadic_from] -version = "~0.23.0" +version = "~0.24.0" path = "module/core/variadic_from" default-features = false features = [ "enabled" ] [workspace.dependencies.clone_dyn] -version = "~0.24.0" +version = "~0.25.0" path = "module/core/clone_dyn" default-features = false features = [ "enabled" ] diff --git a/module/core/clone_dyn/Cargo.toml b/module/core/clone_dyn/Cargo.toml index df7631e07c..b27b73a6a0 100644 --- a/module/core/clone_dyn/Cargo.toml +++ b/module/core/clone_dyn/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clone_dyn" -version = "0.24.0" +version = "0.25.0" edition = "2021" authors = [ "Kostiantyn Wandalen ", @@ -26,12 +26,12 @@ all-features = false [features] -default = [ "enabled", "clone_dyn_types", "clone_dyn_meta" ] -full = [ "enabled", "clone_dyn_types", "clone_dyn_meta" ] +default = [ "enabled", "clone_dyn_types", "derive_clone_dyn" ] +full = [ "enabled", "clone_dyn_types", "derive_clone_dyn" ] enabled = [] clone_dyn_types = [ "dep:clone_dyn_types", "clone_dyn_types/enabled" ] -clone_dyn_meta = [ "dep:clone_dyn_meta", "clone_dyn_meta/enabled", "clone_dyn_types" ] +derive_clone_dyn = [ "dep:clone_dyn_meta", "clone_dyn_meta/enabled", "clone_dyn_types" ] [dependencies] clone_dyn_meta = { workspace = true, optional = true } diff --git a/module/core/clone_dyn/Readme.md b/module/core/clone_dyn/Readme.md index f23c03a8c9..43e159b62c 100644 --- a/module/core/clone_dyn/Readme.md +++ b/module/core/clone_dyn/Readme.md @@ -70,9 +70,9 @@ The main function demonstrates the overall usage by creating a vector, obtaining ```rust -# #[ cfg( not( all( feature = "enabled", feature = "clone_dyn_meta" ) ) ) ] +# #[ cfg( not( all( feature = "enabled", feature = "derive_clone_dyn" ) ) ) ] # fn main() {} -# #[ cfg( all( feature = "enabled", feature = "clone_dyn_meta" ) ) ] +# #[ cfg( all( feature = "enabled", feature = "derive_clone_dyn" ) ) ] # fn main() # { diff --git a/module/core/clone_dyn/examples/clone_dyn_trivial.rs b/module/core/clone_dyn/examples/clone_dyn_trivial.rs index cd67d1cbea..aecf14563d 100644 --- a/module/core/clone_dyn/examples/clone_dyn_trivial.rs +++ b/module/core/clone_dyn/examples/clone_dyn_trivial.rs @@ -56,9 +56,9 @@ //! The main function demonstrates the overall usage by creating a vector, obtaining an iterator, and using the iterator to print elements. //! -#[ cfg( not( all( feature = "enabled", feature = "clone_dyn_meta" ) ) ) ] +#[ cfg( not( all( feature = "enabled", feature = "derive_clone_dyn" ) ) ) ] fn main() {} -#[ cfg( all( feature = "enabled", feature = "clone_dyn_meta" ) ) ] +#[ cfg( all( feature = "enabled", feature = "derive_clone_dyn" ) ) ] fn main() { use clone_dyn::{ clone_dyn, CloneDyn }; diff --git a/module/core/clone_dyn/src/lib.rs b/module/core/clone_dyn/src/lib.rs index 2f2ba25890..224432e031 100644 --- a/module/core/clone_dyn/src/lib.rs +++ b/module/core/clone_dyn/src/lib.rs @@ -9,7 +9,7 @@ #[ cfg( feature = "enabled" ) ] pub mod dependency { - #[ cfg( feature = "clone_dyn_meta" ) ] + #[ cfg( feature = "derive_clone_dyn" ) ] pub use ::clone_dyn_meta; #[ cfg( feature = "clone_dyn_types" ) ] pub use ::clone_dyn_types; @@ -72,7 +72,7 @@ pub mod prelude #[ doc( inline ) ] #[ allow( unused_imports ) ] - #[ cfg( feature = "clone_dyn_meta" ) ] + #[ cfg( feature = "derive_clone_dyn" ) ] pub use ::clone_dyn_meta::clone_dyn; #[ doc( inline ) ] diff --git a/module/core/clone_dyn/tests/inc/mod.rs b/module/core/clone_dyn/tests/inc/mod.rs index fc05ba6236..6e0cb7295a 100644 --- a/module/core/clone_dyn/tests/inc/mod.rs +++ b/module/core/clone_dyn/tests/inc/mod.rs @@ -2,9 +2,9 @@ #[ allow( unused_imports ) ] use super::*; -#[ cfg( feature = "clone_dyn_meta" ) ] +#[ cfg( feature = "derive_clone_dyn" ) ] pub mod basic_manual; -#[ cfg( feature = "clone_dyn_meta" ) ] +#[ cfg( feature = "derive_clone_dyn" ) ] pub mod basic; -#[ cfg( feature = "clone_dyn_meta" ) ] +#[ cfg( feature = "derive_clone_dyn" ) ] pub mod parametrized; diff --git a/module/core/derive_tools/Cargo.toml b/module/core/derive_tools/Cargo.toml index d41086762e..0dd6c2d30f 100644 --- a/module/core/derive_tools/Cargo.toml +++ b/module/core/derive_tools/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "derive_tools" -version = "0.28.0" +version = "0.29.0" edition = "2021" authors = [ "Kostiantyn Wandalen ", @@ -203,7 +203,7 @@ parse-display = { version = "~0.8.2", optional = true, default-features = false ## internal derive_tools_meta = { workspace = true, optional = true, features = [] } variadic_from = { workspace = true, optional = true, features = [] } -clone_dyn = { workspace = true, optional = true, features = [ "clone_dyn_types", "clone_dyn_meta" ] } +clone_dyn = { workspace = true, optional = true, features = [ "clone_dyn_types", "derive_clone_dyn" ] } [dev-dependencies] diff --git a/module/core/derive_tools/src/lib.rs b/module/core/derive_tools/src/lib.rs index 7c3ddfdecf..be57137ee6 100644 --- a/module/core/derive_tools/src/lib.rs +++ b/module/core/derive_tools/src/lib.rs @@ -201,3 +201,44 @@ pub mod prelude pub use ::variadic_from::prelude::*; } + +// xxx : minimize dependendencies +// Adding aho-corasick v1.1.3 +// Adding cfg_aliases v0.1.1 (latest: v0.2.1) +// Adding clone_dyn v0.24.0 +// Adding clone_dyn_meta v0.24.0 +// Adding clone_dyn_types v0.23.0 +// Adding collection_tools v0.12.0 +// Adding const_format v0.2.33 +// Adding const_format_proc_macros v0.2.33 +// Adding convert_case v0.6.0 +// Adding derive_more v1.0.0 +// Adding derive_more-impl v1.0.0 +// Adding derive_tools v0.28.0 +// Adding derive_tools_meta v0.27.0 +// Adding either v1.13.0 +// Adding former_types v2.8.0 +// Adding heck v0.4.1 (latest: v0.5.0) +// Adding interval_adapter v0.24.0 +// Adding iter_tools v0.21.0 +// Adding itertools v0.11.0 (latest: v0.13.0) +// Adding macro_tools v0.40.0 +// Adding parse-display v0.8.2 (latest: v0.10.0) +// Adding parse-display-derive v0.8.2 (latest: v0.10.0) +// Adding phf v0.10.1 (latest: v0.11.2) +// Adding phf_generator v0.10.0 (latest: v0.11.2) +// Adding phf_macros v0.10.0 (latest: v0.11.2) +// Adding phf_shared v0.10.0 (latest: v0.11.2) +// Adding proc-macro-hack v0.5.20+deprecated +// Adding regex v1.10.6 +// Adding regex-automata v0.4.7 +// Adding regex-syntax v0.7.5 (latest: v0.8.4) +// Adding regex-syntax v0.8.4 +// Adding rustversion v1.0.17 +// Adding structmeta v0.2.0 (latest: v0.3.0) +// Adding structmeta-derive v0.2.0 (latest: v0.3.0) +// Adding strum v0.25.0 (latest: v0.26.3) +// Adding strum_macros v0.25.3 (latest: v0.26.4) +// Adding unicode-segmentation v1.11.0 +// Adding unicode-xid v0.2.5 +// Adding variadic_from v0.23.0 \ No newline at end of file diff --git a/module/core/derive_tools/tests/inc/new/basic_manual_test.rs b/module/core/derive_tools/tests/inc/new/basic_manual_test.rs index 993956eefa..c7f40395c6 100644 --- a/module/core/derive_tools/tests/inc/new/basic_manual_test.rs +++ b/module/core/derive_tools/tests/inc/new/basic_manual_test.rs @@ -1,15 +1,20 @@ use super::*; -#[ derive( Debug, Clone, Copy, PartialEq ) ] -pub struct IsTransparent( bool ); - -impl IsTransparent +mod mod1 { - #[ inline( always ) ] - fn new( src : bool ) -> Self + + #[ derive( Debug, Clone, Copy, PartialEq ) ] + pub struct Struct1( pub bool ); + + impl Struct1 { - Self( src ) + #[ inline( always ) ] + pub fn new( src : bool ) -> Self + { + Self( src ) + } } + } include!( "./only_test/basic.rs" ); diff --git a/module/core/derive_tools/tests/inc/new/basic_test.rs b/module/core/derive_tools/tests/inc/new/basic_test.rs index 6cea8c5a49..c96850d3de 100644 --- a/module/core/derive_tools/tests/inc/new/basic_test.rs +++ b/module/core/derive_tools/tests/inc/new/basic_test.rs @@ -1,6 +1,10 @@ use super::*; -#[ derive( Debug, Clone, Copy, PartialEq, the_module::New ) ] -pub struct IsTransparent( bool ); +mod mod1 +{ + use super::*; + #[ derive( Debug, Clone, Copy, PartialEq, the_module::New ) ] + pub struct Struct1( pub bool ); +} include!( "./only_test/basic.rs" ); diff --git a/module/core/derive_tools/tests/inc/new/multiple_named_manual_test.rs b/module/core/derive_tools/tests/inc/new/multiple_named_manual_test.rs index 839456b1a0..45a7007502 100644 --- a/module/core/derive_tools/tests/inc/new/multiple_named_manual_test.rs +++ b/module/core/derive_tools/tests/inc/new/multiple_named_manual_test.rs @@ -1,19 +1,24 @@ use super::*; -#[ derive( Debug, PartialEq, Eq ) ] -struct StructNamedFields +mod mod1 { - a : i32, - b : bool, -} -impl StructNamedFields -{ - #[ inline( always ) ] - fn new( a : i32, b : bool ) -> Self + #[ derive( Debug, PartialEq, Eq ) ] + pub struct Struct1 + { + pub a : i32, + pub b : bool, + } + + impl Struct1 { - Self{ a, b } + #[ inline( always ) ] + pub fn new( a : i32, b : bool ) -> Self + { + Self{ a, b } + } } + } include!( "./only_test/multiple_named.rs" ); diff --git a/module/core/derive_tools/tests/inc/new/multiple_named_test.rs b/module/core/derive_tools/tests/inc/new/multiple_named_test.rs index c3988146df..3e148771eb 100644 --- a/module/core/derive_tools/tests/inc/new/multiple_named_test.rs +++ b/module/core/derive_tools/tests/inc/new/multiple_named_test.rs @@ -1,11 +1,17 @@ use super::*; -#[ derive( Debug, PartialEq, Eq, the_module::New ) ] -// #[ debug ] -struct StructNamedFields +mod mod1 { - a : i32, - b : bool, + use super::*; + + #[ derive( Debug, PartialEq, Eq, the_module::New ) ] + // #[ debug ] + pub struct Struct1 + { + pub a : i32, + pub b : bool, + } + } include!( "./only_test/multiple_named.rs" ); diff --git a/module/core/derive_tools/tests/inc/new/multiple_unnamed_manual_test.rs b/module/core/derive_tools/tests/inc/new/multiple_unnamed_manual_test.rs index 08ee277851..bed9e79851 100644 --- a/module/core/derive_tools/tests/inc/new/multiple_unnamed_manual_test.rs +++ b/module/core/derive_tools/tests/inc/new/multiple_unnamed_manual_test.rs @@ -1,15 +1,20 @@ use super::*; -#[ derive( Debug, PartialEq, Eq ) ] -struct StructWithManyFields( i32, bool ); - -impl StructWithManyFields +mod mod1 { - #[ inline( always ) ] - fn new( src1 : i32, src2 : bool ) -> Self + + #[ derive( Debug, PartialEq, Eq ) ] + pub struct Struct1( pub i32, pub bool ); + + impl Struct1 { - Self( src1, src2 ) + #[ inline( always ) ] + pub fn new( src1 : i32, src2 : bool ) -> Self + { + Self( src1, src2 ) + } } + } include!( "./only_test/multiple_unnamed.rs" ); diff --git a/module/core/derive_tools/tests/inc/new/multiple_unnamed_test.rs b/module/core/derive_tools/tests/inc/new/multiple_unnamed_test.rs index 6a8f882287..8df3f37489 100644 --- a/module/core/derive_tools/tests/inc/new/multiple_unnamed_test.rs +++ b/module/core/derive_tools/tests/inc/new/multiple_unnamed_test.rs @@ -1,6 +1,12 @@ use super::*; -#[ derive( Debug, PartialEq, Eq, the_module::New ) ] -struct StructWithManyFields( i32, bool ); +mod mod1 +{ + use super::*; + + #[ derive( Debug, PartialEq, Eq, the_module::New ) ] + pub struct Struct1( pub i32, pub bool ); + +} include!( "./only_test/multiple_unnamed.rs" ); diff --git a/module/core/derive_tools/tests/inc/new/named_manual_test.rs b/module/core/derive_tools/tests/inc/new/named_manual_test.rs index 679afeec66..56f656a1c9 100644 --- a/module/core/derive_tools/tests/inc/new/named_manual_test.rs +++ b/module/core/derive_tools/tests/inc/new/named_manual_test.rs @@ -1,18 +1,23 @@ use super::*; -#[ derive( Debug, PartialEq, Eq ) ] -struct MyStruct +mod mod1 { - a : i32, -} -impl MyStruct -{ - #[ inline( always ) ] - fn new( src : i32 ) -> Self + #[ derive( Debug, PartialEq, Eq ) ] + pub struct Struct1 + { + pub a : i32, + } + + impl Struct1 { - Self{ a : src } + #[ inline( always ) ] + pub fn new( src : i32 ) -> Self + { + Self{ a : src } + } } + } include!( "./only_test/named.rs" ); diff --git a/module/core/derive_tools/tests/inc/new/named_test.rs b/module/core/derive_tools/tests/inc/new/named_test.rs index 1df964f76f..66d8fd8ac0 100644 --- a/module/core/derive_tools/tests/inc/new/named_test.rs +++ b/module/core/derive_tools/tests/inc/new/named_test.rs @@ -1,9 +1,15 @@ use super::*; -#[ derive( Debug, PartialEq, Eq, the_module::New ) ] -struct MyStruct +mod mod1 { - a : i32, + use super::*; + + #[ derive( Debug, PartialEq, Eq, the_module::New ) ] + pub struct Struct1 + { + pub a : i32, + } + } include!( "./only_test/named.rs" ); diff --git a/module/core/derive_tools/tests/inc/new/only_test/basic.rs b/module/core/derive_tools/tests/inc/new/only_test/basic.rs index 7dadc915f2..cfaf3127df 100644 --- a/module/core/derive_tools/tests/inc/new/only_test/basic.rs +++ b/module/core/derive_tools/tests/inc/new/only_test/basic.rs @@ -2,12 +2,13 @@ #[ test ] fn from_test() { + use mod1::Struct1; - let got = IsTransparent::new( true ); - let exp = IsTransparent( true ); + let got = Struct1::new( true ); + let exp = Struct1( true ); a_id!( got, exp ); - let got = IsTransparent::new( false ); - let exp = IsTransparent( false ); + let got = Struct1::new( false ); + let exp = Struct1( false ); a_id!( got, exp ); } diff --git a/module/core/derive_tools/tests/inc/new/only_test/multiple_named.rs b/module/core/derive_tools/tests/inc/new/only_test/multiple_named.rs index eebdbba992..adf93b4c93 100644 --- a/module/core/derive_tools/tests/inc/new/only_test/multiple_named.rs +++ b/module/core/derive_tools/tests/inc/new/only_test/multiple_named.rs @@ -1,7 +1,9 @@ #[ test ] fn from_named() { - let got : StructNamedFields = StructNamedFields::new( 10, true ); - let exp = StructNamedFields{ a : 10 , b : true }; + use mod1::Struct1; + + let got : Struct1 = Struct1::new( 10, true ); + let exp = Struct1{ a : 10 , b : true }; a_id!( got, exp ); } diff --git a/module/core/derive_tools/tests/inc/new/only_test/multiple_unnamed.rs b/module/core/derive_tools/tests/inc/new/only_test/multiple_unnamed.rs index 94abf3c133..f8d960c898 100644 --- a/module/core/derive_tools/tests/inc/new/only_test/multiple_unnamed.rs +++ b/module/core/derive_tools/tests/inc/new/only_test/multiple_unnamed.rs @@ -1,7 +1,9 @@ #[ test ] fn from_named() { - let got : StructWithManyFields = StructWithManyFields::new( 10, true ); - let exp = StructWithManyFields( 10 , true ); + use mod1::Struct1; + + let got : Struct1 = Struct1::new( 10, true ); + let exp = Struct1( 10 , true ); a_id!( got, exp ); } diff --git a/module/core/derive_tools/tests/inc/new/only_test/named.rs b/module/core/derive_tools/tests/inc/new/only_test/named.rs index b654961735..71804413ce 100644 --- a/module/core/derive_tools/tests/inc/new/only_test/named.rs +++ b/module/core/derive_tools/tests/inc/new/only_test/named.rs @@ -1,7 +1,9 @@ #[ test ] fn from_named() { - let got : MyStruct = MyStruct::new( 13 ); - let exp = MyStruct { a : 13 }; + use mod1::Struct1; + + let got : Struct1 = Struct1::new( 13 ); + let exp = Struct1 { a : 13 }; a_id!( got, exp ); } diff --git a/module/core/derive_tools/tests/inc/new/only_test/unit.rs b/module/core/derive_tools/tests/inc/new/only_test/unit.rs index 279908a05d..9366152172 100644 --- a/module/core/derive_tools/tests/inc/new/only_test/unit.rs +++ b/module/core/derive_tools/tests/inc/new/only_test/unit.rs @@ -1,7 +1,9 @@ #[ test ] fn from_named() { - let got : UnitStruct = UnitStruct::new(); - let exp = UnitStruct; + use mod1::Struct1; + + let got : Struct1 = Struct1::new(); + let exp = Struct1; a_id!( got, exp ); } diff --git a/module/core/derive_tools/tests/inc/new/unit_manual_test.rs b/module/core/derive_tools/tests/inc/new/unit_manual_test.rs index d5fc60e6c5..2d04912112 100644 --- a/module/core/derive_tools/tests/inc/new/unit_manual_test.rs +++ b/module/core/derive_tools/tests/inc/new/unit_manual_test.rs @@ -1,15 +1,20 @@ use super::*; -#[ derive( Debug, Clone, Copy, PartialEq ) ] -struct UnitStruct; - -impl UnitStruct +mod mod1 { - #[ inline( always ) ] - fn new() -> Self + + #[ derive( Debug, Clone, Copy, PartialEq ) ] + pub struct Struct1; + + impl Struct1 { - Self + #[ inline( always ) ] + pub fn new() -> Self + { + Self + } } + } include!( "./only_test/unit.rs" ); diff --git a/module/core/derive_tools/tests/inc/new/unit_test.rs b/module/core/derive_tools/tests/inc/new/unit_test.rs index 606df185d8..4e40c31a0e 100644 --- a/module/core/derive_tools/tests/inc/new/unit_test.rs +++ b/module/core/derive_tools/tests/inc/new/unit_test.rs @@ -1,6 +1,12 @@ use super::*; -#[ derive( Debug, Clone, Copy, PartialEq, the_module::New ) ] -struct UnitStruct; +mod mod1 +{ + use super::*; + + #[ derive( Debug, Clone, Copy, PartialEq, the_module::New ) ] + pub struct Struct1; + +} include!( "./only_test/unit.rs" ); diff --git a/module/core/derive_tools_meta/Cargo.toml b/module/core/derive_tools_meta/Cargo.toml index b931c9806a..a5210dcfa6 100644 --- a/module/core/derive_tools_meta/Cargo.toml +++ b/module/core/derive_tools_meta/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "derive_tools_meta" -version = "0.27.0" +version = "0.28.0" edition = "2021" authors = [ "Kostiantyn Wandalen ", diff --git a/module/core/derive_tools_meta/src/derive/new.rs b/module/core/derive_tools_meta/src/derive/new.rs index 2f0341c159..5e274c3eb1 100644 --- a/module/core/derive_tools_meta/src/derive/new.rs +++ b/module/core/derive_tools_meta/src/derive/new.rs @@ -145,7 +145,7 @@ fn generate_unit #generics_where { #[ inline( always ) ] - fn new() -> Self + pub fn new() -> Self { Self } @@ -175,8 +175,8 @@ fn generate_single_field_named #generics_where { #[ inline( always ) ] - // fn new( src : i32 ) -> Self - fn new( src : #field_type ) -> Self + // pub fn new( src : i32 ) -> Self + pub fn new( src : #field_type ) -> Self { // Self { a : src } Self { #field_name: src } @@ -207,8 +207,8 @@ fn generate_single_field #generics_where { #[ inline( always ) ] - // fn new( src : bool ) -> Self - fn new( src : #field_type ) -> Self + // pub fn new( src : bool ) -> Self + pub fn new( src : #field_type ) -> Self { // Self( src ) Self( src ) @@ -248,8 +248,8 @@ fn generate_multiple_fields_named< 'a > #generics_where { #[ inline( always ) ] - // fn new( src : ( i32, bool ) ) -> Self - fn new( #( #val_type ),* ) -> Self + // pub fn new( src : ( i32, bool ) ) -> Self + pub fn new( #( #val_type ),* ) -> Self { // StructNamedFields{ a : src.0, b : src.1 } #item_name { #( #field_names ),* } @@ -287,8 +287,8 @@ fn generate_multiple_fields< 'a > #generics_where { #[ inline( always ) ] - // fn new( src : (i32, bool) ) -> Self - fn new( src : ( #( #field_types ),* ) ) -> Self + // pub fn new( src : (i32, bool) ) -> Self + pub fn new( src : ( #( #field_types ),* ) ) -> Self { // StructWithManyFields( src.0, src.1 ) #item_name( #( #params ),* ) @@ -359,7 +359,7 @@ where {2} {{ #[ inline ] - fn new( src : {args} ) -> Self + pub fn new( src : {args} ) -> Self {{ Self::{variant_name}( {use_src} ) }} @@ -388,7 +388,7 @@ field : {variant_name}"#, #generics_where { #[ inline ] - fn new( src : #args ) -> Self + pub fn new( src : #args ) -> Self { Self::#variant_name( #use_src ) } diff --git a/module/core/variadic_from/Cargo.toml b/module/core/variadic_from/Cargo.toml index e3d1871e12..2a67bdda26 100644 --- a/module/core/variadic_from/Cargo.toml +++ b/module/core/variadic_from/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "variadic_from" -version = "0.23.0" +version = "0.24.0" edition = "2021" authors = [ "Kostiantyn Wandalen ", From e09a21e673ab1566f9c762fdafba050c0bf55fef Mon Sep 17 00:00:00 2001 From: wandalen Date: Mon, 9 Sep 2024 13:24:18 +0300 Subject: [PATCH 14/67] willbe-v0.16.0 --- Cargo.toml | 2 +- module/move/willbe/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 359913746a..1ab5b51426 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -436,7 +436,7 @@ path = "module/move/wcensor" ## willbe [workspace.dependencies.willbe] -version = "~0.15.0" +version = "~0.16.0" path = "module/move/willbe" diff --git a/module/move/willbe/Cargo.toml b/module/move/willbe/Cargo.toml index a1cd2b21d8..0faeec2755 100644 --- a/module/move/willbe/Cargo.toml +++ b/module/move/willbe/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "willbe" -version = "0.15.0" +version = "0.16.0" edition = "2021" authors = [ "Kostiantyn Wandalen ", From 29dbd4e5e6a8a22e6477d1734ee7850469bb17dd Mon Sep 17 00:00:00 2001 From: Viktor Dudnik <37380849+0x07C0@users.noreply.github.com> Date: Mon, 9 Sep 2024 18:19:54 +0300 Subject: [PATCH 15/67] fix: deploy logic and template files --- module/move/willbe/src/action/deploy_renew.rs | 217 ++++++------------ .../move/willbe/src/command/deploy_renew.rs | 6 +- module/move/willbe/src/command/mod.rs | 6 +- module/move/willbe/src/tool/template.rs | 62 +---- .../move/willbe/template/deploy/Makefile.hbs | 2 +- .../{cloud-init.tpl => cloud-init.tpl.hbs} | 2 +- .../deploy/hetzner/{main.tf => main.tf.hbs} | 6 +- .../hetzner/{outputs.tf => outputs.tf.hbs} | 2 +- .../{cloud-init.tpl => cloud-init.tpl.hbs} | 2 +- 9 files changed, 97 insertions(+), 208 deletions(-) rename module/move/willbe/template/deploy/deploy/aws/templates/{cloud-init.tpl => cloud-init.tpl.hbs} (95%) rename module/move/willbe/template/deploy/deploy/hetzner/{main.tf => main.tf.hbs} (90%) rename module/move/willbe/template/deploy/deploy/hetzner/{outputs.tf => outputs.tf.hbs} (86%) rename module/move/willbe/template/deploy/deploy/hetzner/templates/{cloud-init.tpl => cloud-init.tpl.hbs} (95%) diff --git a/module/move/willbe/src/action/deploy_renew.rs b/module/move/willbe/src/action/deploy_renew.rs index 2a6d3b52fd..56bf766550 100644 --- a/module/move/willbe/src/action/deploy_renew.rs +++ b/module/move/willbe/src/action/deploy_renew.rs @@ -5,146 +5,80 @@ mod private use error::{ untyped::Context }; use tool::template::*; - // /// Template for creating deploy files. - // /// - // /// Includes terraform deploy options to GCP, and Hetzner, - // /// a Makefile for useful commands, and a key directory. - // #[ derive( Debug ) ] - // pub struct DeployTemplate - // { - // files : DeployTemplateFiles, - // parameters : TemplateParameters, - // values : TemplateValues, - // } - - // // qqq : for Viktor : why DeployTemplate can't be part of template.rs? - - // impl Template< DeployTemplateFiles > for DeployTemplate - // { - // fn create_all( self, path : &Path ) -> error::untyped::Result< () > - // { - // self.files.create_all( path, &self.values ) - // } - - // fn parameters( &self ) -> &TemplateParameters - // { - // &self.parameters - // } - - // fn set_values( &mut self, values : TemplateValues ) - // { - // self.values = values - // } - - // fn get_values( &self ) -> &TemplateValues - // { - // &self.values - // } - - // fn get_values_mut( &mut self ) -> &mut TemplateValues - // { - // &mut self.values - // } - - // fn parameter_storage( &self ) -> &Path { - // "./.deploy_template.toml".as_ref() - // } - - // fn template_name( &self ) -> &'static str { - // "deploy" - // } - // } - - // impl Default for DeployTemplate - // { - // fn default() -> Self - // { - // let parameters = TemplateParameters::former() - // .parameter( "gcp_project_id" ).is_mandatory( true ).end() - // .parameter( "gcp_region" ).end() - // .parameter( "gcp_artifact_repo_name" ).end() - // .parameter( "docker_image_name" ).end() - // .form(); - - // Self - // { - // files : Default::default(), - // parameters, - // values : Default::default(), - // } - // } - // } - - // // qqq : for Viktor : is that structure required? - // /// Files for the deploy template. - // /// - // /// Default implementation contains all required files. - // #[ derive( Debug ) ] - // pub struct DeployTemplateFiles( Vec< TemplateFileDescriptor > ); - - // impl Default for DeployTemplateFiles - // { - // fn default() -> Self - // { - // let formed = TemplateFilesBuilder::former() - // // root - // .file().data( include_str!( "../../template/deploy/.deploy_template.toml.hbs" ) ).path( "./.deploy_template.toml" ).mode( WriteMode::TomlExtend ).is_template( true ).end() - // .file().data( include_str!( "../../template/deploy/Makefile.hbs" ) ).path( "./Makefile" ).is_template( true ).end() - // // /key - // .file().data( include_str!( "../../template/deploy/key/pack.sh" ) ).path( "./key/pack.sh" ).end() - // .file().data( include_str!( "../../template/deploy/key/Readme.md" ) ).path( "./key/Readme.md" ).end() - // // /deploy/ - // .file().data( include_str!( "../../template/deploy/deploy/Dockerfile" ) ).path( "./deploy/Dockerfile" ).end() - // .file().data( include_str!( "../../template/deploy/deploy/Readme.md" ) ).path( "./deploy/Readme.md" ).end() - // // /deploy/gar - // .file().data( include_str!( "../../template/deploy/deploy/gar/Readme.md" ) ).path( "./deploy/gar/Readme.md" ).end() - // .file().data( include_str!( "../../template/deploy/deploy/gar/main.tf" ) ).path( "./deploy/gar/main.tf" ).end() - // .file().data( include_str!( "../../template/deploy/deploy/gar/outputs.tf" ) ).path( "./deploy/gar/outputs.tf" ).end() - // .file().data( include_str!( "../../template/deploy/deploy/gar/variables.tf" ) ).path( "./deploy/gar/variables.tf" ).end() - // // /deploy/gce - // .file().data( include_str!( "../../template/deploy/deploy/gce/Readme.md" ) ).path( "./deploy/gce/Readme.md" ).end() - // .file().data( include_str!( "../../template/deploy/deploy/gce/main.tf" ) ).path( "./deploy/gce/main.tf" ).end() - // .file().data( include_str!( "../../template/deploy/deploy/gce/outputs.tf" ) ).path( "./deploy/gce/outputs.tf" ).end() - // .file().data( include_str!( "../../template/deploy/deploy/gce/variables.tf" ) ).path( "./deploy/gce/variables.tf" ).end() - // // /deploy/gce/templates - // .file().data( include_str!( "../../template/deploy/deploy/gce/templates/cloud-init.tpl" ) ).path( "./deploy/gce/templates/cloud-init.tpl" ).end() - // // /deploy/gcs - // .file().data( include_str!( "../../template/deploy/deploy/gcs/main.tf" ) ).path( "./deploy/gcs/main.tf" ).end() - // // /deploy/hetzner - // .file().data( include_str!( "../../template/deploy/deploy/hetzner/main.tf" ) ).path( "./deploy/hetzner/main.tf" ).end() - // .file().data( include_str!( "../../template/deploy/deploy/hetzner/outputs.tf" ) ).path( "./deploy/hetzner/outputs.tf" ).end() - // .file().data( include_str!( "../../template/deploy/deploy/hetzner/variables.tf" ) ).path( "./deploy/hetzner/variables.tf" ).end() - // // /deploy/hetzner/templates - // .file().data( include_str!( "../../template/deploy/deploy/hetzner/templates/cloud-init.tpl" ) ).path( "./deploy/hetzner/templates/cloud-init.tpl" ).end() - // // /deploy/aws - // .file().data( include_str!( "../../template/deploy/deploy/aws/main.tf" ) ).path( "./deploy/aws/main.tf" ).end() - // .file().data( include_str!( "../../template/deploy/deploy/aws/outputs.tf" ) ).path( "./deploy/aws/outputs.tf" ).end() - // .file().data( include_str!( "../../template/deploy/deploy/aws/variables.tf" ) ).path( "./deploy/aws/variables.tf" ).end() - // // /deploy/aws/templates - // .file().data( include_str!( "../../template/deploy/deploy/aws/templates/cloud-init.tpl" ) ).path( "./deploy/aws/templates/cloud-init.tpl" ).end() - // .form(); - - // Self( formed.files ) - // } - // } - - // // qqq : for Viktor : should not be required - // impl TemplateFiles for DeployTemplateFiles {} - // // qqq : for Viktor : should not be required - // impl IntoIterator for DeployTemplateFiles - // { - // type Item = TemplateFileDescriptor; - - // type IntoIter = std::vec::IntoIter< Self::Item >; - - // fn into_iter( self ) -> Self::IntoIter - // { - // self.0.into_iter() - // } - // } + /// Template for creating deploy files. + /// + /// Includes terraform deploy options to GCP, and Hetzner, + /// a Makefile for useful commands, and a key directory. + #[ derive( Debug ) ] + pub struct DeployTemplate; + + impl DeployTemplate + { + /// Creates am instance of `[TemplateHolder]` for deployment template. + /// + /// Used for properly initializing a template + pub fn default() -> TemplateHolder + { + let parameters = TemplateParameters::former() + .parameter( "gcp_project_id" ).is_mandatory( true ).end() + .parameter( "gcp_region" ).end() + .parameter( "gcp_artifact_repo_name" ).end() + .parameter( "docker_image_name" ).end() + .form(); + + TemplateHolder + { + files : get_deploy_template_files(), + parameters, + values : Default::default(), + parameter_storage : "./.deploy_template.toml".as_ref(), + template_name : "deploy", + } + } + } - // aaa : for Petro : redundant function - // aaa : this function not my, but ok I'll remove it. + fn get_deploy_template_files() -> Vec< TemplateFileDescriptor > + { + let formed = TemplateFilesBuilder::former() + // root + .file().data( include_str!( "../../template/deploy/.deploy_template.toml.hbs" ) ).path( "./.deploy_template.toml" ).mode( WriteMode::TomlExtend ).is_template( true ).end() + .file().data( include_str!( "../../template/deploy/Makefile.hbs" ) ).path( "./Makefile" ).is_template( true ).end() + // /key + .file().data( include_str!( "../../template/deploy/key/pack.sh" ) ).path( "./key/pack.sh" ).end() + .file().data( include_str!( "../../template/deploy/key/Readme.md" ) ).path( "./key/Readme.md" ).end() + // /deploy/ + .file().data( include_str!( "../../template/deploy/deploy/Dockerfile" ) ).path( "./deploy/Dockerfile" ).end() + .file().data( include_str!( "../../template/deploy/deploy/Readme.md" ) ).path( "./deploy/Readme.md" ).end() + // /deploy/gar + .file().data( include_str!( "../../template/deploy/deploy/gar/Readme.md" ) ).path( "./deploy/gar/Readme.md" ).end() + .file().data( include_str!( "../../template/deploy/deploy/gar/main.tf" ) ).path( "./deploy/gar/main.tf" ).end() + .file().data( include_str!( "../../template/deploy/deploy/gar/outputs.tf" ) ).path( "./deploy/gar/outputs.tf" ).end() + .file().data( include_str!( "../../template/deploy/deploy/gar/variables.tf" ) ).path( "./deploy/gar/variables.tf" ).end() + // /deploy/gce + .file().data( include_str!( "../../template/deploy/deploy/gce/Readme.md" ) ).path( "./deploy/gce/Readme.md" ).end() + .file().data( include_str!( "../../template/deploy/deploy/gce/main.tf" ) ).path( "./deploy/gce/main.tf" ).end() + .file().data( include_str!( "../../template/deploy/deploy/gce/outputs.tf" ) ).path( "./deploy/gce/outputs.tf" ).end() + .file().data( include_str!( "../../template/deploy/deploy/gce/variables.tf" ) ).path( "./deploy/gce/variables.tf" ).end() + // /deploy/gce/templates + .file().data( include_str!( "../../template/deploy/deploy/gce/templates/cloud-init.tpl" ) ).path( "./deploy/gce/templates/cloud-init.tpl" ).end() + // /deploy/gcs + .file().data( include_str!( "../../template/deploy/deploy/gcs/main.tf" ) ).path( "./deploy/gcs/main.tf" ).end() + // /deploy/hetzner + .file().data( include_str!( "../../template/deploy/deploy/hetzner/main.tf.hbs" ) ).path( "./deploy/hetzner/main.tf" ).is_template( true ).end() + .file().data( include_str!( "../../template/deploy/deploy/hetzner/outputs.tf.hbs" ) ).path( "./deploy/hetzner/outputs.tf" ).is_template( true ).end() + .file().data( include_str!( "../../template/deploy/deploy/hetzner/variables.tf" ) ).path( "./deploy/hetzner/variables.tf" ).end() + // /deploy/hetzner/templates + .file().data( include_str!( "../../template/deploy/deploy/hetzner/templates/cloud-init.tpl.hbs" ) ).path( "./deploy/hetzner/templates/cloud-init.tpl" ).end() + // /deploy/aws + .file().data( include_str!( "../../template/deploy/deploy/aws/main.tf" ) ).path( "./deploy/aws/main.tf" ).end() + .file().data( include_str!( "../../template/deploy/deploy/aws/outputs.tf" ) ).path( "./deploy/aws/outputs.tf" ).end() + .file().data( include_str!( "../../template/deploy/deploy/aws/variables.tf" ) ).path( "./deploy/aws/variables.tf" ).end() + // /deploy/aws/templates + .file().data( include_str!( "../../template/deploy/deploy/aws/templates/cloud-init.tpl.hbs" ) ).path( "./deploy/aws/templates/cloud-init.tpl" ).is_template( true ).end() + .form(); + + formed.files + } fn dir_name_to_formatted( dir_name : &str, separator : &str ) -> String { @@ -179,7 +113,6 @@ mod private template .values .insert_if_empty( "gcp_artifact_repo_name", wca::Value::String( artifact_repo_name ) ); - template .values .insert_if_empty( "docker_image_name", wca::Value::String( docker_image_name ) ); @@ -187,7 +120,7 @@ mod private .values .insert_if_empty( "gcp_region", wca::Value::String( "europe-central2".into() ) ); } - template.create_all( path )?; + template.files.create_all( path, &template.values )?; Ok( () ) } @@ -196,5 +129,5 @@ mod private crate::mod_interface! { orphan use deploy_renew; - //orphan use DeployTemplate; + orphan use DeployTemplate; } diff --git a/module/move/willbe/src/command/deploy_renew.rs b/module/move/willbe/src/command/deploy_renew.rs index c66107fe8d..7e1e68e476 100644 --- a/module/move/willbe/src/command/deploy_renew.rs +++ b/module/move/willbe/src/command/deploy_renew.rs @@ -4,9 +4,7 @@ mod private use wca::VerifiedCommand; use error::{ untyped::Context }; - use tool::TemplateHolder; - //use tool::template::Template; - // use action::deploy_renew::*; + use action::deploy_renew::*; /// /// Create new deploy. @@ -17,7 +15,7 @@ mod private { let current_dir = std::env::current_dir()?; - let mut template = TemplateHolder::default(); + let mut template = DeployTemplate::default(); _ = template.load_existing_params( ¤t_dir ); let parameters = template.parameters(); let mut values = parameters.values_from_props( &o.props ); diff --git a/module/move/willbe/src/command/mod.rs b/module/move/willbe/src/command/mod.rs index f6115c3f10..6b87d5877b 100644 --- a/module/move/willbe/src/command/mod.rs +++ b/module/move/willbe/src/command/mod.rs @@ -225,7 +225,7 @@ with_gitpod: If set to 1, a column with a link to Gitpod will be added. Clicking .command( "deploy.renew" ) .hint( "Create deploy template" ) - .long_hint( "Creates static files and directories.\nDeployment to different hosts is done via Makefile." ) + .long_hint( "Creates static files and directories.\nDeployment to different hosts is done via Makefile.\n\nUsage example: deploy.renew gcp_project_id:wtools" ) .property( "gcp_project_id" ) .hint( "Google Cloud Platform Project id for image deployment, terraform state bucket, and, if specified, GCE instance deployment." ) .kind( Type::String ) @@ -239,12 +239,12 @@ with_gitpod: If set to 1, a column with a link to Gitpod will be added. Clicking .property( "gcp_artifact_repo_name" ) .hint( "Google Cloud Platform Artifact Repository to store docker image in. Will be generated from current directory name if unspecified." ) .kind( Type::String ) - .optional( false ) + .optional( true ) .end() .property( "docker_image_name" ) .hint( "Docker image name to build and deploy. Will be generated from current directory name if unspecified." ) .kind( Type::String ) - .optional( false ) + .optional( true ) .end() .routine( command::deploy_renew ) .end() diff --git a/module/move/willbe/src/tool/template.rs b/module/move/willbe/src/tool/template.rs index affe4072be..bd90b0b5d7 100644 --- a/module/move/willbe/src/tool/template.rs +++ b/module/move/willbe/src/tool/template.rs @@ -15,15 +15,10 @@ mod private }; use error::untyped::Context; - // qqq : for Nikita : is that trait really necessary? - // Template - remove - // DeployTemplate - move here - // DeployTemplateFiles - remove - - /// Template for creating deploy files. + /// Container for templates. /// - /// Includes terraform deploy options to GCP, and Hetzner, - /// a Makefile for useful commands, and a key directory. + /// Includes files to create, parameters that those templates accept, + /// and values for those parameters. #[ derive( Debug ) ] pub struct TemplateHolder { @@ -33,11 +28,15 @@ mod private pub parameters : TemplateParameters, /// The values associated with the template. pub values : TemplateValues, + /// Path to the parameter storage for recovering values + /// for already generated templated files. + pub parameter_storage : &'static Path, + /// Name of the template to generate + pub template_name : &'static str, } impl TemplateFiles for Vec< TemplateFileDescriptor > {} - // qqq : for Viktor : why DeployTemplate can't be part of template.rs? impl TemplateHolder { @@ -95,27 +94,6 @@ mod private &mut self.values } - /// Returns the path to the parameter storage file. - /// - /// # Returns - /// - /// A reference to a `Path` representing the parameter storage file. - pub fn parameter_storage( &self ) -> &Path - { - "./.deploy_template.toml".as_ref() - // qqq : for Mykyta : hardcode? - } - - /// Returns the name of the template. - /// - /// # Returns - /// - /// A static string slice representing the template name. - pub fn template_name( &self ) -> &'static str - { - "deploy" - } - /// Loads existing parameters from the specified path and updates the template values. /// /// # Parameters @@ -127,10 +105,10 @@ mod private /// An `Option` which is `Some(())` if the parameters are loaded successfully, or `None` otherwise. pub fn load_existing_params( &mut self, path : &Path ) -> Option< () > { - let data = fs::read_to_string( path.join( self.parameter_storage() ) ).ok()?; + let data = fs::read_to_string( path.join( self.parameter_storage ) ).ok()?; let document = data.parse::< toml_edit::Document >().ok()?; let parameters : Vec< _ > = self.parameters().descriptors.iter().map( | d | &d.parameter ).cloned().collect(); - let template_table = document.get( self.template_name() )?; + let template_table = document.get( self.template_name )?; for parameter in parameters { let value = template_table.get( ¶meter ) @@ -164,26 +142,6 @@ mod private } } - impl Default for TemplateHolder - { - fn default() -> Self - { - let parameters = TemplateParameters::former() - .parameter( "gcp_project_id" ).is_mandatory( true ).end() - .parameter( "gcp_region" ).end() - .parameter( "gcp_artifact_repo_name" ).end() - .parameter( "docker_image_name" ).end() - .form(); - - Self - { - files : Default::default(), - parameters, - values : Default::default(), - } - } - } - /// Files stored in a template. /// /// Can be iterated over, consuming the owner of the files. diff --git a/module/move/willbe/template/deploy/Makefile.hbs b/module/move/willbe/template/deploy/Makefile.hbs index 2f3461aea8..f3cc528435 100644 --- a/module/move/willbe/template/deploy/Makefile.hbs +++ b/module/move/willbe/template/deploy/Makefile.hbs @@ -26,7 +26,7 @@ export TF_VAR_IMAGE_NAME ?= {{docker_image_name}} # Path to the service account credentials export google_sa_creds ?= key/service_account.json # Cloud Storage bucket name -export TF_VAR_BUCKET_NAME ?= uaconf_tfstate +export TF_VAR_BUCKET_NAME ?= {{docker_image_name}}_tfstate # Specifies where to deploy the project. Possible values: `hetzner`, `gce`, `aws` export CSP ?= hetzner diff --git a/module/move/willbe/template/deploy/deploy/aws/templates/cloud-init.tpl b/module/move/willbe/template/deploy/deploy/aws/templates/cloud-init.tpl.hbs similarity index 95% rename from module/move/willbe/template/deploy/deploy/aws/templates/cloud-init.tpl rename to module/move/willbe/template/deploy/deploy/aws/templates/cloud-init.tpl.hbs index 7a19732c3a..78a74e4837 100644 --- a/module/move/willbe/template/deploy/deploy/aws/templates/cloud-init.tpl +++ b/module/move/willbe/template/deploy/deploy/aws/templates/cloud-init.tpl.hbs @@ -43,4 +43,4 @@ write_files: runcmd: -- nohup /root/init.sh > /var/log/uaconf-instance-init.log 2>&1 & +- nohup /root/init.sh > /var/log/{{docker_image_name}}-instance-init.log 2>&1 & diff --git a/module/move/willbe/template/deploy/deploy/hetzner/main.tf b/module/move/willbe/template/deploy/deploy/hetzner/main.tf.hbs similarity index 90% rename from module/move/willbe/template/deploy/deploy/hetzner/main.tf rename to module/move/willbe/template/deploy/deploy/hetzner/main.tf.hbs index b0f2f60194..5611dafc2d 100644 --- a/module/move/willbe/template/deploy/deploy/hetzner/main.tf +++ b/module/move/willbe/template/deploy/deploy/hetzner/main.tf.hbs @@ -16,7 +16,7 @@ provider "hcloud" { # Static IP for the instance resource "hcloud_primary_ip" "primary_ip" { - name = "uaconf-2024-ip" + name = "{{docker_image_name}}-ip" datacenter = "hel1-dc2" type = "ipv4" assignee_type = "server" @@ -24,8 +24,8 @@ resource "hcloud_primary_ip" "primary_ip" { } # Hetzner instance itself -resource "hcloud_server" "uaconf" { - name = "uaconf-2024" +resource "hcloud_server" "{{docker_image_name}}" { + name = "{{docker_image_name}}" image = "ubuntu-22.04" server_type = "cx22" datacenter = "hel1-dc2" diff --git a/module/move/willbe/template/deploy/deploy/hetzner/outputs.tf b/module/move/willbe/template/deploy/deploy/hetzner/outputs.tf.hbs similarity index 86% rename from module/move/willbe/template/deploy/deploy/hetzner/outputs.tf rename to module/move/willbe/template/deploy/deploy/hetzner/outputs.tf.hbs index f6d2ebd5e8..ba994920c7 100644 --- a/module/move/willbe/template/deploy/deploy/hetzner/outputs.tf +++ b/module/move/willbe/template/deploy/deploy/hetzner/outputs.tf.hbs @@ -1,5 +1,5 @@ locals { - ip = hcloud_server.uaconf.ipv4_address + ip = hcloud_server.{{docker_image_name}}.ipv4_address } # Output that we get after applying. diff --git a/module/move/willbe/template/deploy/deploy/hetzner/templates/cloud-init.tpl b/module/move/willbe/template/deploy/deploy/hetzner/templates/cloud-init.tpl.hbs similarity index 95% rename from module/move/willbe/template/deploy/deploy/hetzner/templates/cloud-init.tpl rename to module/move/willbe/template/deploy/deploy/hetzner/templates/cloud-init.tpl.hbs index 37cb18d6e9..081db47304 100644 --- a/module/move/willbe/template/deploy/deploy/hetzner/templates/cloud-init.tpl +++ b/module/move/willbe/template/deploy/deploy/hetzner/templates/cloud-init.tpl.hbs @@ -43,4 +43,4 @@ write_files: runcmd: -- nohup /root/init.sh > /var/log/uaconf-instance-init.log 2>&1 & +- nohup /root/init.sh > /var/log/{{docker_image_name}}-instance-init.log 2>&1 & From 1160b711a24320b5ea199e4c7267c2f4fff8ada9 Mon Sep 17 00:00:00 2001 From: wandalen Date: Mon, 9 Sep 2024 18:24:48 +0300 Subject: [PATCH 16/67] willbe-v0.17.0 --- Cargo.toml | 2 +- module/move/willbe/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 1ab5b51426..9502ab371a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -436,7 +436,7 @@ path = "module/move/wcensor" ## willbe [workspace.dependencies.willbe] -version = "~0.16.0" +version = "~0.17.0" path = "module/move/willbe" diff --git a/module/move/willbe/Cargo.toml b/module/move/willbe/Cargo.toml index 0faeec2755..f767e596df 100644 --- a/module/move/willbe/Cargo.toml +++ b/module/move/willbe/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "willbe" -version = "0.16.0" +version = "0.17.0" edition = "2021" authors = [ "Kostiantyn Wandalen ", From 3a0204dfd0c98f505d9c86597099ebbf351feec0 Mon Sep 17 00:00:00 2001 From: Viktor Dudnik <37380849+0x07C0@users.noreply.github.com> Date: Mon, 9 Sep 2024 18:56:58 +0300 Subject: [PATCH 17/67] fix: replace spaces in makefile with tabs --- .../move/willbe/template/deploy/Makefile.hbs | 128 +++++++++--------- 1 file changed, 64 insertions(+), 64 deletions(-) diff --git a/module/move/willbe/template/deploy/Makefile.hbs b/module/move/willbe/template/deploy/Makefile.hbs index 2f3461aea8..f86b1dc800 100644 --- a/module/move/willbe/template/deploy/Makefile.hbs +++ b/module/move/willbe/template/deploy/Makefile.hbs @@ -43,129 +43,129 @@ export AWS_ACCESS_KEY_ID ?= $(SECRET_AWS_ACCESS_KEY_ID) # AWS Secret Access key for deploying to an EC2 instance export AWS_SECRET_ACCESS_KEY ?= $(SECRET_AWS_ACCESS_KEY) -# Check Hetzner and deployment related keys +# Check Hetzner and deployment related keys check-hetzner-keys: - @[ -f key/SECRET_CSP_HETZNER ] \ + @[ -f key/SECRET_CSP_HETZNER ] \ || [ ! -z "${SECRET_CSP_HETZNER}" ] \ || { echo "ERROR: File key/SECRET_CSP_HETZNER does not exist"; exit 1; } -# Check AWS and deployment related keys +# Check AWS and deployment related keys check-aws-keys: - @[ -f key/SECRET_AWS_ACCESS_KEY_ID ] \ + @[ -f key/SECRET_AWS_ACCESS_KEY_ID ] \ || [ ! -z "${SECRET_AWS_ACCESS_KEY_ID}" ] \ || echo "ERROR: File key/SECRET_AWS_ACCESS_KEY_ID does not exist" - @[ -f key/SECRET_AWS_ACCESS_KEY ] \ + @[ -f key/SECRET_AWS_ACCESS_KEY ] \ || [ ! -z "${SECRET_AWS_ACCESS_KEY}" ] \ || echo "ERROR: File key/SECRET_AWS_ACCESS_KEY does not exist" - @[ -f key/SECRET_AWS_ACCESS_KEY_ID ] \ + @[ -f key/SECRET_AWS_ACCESS_KEY_ID ] \ || [ ! -z "${SECRET_AWS_ACCESS_KEY_ID}" ] \ || exit 1 - @[ -f key/SECRET_AWS_ACCESS_KEY ] \ + @[ -f key/SECRET_AWS_ACCESS_KEY ] \ || [ ! -z "${SECRET_AWS_ACCESS_KEY}" ] \ || exit 1 check-gce-keys: - @echo "All required GCE keys are the same as GCP keys" + @echo "All required GCE keys are the same as GCP keys" # Check if required GCP keys are present check-gcp-keys: - @[ -f key/service_account.json ] \ + @[ -f key/service_account.json ] \ || echo "ERROR: File key/service_account.json does not exist" - @[ -f key/SECRET_STATE_ARCHIVE_KEY ] \ + @[ -f key/SECRET_STATE_ARCHIVE_KEY ] \ || [ ! -z "${SECRET_STATE_ARCHIVE_KEY}" ] \ || echo "ERROR: File key/SECRET_STATE_ARCHIVE_KEY does not exist" - @[ -f key/service_account.json ] \ + @[ -f key/service_account.json ] \ || exit 1 - @[ -f key/SECRET_STATE_ARCHIVE_KEY ] \ + @[ -f key/SECRET_STATE_ARCHIVE_KEY ] \ || [ ! -z "${SECRET_STATE_ARCHIVE_KEY}" ] \ || exit 1 # Start local docker container start: - docker compose up -d + docker compose up -d # Stop local docker container stop: - docker compose down + docker compose down # Remove created docker image clean: stop - docker rmi $(TF_VAR_IMAGE_NAME) - docker buildx prune -af + docker rmi $(TF_VAR_IMAGE_NAME) + docker buildx prune -af # Install gcloud for Debian/Ubuntu install-gcloud: - # GCloud - sudo apt-get update - sudo apt-get install -y apt-transport-https ca-certificates gnupg curl sudo - curl https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo gpg --dearmor -o /usr/share/keyrings/cloud.google.gpg - echo "deb [signed-by=/usr/share/keyrings/cloud.google.gpg] https://packages.cloud.google.com/apt cloud-sdk main" | sudo tee -a /etc/apt/sources.list.d/google-cloud-sdk.list - sudo apt-get update && sudo apt-get install -y google-cloud-cli + # GCloud + sudo apt-get update + sudo apt-get install -y apt-transport-https ca-certificates gnupg curl sudo + curl https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo gpg --dearmor -o /usr/share/keyrings/cloud.google.gpg + echo "deb [signed-by=/usr/share/keyrings/cloud.google.gpg] https://packages.cloud.google.com/apt cloud-sdk main" | sudo tee -a /etc/apt/sources.list.d/google-cloud-sdk.list + sudo apt-get update && sudo apt-get install -y google-cloud-cli # Install terraform for Debian/Ubuntu install-terraform: - sudo apt-get update && sudo apt-get install -y gnupg software-properties-common - wget -O- https://apt.releases.hashicorp.com/gpg | gpg --dearmor | sudo tee /usr/share/keyrings/hashicorp-archive-keyring.gpg - gpg --no-default-keyring --keyring /usr/share/keyrings/hashicorp-archive-keyring.gpg --fingerprint - echo "deb [signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/hashicorp.list - sudo apt update && sudo apt-get install terraform + sudo apt-get update && sudo apt-get install -y gnupg software-properties-common + wget -O- https://apt.releases.hashicorp.com/gpg | gpg --dearmor | sudo tee /usr/share/keyrings/hashicorp-archive-keyring.gpg + gpg --no-default-keyring --keyring /usr/share/keyrings/hashicorp-archive-keyring.gpg --fingerprint + echo "deb [signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/hashicorp.list + sudo apt update && sudo apt-get install terraform # Install gcloud and terraform install: install-gcloud install-terraform - gcloud --version - terraform -version + gcloud --version + terraform -version # Login to GCP with user account gcp-auth: - gcloud auth application-default login + gcloud auth application-default login # Authorize to GCP with service account gcp-service: - gcloud auth activate-service-account --key-file=$(google_sa_creds) + gcloud auth activate-service-account --key-file=$(google_sa_creds) # Add docker repo auth helper gcp-docker: - gcloud auth configure-docker $(TF_VAR_REGION)-docker.pkg.dev --quiet + gcloud auth configure-docker $(TF_VAR_REGION)-docker.pkg.dev --quiet # Initializes all terraform projects # Downloads required modules and validates .tf files tf-init: - terraform -chdir=$(tf_dir)/gar init - terraform -chdir=$(tf_dir)/gce init - terraform -chdir=$(tf_dir)/hetzner init - terraform -chdir=$(tf_dir)/aws init + terraform -chdir=$(tf_dir)/gar init + terraform -chdir=$(tf_dir)/gce init + terraform -chdir=$(tf_dir)/hetzner init + terraform -chdir=$(tf_dir)/aws init # Creates Artifact Registry repository on GCP in specified location create-artifact-repo: tf-init - terraform -chdir=$(tf_dir)/gar apply -auto-approve + terraform -chdir=$(tf_dir)/gar apply -auto-approve # Builds uarust_conf_site image build-image: - docker build . -t name:$(TF_VAR_IMAGE_NAME) -t $(tag) + docker build . -t name:$(TF_VAR_IMAGE_NAME) -t $(tag) # Builds and pushes local docker image to the private repository push-image: gcp-docker create-artifact-repo - docker push $(tag) + docker push $(tag) # Creates GCE instance with the website configured on boot create-gce: check-gce-keys gcp-service state_storage_pull push-image - terraform -chdir=$(tf_dir)/gce apply -auto-approve + terraform -chdir=$(tf_dir)/gce apply -auto-approve # Creates AWS EC2 instance with the website configured on boot create-aws: check-aws-keys gcp-service state_storage_pull push-image - terraform -chdir=$(tf_dir)/aws apply -auto-approve + terraform -chdir=$(tf_dir)/aws apply -auto-approve # Creates Hetzner instance with the website configured on boot create-hetzner: check-hetzner-keys gcp-service state_storage_pull push-image - terraform -chdir=$(tf_dir)/hetzner apply -auto-approve + terraform -chdir=$(tf_dir)/hetzner apply -auto-approve # Deploys everything and updates terraform states deploy-in-container: create-$(CSP) state_storage_push # Deploys using tools from the container deploy: check-gcp-keys build-image - docker build . -t deploy-$(TF_VAR_IMAGE_NAME) -f ./$(tf_dir)/Dockerfile --build-arg google_sa_creds="$(google_sa_creds)" - @docker run -v //var/run/docker.sock:/var/run/docker.sock -v .:/app \ + docker build . -t deploy-$(TF_VAR_IMAGE_NAME) -f ./$(tf_dir)/Dockerfile --build-arg google_sa_creds="$(google_sa_creds)" + @docker run -v //var/run/docker.sock:/var/run/docker.sock -v .:/app \ -e SECRET_STATE_ARCHIVE_KEY=$(SECRET_STATE_ARCHIVE_KEY) \ -e SECRET_CSP_HETZNER=$(SECRET_CSP_HETZNER) \ -e SECRET_AWS_ACCESS_KEY_ID=$(SECRET_AWS_ACCESS_KEY_ID) \ @@ -175,35 +175,35 @@ deploy: check-gcp-keys build-image # Review changes that terraform will do on apply tf-plan: tf-init - terraform -chdir=$(tf_dir)/gar plan - terraform -chdir=$(tf_dir)/gce plan - terraform -chdir=$(tf_dir)/hetzner plan - terraform -chdir=$(tf_dir)/aws plan + terraform -chdir=$(tf_dir)/gar plan + terraform -chdir=$(tf_dir)/gce plan + terraform -chdir=$(tf_dir)/hetzner plan + terraform -chdir=$(tf_dir)/aws plan # Destroy created infrastracture on GCP tf-destroy: tf-init - terraform -chdir=$(tf_dir)/gar destroy - terraform -chdir=$(tf_dir)/gce destroy - terraform -chdir=$(tf_dir)/hetzner destroy - terraform -chdir=$(tf_dir)/aws destroy + terraform -chdir=$(tf_dir)/gar destroy + terraform -chdir=$(tf_dir)/gce destroy + terraform -chdir=$(tf_dir)/hetzner destroy + terraform -chdir=$(tf_dir)/aws destroy # Pushes encrypted terraform state files to the GCS Bucket state_storage_push: - @echo Pushing encrypted terraform state files to the GCS Bucket - @gcloud storage cp $(tf_dir)/gce/terraform.tfstate gs://$(TF_VAR_BUCKET_NAME)/gce.tfstate --encryption-key="$(SECRET_STATE_ARCHIVE_KEY)" - @gcloud storage cp $(tf_dir)/gar/terraform.tfstate gs://$(TF_VAR_BUCKET_NAME)/gar.tfstate --encryption-key="$(SECRET_STATE_ARCHIVE_KEY)" - @gcloud storage cp $(tf_dir)/hetzner/terraform.tfstate gs://$(TF_VAR_BUCKET_NAME)/hetzner.tfstate --encryption-key="$(SECRET_STATE_ARCHIVE_KEY)" - @gcloud storage cp $(tf_dir)/aws/terraform.tfstate gs://$(TF_VAR_BUCKET_NAME)/aws.tfstate --encryption-key="$(SECRET_STATE_ARCHIVE_KEY)" + @echo Pushing encrypted terraform state files to the GCS Bucket + -@gcloud storage cp $(tf_dir)/gce/terraform.tfstate gs://$(TF_VAR_BUCKET_NAME)/gce.tfstate --encryption-key="$(SECRET_STATE_ARCHIVE_KEY)" + -@gcloud storage cp $(tf_dir)/gar/terraform.tfstate gs://$(TF_VAR_BUCKET_NAME)/gar.tfstate --encryption-key="$(SECRET_STATE_ARCHIVE_KEY)" + -@gcloud storage cp $(tf_dir)/hetzner/terraform.tfstate gs://$(TF_VAR_BUCKET_NAME)/hetzner.tfstate --encryption-key="$(SECRET_STATE_ARCHIVE_KEY)" + -@gcloud storage cp $(tf_dir)/aws/terraform.tfstate gs://$(TF_VAR_BUCKET_NAME)/aws.tfstate --encryption-key="$(SECRET_STATE_ARCHIVE_KEY)" # Pulls and decrypts terraform state files to the GCS Bucket state_storage_pull: - @echo Pulling terraform state files to the GCS Bucket - -@gcloud storage cp gs://$(TF_VAR_BUCKET_NAME)/gce.tfstate $(tf_dir)/gce/terraform.tfstate --decryption-keys="$(SECRET_STATE_ARCHIVE_KEY)" - -@gcloud storage cp gs://$(TF_VAR_BUCKET_NAME)/gar.tfstate $(tf_dir)/gar/terraform.tfstate --decryption-keys="$(SECRET_STATE_ARCHIVE_KEY)" - -@gcloud storage cp gs://$(TF_VAR_BUCKET_NAME)/hetzner.tfstate $(tf_dir)/hetzner/terraform.tfstate --decryption-keys="$(SECRET_STATE_ARCHIVE_KEY)" - -@gcloud storage cp gs://$(TF_VAR_BUCKET_NAME)/aws.tfstate $(tf_dir)/aws/terraform.tfstate --decryption-keys="$(SECRET_STATE_ARCHIVE_KEY)" + @echo Pulling terraform state files to the GCS Bucket + -@gcloud storage cp gs://$(TF_VAR_BUCKET_NAME)/gce.tfstate $(tf_dir)/gce/terraform.tfstate --decryption-keys="$(SECRET_STATE_ARCHIVE_KEY)" + -@gcloud storage cp gs://$(TF_VAR_BUCKET_NAME)/gar.tfstate $(tf_dir)/gar/terraform.tfstate --decryption-keys="$(SECRET_STATE_ARCHIVE_KEY)" + -@gcloud storage cp gs://$(TF_VAR_BUCKET_NAME)/hetzner.tfstate $(tf_dir)/hetzner/terraform.tfstate --decryption-keys="$(SECRET_STATE_ARCHIVE_KEY)" + -@gcloud storage cp gs://$(TF_VAR_BUCKET_NAME)/aws.tfstate $(tf_dir)/aws/terraform.tfstate --decryption-keys="$(SECRET_STATE_ARCHIVE_KEY)" # Creates GCS Bucket for terraform states state_storage_init: - terraform -chdir=$(tf_dir)/gcs init - terraform -chdir=$(tf_dir)/gcs apply + terraform -chdir=$(tf_dir)/gcs init + terraform -chdir=$(tf_dir)/gcs apply From f75fd91f19b4054c999de475657172a546e3eb94 Mon Sep 17 00:00:00 2001 From: wandalen Date: Mon, 9 Sep 2024 19:01:34 +0300 Subject: [PATCH 18/67] willbe-v0.18.0 --- Cargo.toml | 2 +- module/move/willbe/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 9502ab371a..7881396f5f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -436,7 +436,7 @@ path = "module/move/wcensor" ## willbe [workspace.dependencies.willbe] -version = "~0.17.0" +version = "~0.18.0" path = "module/move/willbe" diff --git a/module/move/willbe/Cargo.toml b/module/move/willbe/Cargo.toml index f767e596df..1555f1d3fa 100644 --- a/module/move/willbe/Cargo.toml +++ b/module/move/willbe/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "willbe" -version = "0.17.0" +version = "0.18.0" edition = "2021" authors = [ "Kostiantyn Wandalen ", From 256dd381ac5b3f608eb331b1ea3e66ef0b623808 Mon Sep 17 00:00:00 2001 From: wbot <69343704+wtools-bot@users.noreply.github.com> Date: Tue, 10 Sep 2024 17:45:14 +0300 Subject: [PATCH 19/67] AUTO : Forward from format_tools_evolving_4 to alpha (#1459) mod_interface : fix it and implement reuse --- Cargo.toml | 4 +- module/blank/mindx12/Cargo.toml | 34 +++ module/blank/mindx12/License | 22 ++ module/blank/mindx12/Readme.md | 33 +++ module/blank/mindx12/src/lib.rs | 10 + module/blank/mindx12/tests/inc/basic_test.rs | 7 + module/blank/mindx12/tests/inc/mod.rs | 4 + module/blank/mindx12/tests/smoke_test.rs | 14 ++ module/blank/mindx12/tests/tests.rs | 10 + module/blank/mingl/Cargo.toml | 34 +++ module/blank/mingl/License | 22 ++ module/blank/mingl/Readme.md | 33 +++ module/blank/mingl/src/lib.rs | 10 + module/blank/mingl/tests/inc/basic_test.rs | 7 + module/blank/mingl/tests/inc/mod.rs | 4 + module/blank/mingl/tests/smoke_test.rs | 14 ++ module/blank/mingl/tests/tests.rs | 10 + module/blank/minmetal/Cargo.toml | 34 +++ module/blank/minmetal/License | 22 ++ module/blank/minmetal/Readme.md | 33 +++ module/blank/minmetal/src/lib.rs | 10 + module/blank/minmetal/tests/inc/basic_test.rs | 7 + module/blank/minmetal/tests/inc/mod.rs | 4 + module/blank/minmetal/tests/smoke_test.rs | 14 ++ module/blank/minmetal/tests/tests.rs | 10 + module/blank/minopengl/Cargo.toml | 34 +++ module/blank/minopengl/License | 22 ++ module/blank/minopengl/Readme.md | 33 +++ module/blank/minopengl/src/lib.rs | 10 + .../blank/minopengl/tests/inc/basic_test.rs | 7 + module/blank/minopengl/tests/inc/mod.rs | 4 + module/blank/minopengl/tests/smoke_test.rs | 14 ++ module/blank/minopengl/tests/tests.rs | 10 + module/blank/minvulkan/Cargo.toml | 34 +++ module/blank/minvulkan/License | 22 ++ module/blank/minvulkan/Readme.md | 33 +++ module/blank/minvulkan/src/lib.rs | 10 + .../blank/minvulkan/tests/inc/basic_test.rs | 7 + module/blank/minvulkan/tests/inc/mod.rs | 4 + module/blank/minvulkan/tests/smoke_test.rs | 14 ++ module/blank/minvulkan/tests/tests.rs | 10 + module/blank/minwebgl/Cargo.toml | 34 +++ module/blank/minwebgl/License | 22 ++ module/blank/minwebgl/Readme.md | 33 +++ module/blank/minwebgl/src/lib.rs | 10 + module/blank/minwebgl/tests/inc/basic_test.rs | 7 + module/blank/minwebgl/tests/inc/mod.rs | 4 + module/blank/minwebgl/tests/smoke_test.rs | 14 ++ module/blank/minwebgl/tests/tests.rs | 10 + module/blank/minwebgpu/Cargo.toml | 34 +++ module/blank/minwebgpu/License | 22 ++ module/blank/minwebgpu/Readme.md | 33 +++ module/blank/minwebgpu/src/lib.rs | 10 + .../blank/minwebgpu/tests/inc/basic_test.rs | 7 + module/blank/minwebgpu/tests/inc/mod.rs | 4 + module/blank/minwebgpu/tests/smoke_test.rs | 14 ++ module/blank/minwebgpu/tests/tests.rs | 10 + module/blank/minwgpu/Cargo.toml | 34 +++ module/blank/minwgpu/License | 22 ++ module/blank/minwgpu/Readme.md | 33 +++ module/blank/minwgpu/src/lib.rs | 10 + module/blank/minwgpu/tests/inc/basic_test.rs | 7 + module/blank/minwgpu/tests/inc/mod.rs | 4 + module/blank/minwgpu/tests/smoke_test.rs | 14 ++ module/blank/minwgpu/tests/tests.rs | 10 + module/core/meta_tools/src/lib.rs | 3 + module/core/mod_interface/Cargo.toml | 2 +- module/core/mod_interface/Readme.md | 191 ++++++++++++----- .../examples/mod_interface_debug/Readme.md | 4 +- .../src/{inner.rs => child.rs} | 2 +- .../examples/mod_interface_debug/src/main.rs | 14 +- .../mod_interface_trivial/src/child.rs | 23 ++ .../mod_interface_trivial/src/inner.rs | 15 -- .../mod_interface_trivial/src/main.rs | 52 +++-- .../tests/inc/derive/attr_debug/mod.rs | 4 +- .../tests/inc/derive/micro_modules/mod.rs | 1 + .../{mod_protected.rs => mod_own.rs} | 0 .../micro_modules_glob/child.rs} | 0 .../inc/derive/micro_modules_glob/mod.rs | 28 +++ .../{mod_protected1.rs => mod_own1.rs} | 0 .../{mod_protected2.rs => mod_own2.rs} | 0 .../{mod_protected1.rs => mod_own1.rs} | 0 .../{mod_protected2.rs => mod_own2.rs} | 0 .../tests/inc/derive/reuse_basic/child.rs | 15 ++ .../tests/inc/derive/reuse_basic/mod.rs | 34 +++ .../tests/inc/derive/use_as/derive.rs | 4 +- .../tests/inc/derive/use_as/manual_only.rs | 2 +- .../tests/inc/derive/use_basic/mod.rs | 2 + .../tests/inc/derive/use_layer/mod.rs | 7 +- .../tests/inc/manual/micro_modules/mod_own.rs | 5 + .../{mod_protected1.rs => mod_own1.rs} | 0 .../{mod_protected2.rs => mod_own2.rs} | 0 module/core/mod_interface/tests/inc/mod.rs | 9 +- module/core/mod_interface_meta/Cargo.toml | 2 +- module/core/mod_interface_meta/src/impls.rs | 197 +++++++++++++----- module/core/mod_interface_meta/src/lib.rs | 3 +- module/core/mod_interface_meta/src/record.rs | 9 +- .../core/mod_interface_meta/src/use_tree.rs | 59 ++++-- module/core/process_tools/src/lib.rs | 2 + module/core/proper_path_tools/src/lib.rs | 2 + module/core/test_tools/src/lib.rs | 4 + .../core/test_tools/src/test/compiletime.rs | 62 ++++++ module/core/test_tools/src/test/helper.rs | 4 +- module/core/test_tools/src/test/mod.rs | 2 + module/move/wca/src/ca/executor/mod.rs | 2 + module/move/wca/src/ca/grammar/mod.rs | 2 + module/move/wca/src/ca/mod.rs | 2 + module/move/wca/src/ca/parser/mod.rs | 4 +- module/move/wca/src/ca/tool/mod.rs | 2 + module/move/wca/src/ca/verifier/mod.rs | 2 + module/move/wca/src/lib.rs | 2 + module/move/willbe/src/action/mod.rs | 2 + module/move/willbe/src/entity/mod.rs | 2 + module/move/willbe/src/tool/mod.rs | 2 + 114 files changed, 1689 insertions(+), 172 deletions(-) create mode 100644 module/blank/mindx12/Cargo.toml create mode 100644 module/blank/mindx12/License create mode 100644 module/blank/mindx12/Readme.md create mode 100644 module/blank/mindx12/src/lib.rs create mode 100644 module/blank/mindx12/tests/inc/basic_test.rs create mode 100644 module/blank/mindx12/tests/inc/mod.rs create mode 100644 module/blank/mindx12/tests/smoke_test.rs create mode 100644 module/blank/mindx12/tests/tests.rs create mode 100644 module/blank/mingl/Cargo.toml create mode 100644 module/blank/mingl/License create mode 100644 module/blank/mingl/Readme.md create mode 100644 module/blank/mingl/src/lib.rs create mode 100644 module/blank/mingl/tests/inc/basic_test.rs create mode 100644 module/blank/mingl/tests/inc/mod.rs create mode 100644 module/blank/mingl/tests/smoke_test.rs create mode 100644 module/blank/mingl/tests/tests.rs create mode 100644 module/blank/minmetal/Cargo.toml create mode 100644 module/blank/minmetal/License create mode 100644 module/blank/minmetal/Readme.md create mode 100644 module/blank/minmetal/src/lib.rs create mode 100644 module/blank/minmetal/tests/inc/basic_test.rs create mode 100644 module/blank/minmetal/tests/inc/mod.rs create mode 100644 module/blank/minmetal/tests/smoke_test.rs create mode 100644 module/blank/minmetal/tests/tests.rs create mode 100644 module/blank/minopengl/Cargo.toml create mode 100644 module/blank/minopengl/License create mode 100644 module/blank/minopengl/Readme.md create mode 100644 module/blank/minopengl/src/lib.rs create mode 100644 module/blank/minopengl/tests/inc/basic_test.rs create mode 100644 module/blank/minopengl/tests/inc/mod.rs create mode 100644 module/blank/minopengl/tests/smoke_test.rs create mode 100644 module/blank/minopengl/tests/tests.rs create mode 100644 module/blank/minvulkan/Cargo.toml create mode 100644 module/blank/minvulkan/License create mode 100644 module/blank/minvulkan/Readme.md create mode 100644 module/blank/minvulkan/src/lib.rs create mode 100644 module/blank/minvulkan/tests/inc/basic_test.rs create mode 100644 module/blank/minvulkan/tests/inc/mod.rs create mode 100644 module/blank/minvulkan/tests/smoke_test.rs create mode 100644 module/blank/minvulkan/tests/tests.rs create mode 100644 module/blank/minwebgl/Cargo.toml create mode 100644 module/blank/minwebgl/License create mode 100644 module/blank/minwebgl/Readme.md create mode 100644 module/blank/minwebgl/src/lib.rs create mode 100644 module/blank/minwebgl/tests/inc/basic_test.rs create mode 100644 module/blank/minwebgl/tests/inc/mod.rs create mode 100644 module/blank/minwebgl/tests/smoke_test.rs create mode 100644 module/blank/minwebgl/tests/tests.rs create mode 100644 module/blank/minwebgpu/Cargo.toml create mode 100644 module/blank/minwebgpu/License create mode 100644 module/blank/minwebgpu/Readme.md create mode 100644 module/blank/minwebgpu/src/lib.rs create mode 100644 module/blank/minwebgpu/tests/inc/basic_test.rs create mode 100644 module/blank/minwebgpu/tests/inc/mod.rs create mode 100644 module/blank/minwebgpu/tests/smoke_test.rs create mode 100644 module/blank/minwebgpu/tests/tests.rs create mode 100644 module/blank/minwgpu/Cargo.toml create mode 100644 module/blank/minwgpu/License create mode 100644 module/blank/minwgpu/Readme.md create mode 100644 module/blank/minwgpu/src/lib.rs create mode 100644 module/blank/minwgpu/tests/inc/basic_test.rs create mode 100644 module/blank/minwgpu/tests/inc/mod.rs create mode 100644 module/blank/minwgpu/tests/smoke_test.rs create mode 100644 module/blank/minwgpu/tests/tests.rs rename module/core/mod_interface/examples/mod_interface_debug/src/{inner.rs => child.rs} (80%) create mode 100644 module/core/mod_interface/examples/mod_interface_trivial/src/child.rs delete mode 100644 module/core/mod_interface/examples/mod_interface_trivial/src/inner.rs rename module/core/mod_interface/tests/inc/derive/micro_modules/{mod_protected.rs => mod_own.rs} (100%) rename module/core/mod_interface/tests/inc/{manual/micro_modules/mod_protected.rs => derive/micro_modules_glob/child.rs} (100%) create mode 100644 module/core/mod_interface/tests/inc/derive/micro_modules_glob/mod.rs rename module/core/mod_interface/tests/inc/derive/micro_modules_two/{mod_protected1.rs => mod_own1.rs} (100%) rename module/core/mod_interface/tests/inc/derive/micro_modules_two/{mod_protected2.rs => mod_own2.rs} (100%) rename module/core/mod_interface/tests/inc/derive/micro_modules_two_joined/{mod_protected1.rs => mod_own1.rs} (100%) rename module/core/mod_interface/tests/inc/derive/micro_modules_two_joined/{mod_protected2.rs => mod_own2.rs} (100%) create mode 100644 module/core/mod_interface/tests/inc/derive/reuse_basic/child.rs create mode 100644 module/core/mod_interface/tests/inc/derive/reuse_basic/mod.rs create mode 100644 module/core/mod_interface/tests/inc/manual/micro_modules/mod_own.rs rename module/core/mod_interface/tests/inc/manual/micro_modules_two/{mod_protected1.rs => mod_own1.rs} (100%) rename module/core/mod_interface/tests/inc/manual/micro_modules_two/{mod_protected2.rs => mod_own2.rs} (100%) diff --git a/Cargo.toml b/Cargo.toml index 7881396f5f..00ffdb5cdf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -248,12 +248,12 @@ version = "~0.7.0" path = "module/core/impls_index_meta" [workspace.dependencies.mod_interface] -version = "~0.24.0" +version = "~0.25.0" path = "module/core/mod_interface" default-features = false [workspace.dependencies.mod_interface_meta] -version = "~0.24.0" +version = "~0.25.0" path = "module/core/mod_interface_meta" default-features = false diff --git a/module/blank/mindx12/Cargo.toml b/module/blank/mindx12/Cargo.toml new file mode 100644 index 0000000000..a26d724817 --- /dev/null +++ b/module/blank/mindx12/Cargo.toml @@ -0,0 +1,34 @@ +[package] +name = "mindx12" +version = "0.1.0" +edition = "2021" +authors = [ + "Kostiantyn Wandalen ", +] +license = "MIT" +readme = "Readme.md" +documentation = "https://docs.rs/mindx12" +repository = "https://github.com/Wandalen/wTools/tree/master/module/core/mindx12" +homepage = "https://github.com/Wandalen/wTools/tree/master/module/core/mindx12" +description = """ +Draw language. +""" +categories = [ "algorithms", "development-tools" ] +keywords = [ "fundamental", "general-purpose" ] + +[lints] +workspace = true + +[package.metadata.docs.rs] +features = [ "full" ] +all-features = false + +[features] +default = [ "enabled" ] +full = [ "enabled" ] +enabled = [] + +[dependencies] + +[dev-dependencies] +test_tools = { workspace = true } diff --git a/module/blank/mindx12/License b/module/blank/mindx12/License new file mode 100644 index 0000000000..6d5ef8559f --- /dev/null +++ b/module/blank/mindx12/License @@ -0,0 +1,22 @@ +Copyright Kostiantyn W and Out of the Box Systems (c) 2013-2024 + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. diff --git a/module/blank/mindx12/Readme.md b/module/blank/mindx12/Readme.md new file mode 100644 index 0000000000..adc110369e --- /dev/null +++ b/module/blank/mindx12/Readme.md @@ -0,0 +1,33 @@ + + +# Module :: mindx12 +[![experimental](https://raster.shields.io/static/v1?label=stability&message=experimental&color=orange&logoColor=eee)](https://github.com/emersion/stability-badges#experimental) [![rust-status](https://github.com/Wandalen/wTools/actions/workflows/Modulemindx12Push.yml/badge.svg)](https://github.com/Wandalen/wTools/actions/workflows/Modulemindx12Push.yml) [![docs.rs](https://img.shields.io/docsrs/mindx12?color=e3e8f0&logo=docs.rs)](https://docs.rs/mindx12) [![discord](https://img.shields.io/discord/872391416519737405?color=eee&logo=discord&logoColor=eee&label=ask)](https://discord.gg/m3YfbXpUUY) + +Draw language. + + diff --git a/module/blank/mindx12/src/lib.rs b/module/blank/mindx12/src/lib.rs new file mode 100644 index 0000000000..8736456366 --- /dev/null +++ b/module/blank/mindx12/src/lib.rs @@ -0,0 +1,10 @@ + +#![ doc( html_logo_url = "https://raw.githubusercontent.com/Wandalen/wTools/master/asset/img/logo_v3_trans_square.png" ) ] +#![ doc( html_favicon_url = "https://raw.githubusercontent.com/Wandalen/wTools/alpha/asset/img/logo_v3_trans_square_icon_small_v2.ico" ) ] +#![ doc = include_str!( concat!( env!( "CARGO_MANIFEST_DIR" ), "/", "Readme.md" ) ) ] + +/// Function description. +#[ cfg( feature = "enabled" ) ] +pub fn f1() +{ +} diff --git a/module/blank/mindx12/tests/inc/basic_test.rs b/module/blank/mindx12/tests/inc/basic_test.rs new file mode 100644 index 0000000000..60c9a81cfb --- /dev/null +++ b/module/blank/mindx12/tests/inc/basic_test.rs @@ -0,0 +1,7 @@ +#[ allow( unused_imports ) ] +use super::*; + +#[ test ] +fn basic() +{ +} diff --git a/module/blank/mindx12/tests/inc/mod.rs b/module/blank/mindx12/tests/inc/mod.rs new file mode 100644 index 0000000000..dde9de6f94 --- /dev/null +++ b/module/blank/mindx12/tests/inc/mod.rs @@ -0,0 +1,4 @@ +#[ allow( unused_imports ) ] +use super::*; + +mod basic_test; diff --git a/module/blank/mindx12/tests/smoke_test.rs b/module/blank/mindx12/tests/smoke_test.rs new file mode 100644 index 0000000000..828e9b016b --- /dev/null +++ b/module/blank/mindx12/tests/smoke_test.rs @@ -0,0 +1,14 @@ + + +#[ test ] +fn local_smoke_test() +{ + ::test_tools::smoke_test_for_local_run(); +} + + +#[ test ] +fn published_smoke_test() +{ + ::test_tools::smoke_test_for_published_run(); +} diff --git a/module/blank/mindx12/tests/tests.rs b/module/blank/mindx12/tests/tests.rs new file mode 100644 index 0000000000..5a33e742f0 --- /dev/null +++ b/module/blank/mindx12/tests/tests.rs @@ -0,0 +1,10 @@ + +include!( "../../../../module/step/meta/src/module/terminal.rs" ); + +#[ allow( unused_imports ) ] +use mindx12 as the_module; +#[ allow( unused_imports ) ] +use test_tools::exposed::*; + +#[ cfg( feature = "enabled" ) ] +mod inc; diff --git a/module/blank/mingl/Cargo.toml b/module/blank/mingl/Cargo.toml new file mode 100644 index 0000000000..dbd89af97e --- /dev/null +++ b/module/blank/mingl/Cargo.toml @@ -0,0 +1,34 @@ +[package] +name = "mingl" +version = "0.1.0" +edition = "2021" +authors = [ + "Kostiantyn Wandalen ", +] +license = "MIT" +readme = "Readme.md" +documentation = "https://docs.rs/mingl" +repository = "https://github.com/Wandalen/wTools/tree/master/module/core/mingl" +homepage = "https://github.com/Wandalen/wTools/tree/master/module/core/mingl" +description = """ +Draw language. +""" +categories = [ "algorithms", "development-tools" ] +keywords = [ "fundamental", "general-purpose" ] + +[lints] +workspace = true + +[package.metadata.docs.rs] +features = [ "full" ] +all-features = false + +[features] +default = [ "enabled" ] +full = [ "enabled" ] +enabled = [] + +[dependencies] + +[dev-dependencies] +test_tools = { workspace = true } diff --git a/module/blank/mingl/License b/module/blank/mingl/License new file mode 100644 index 0000000000..6d5ef8559f --- /dev/null +++ b/module/blank/mingl/License @@ -0,0 +1,22 @@ +Copyright Kostiantyn W and Out of the Box Systems (c) 2013-2024 + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. diff --git a/module/blank/mingl/Readme.md b/module/blank/mingl/Readme.md new file mode 100644 index 0000000000..3a3390cd6c --- /dev/null +++ b/module/blank/mingl/Readme.md @@ -0,0 +1,33 @@ + + +# Module :: draw_lang +[![experimental](https://raster.shields.io/static/v1?label=stability&message=experimental&color=orange&logoColor=eee)](https://github.com/emersion/stability-badges#experimental) [![rust-status](https://github.com/Wandalen/wTools/actions/workflows/Moduledraw_langPush.yml/badge.svg)](https://github.com/Wandalen/wTools/actions/workflows/Moduledraw_langPush.yml) [![docs.rs](https://img.shields.io/docsrs/draw_lang?color=e3e8f0&logo=docs.rs)](https://docs.rs/draw_lang) [![discord](https://img.shields.io/discord/872391416519737405?color=eee&logo=discord&logoColor=eee&label=ask)](https://discord.gg/m3YfbXpUUY) + +Draw language. + + diff --git a/module/blank/mingl/src/lib.rs b/module/blank/mingl/src/lib.rs new file mode 100644 index 0000000000..8736456366 --- /dev/null +++ b/module/blank/mingl/src/lib.rs @@ -0,0 +1,10 @@ + +#![ doc( html_logo_url = "https://raw.githubusercontent.com/Wandalen/wTools/master/asset/img/logo_v3_trans_square.png" ) ] +#![ doc( html_favicon_url = "https://raw.githubusercontent.com/Wandalen/wTools/alpha/asset/img/logo_v3_trans_square_icon_small_v2.ico" ) ] +#![ doc = include_str!( concat!( env!( "CARGO_MANIFEST_DIR" ), "/", "Readme.md" ) ) ] + +/// Function description. +#[ cfg( feature = "enabled" ) ] +pub fn f1() +{ +} diff --git a/module/blank/mingl/tests/inc/basic_test.rs b/module/blank/mingl/tests/inc/basic_test.rs new file mode 100644 index 0000000000..60c9a81cfb --- /dev/null +++ b/module/blank/mingl/tests/inc/basic_test.rs @@ -0,0 +1,7 @@ +#[ allow( unused_imports ) ] +use super::*; + +#[ test ] +fn basic() +{ +} diff --git a/module/blank/mingl/tests/inc/mod.rs b/module/blank/mingl/tests/inc/mod.rs new file mode 100644 index 0000000000..dde9de6f94 --- /dev/null +++ b/module/blank/mingl/tests/inc/mod.rs @@ -0,0 +1,4 @@ +#[ allow( unused_imports ) ] +use super::*; + +mod basic_test; diff --git a/module/blank/mingl/tests/smoke_test.rs b/module/blank/mingl/tests/smoke_test.rs new file mode 100644 index 0000000000..828e9b016b --- /dev/null +++ b/module/blank/mingl/tests/smoke_test.rs @@ -0,0 +1,14 @@ + + +#[ test ] +fn local_smoke_test() +{ + ::test_tools::smoke_test_for_local_run(); +} + + +#[ test ] +fn published_smoke_test() +{ + ::test_tools::smoke_test_for_published_run(); +} diff --git a/module/blank/mingl/tests/tests.rs b/module/blank/mingl/tests/tests.rs new file mode 100644 index 0000000000..3e3cefe2bd --- /dev/null +++ b/module/blank/mingl/tests/tests.rs @@ -0,0 +1,10 @@ + +include!( "../../../../module/step/meta/src/module/terminal.rs" ); + +#[ allow( unused_imports ) ] +use mingl as the_module; +#[ allow( unused_imports ) ] +use test_tools::exposed::*; + +#[ cfg( feature = "enabled" ) ] +mod inc; diff --git a/module/blank/minmetal/Cargo.toml b/module/blank/minmetal/Cargo.toml new file mode 100644 index 0000000000..72527fb754 --- /dev/null +++ b/module/blank/minmetal/Cargo.toml @@ -0,0 +1,34 @@ +[package] +name = "minmetal" +version = "0.1.0" +edition = "2021" +authors = [ + "Kostiantyn Wandalen ", +] +license = "MIT" +readme = "Readme.md" +documentation = "https://docs.rs/minmetal" +repository = "https://github.com/Wandalen/wTools/tree/master/module/core/minmetal" +homepage = "https://github.com/Wandalen/wTools/tree/master/module/core/minmetal" +description = """ +Draw language. +""" +categories = [ "algorithms", "development-tools" ] +keywords = [ "fundamental", "general-purpose" ] + +[lints] +workspace = true + +[package.metadata.docs.rs] +features = [ "full" ] +all-features = false + +[features] +default = [ "enabled" ] +full = [ "enabled" ] +enabled = [] + +[dependencies] + +[dev-dependencies] +test_tools = { workspace = true } diff --git a/module/blank/minmetal/License b/module/blank/minmetal/License new file mode 100644 index 0000000000..6d5ef8559f --- /dev/null +++ b/module/blank/minmetal/License @@ -0,0 +1,22 @@ +Copyright Kostiantyn W and Out of the Box Systems (c) 2013-2024 + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. diff --git a/module/blank/minmetal/Readme.md b/module/blank/minmetal/Readme.md new file mode 100644 index 0000000000..f701fbedf7 --- /dev/null +++ b/module/blank/minmetal/Readme.md @@ -0,0 +1,33 @@ + + +# Module :: minmetal +[![experimental](https://raster.shields.io/static/v1?label=stability&message=experimental&color=orange&logoColor=eee)](https://github.com/emersion/stability-badges#experimental) [![rust-status](https://github.com/Wandalen/wTools/actions/workflows/ModuleminmetalPush.yml/badge.svg)](https://github.com/Wandalen/wTools/actions/workflows/ModuleminmetalPush.yml) [![docs.rs](https://img.shields.io/docsrs/minmetal?color=e3e8f0&logo=docs.rs)](https://docs.rs/minmetal) [![discord](https://img.shields.io/discord/872391416519737405?color=eee&logo=discord&logoColor=eee&label=ask)](https://discord.gg/m3YfbXpUUY) + +Draw language. + + diff --git a/module/blank/minmetal/src/lib.rs b/module/blank/minmetal/src/lib.rs new file mode 100644 index 0000000000..8736456366 --- /dev/null +++ b/module/blank/minmetal/src/lib.rs @@ -0,0 +1,10 @@ + +#![ doc( html_logo_url = "https://raw.githubusercontent.com/Wandalen/wTools/master/asset/img/logo_v3_trans_square.png" ) ] +#![ doc( html_favicon_url = "https://raw.githubusercontent.com/Wandalen/wTools/alpha/asset/img/logo_v3_trans_square_icon_small_v2.ico" ) ] +#![ doc = include_str!( concat!( env!( "CARGO_MANIFEST_DIR" ), "/", "Readme.md" ) ) ] + +/// Function description. +#[ cfg( feature = "enabled" ) ] +pub fn f1() +{ +} diff --git a/module/blank/minmetal/tests/inc/basic_test.rs b/module/blank/minmetal/tests/inc/basic_test.rs new file mode 100644 index 0000000000..60c9a81cfb --- /dev/null +++ b/module/blank/minmetal/tests/inc/basic_test.rs @@ -0,0 +1,7 @@ +#[ allow( unused_imports ) ] +use super::*; + +#[ test ] +fn basic() +{ +} diff --git a/module/blank/minmetal/tests/inc/mod.rs b/module/blank/minmetal/tests/inc/mod.rs new file mode 100644 index 0000000000..dde9de6f94 --- /dev/null +++ b/module/blank/minmetal/tests/inc/mod.rs @@ -0,0 +1,4 @@ +#[ allow( unused_imports ) ] +use super::*; + +mod basic_test; diff --git a/module/blank/minmetal/tests/smoke_test.rs b/module/blank/minmetal/tests/smoke_test.rs new file mode 100644 index 0000000000..828e9b016b --- /dev/null +++ b/module/blank/minmetal/tests/smoke_test.rs @@ -0,0 +1,14 @@ + + +#[ test ] +fn local_smoke_test() +{ + ::test_tools::smoke_test_for_local_run(); +} + + +#[ test ] +fn published_smoke_test() +{ + ::test_tools::smoke_test_for_published_run(); +} diff --git a/module/blank/minmetal/tests/tests.rs b/module/blank/minmetal/tests/tests.rs new file mode 100644 index 0000000000..f2f68bee4f --- /dev/null +++ b/module/blank/minmetal/tests/tests.rs @@ -0,0 +1,10 @@ + +include!( "../../../../module/step/meta/src/module/terminal.rs" ); + +#[ allow( unused_imports ) ] +use minmetal as the_module; +#[ allow( unused_imports ) ] +use test_tools::exposed::*; + +#[ cfg( feature = "enabled" ) ] +mod inc; diff --git a/module/blank/minopengl/Cargo.toml b/module/blank/minopengl/Cargo.toml new file mode 100644 index 0000000000..8be8629874 --- /dev/null +++ b/module/blank/minopengl/Cargo.toml @@ -0,0 +1,34 @@ +[package] +name = "minopengl" +version = "0.1.0" +edition = "2021" +authors = [ + "Kostiantyn Wandalen ", +] +license = "MIT" +readme = "Readme.md" +documentation = "https://docs.rs/minopengl" +repository = "https://github.com/Wandalen/wTools/tree/master/module/core/minopengl" +homepage = "https://github.com/Wandalen/wTools/tree/master/module/core/minopengl" +description = """ +Draw language. +""" +categories = [ "algorithms", "development-tools" ] +keywords = [ "fundamental", "general-purpose" ] + +[lints] +workspace = true + +[package.metadata.docs.rs] +features = [ "full" ] +all-features = false + +[features] +default = [ "enabled" ] +full = [ "enabled" ] +enabled = [] + +[dependencies] + +[dev-dependencies] +test_tools = { workspace = true } diff --git a/module/blank/minopengl/License b/module/blank/minopengl/License new file mode 100644 index 0000000000..6d5ef8559f --- /dev/null +++ b/module/blank/minopengl/License @@ -0,0 +1,22 @@ +Copyright Kostiantyn W and Out of the Box Systems (c) 2013-2024 + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. diff --git a/module/blank/minopengl/Readme.md b/module/blank/minopengl/Readme.md new file mode 100644 index 0000000000..7d19f733ea --- /dev/null +++ b/module/blank/minopengl/Readme.md @@ -0,0 +1,33 @@ + + +# Module :: minopengl +[![experimental](https://raster.shields.io/static/v1?label=stability&message=experimental&color=orange&logoColor=eee)](https://github.com/emersion/stability-badges#experimental) [![rust-status](https://github.com/Wandalen/wTools/actions/workflows/ModuleminopenglPush.yml/badge.svg)](https://github.com/Wandalen/wTools/actions/workflows/ModuleminopenglPush.yml) [![docs.rs](https://img.shields.io/docsrs/minopengl?color=e3e8f0&logo=docs.rs)](https://docs.rs/minopengl) [![discord](https://img.shields.io/discord/872391416519737405?color=eee&logo=discord&logoColor=eee&label=ask)](https://discord.gg/m3YfbXpUUY) + +Draw language. + + diff --git a/module/blank/minopengl/src/lib.rs b/module/blank/minopengl/src/lib.rs new file mode 100644 index 0000000000..8736456366 --- /dev/null +++ b/module/blank/minopengl/src/lib.rs @@ -0,0 +1,10 @@ + +#![ doc( html_logo_url = "https://raw.githubusercontent.com/Wandalen/wTools/master/asset/img/logo_v3_trans_square.png" ) ] +#![ doc( html_favicon_url = "https://raw.githubusercontent.com/Wandalen/wTools/alpha/asset/img/logo_v3_trans_square_icon_small_v2.ico" ) ] +#![ doc = include_str!( concat!( env!( "CARGO_MANIFEST_DIR" ), "/", "Readme.md" ) ) ] + +/// Function description. +#[ cfg( feature = "enabled" ) ] +pub fn f1() +{ +} diff --git a/module/blank/minopengl/tests/inc/basic_test.rs b/module/blank/minopengl/tests/inc/basic_test.rs new file mode 100644 index 0000000000..60c9a81cfb --- /dev/null +++ b/module/blank/minopengl/tests/inc/basic_test.rs @@ -0,0 +1,7 @@ +#[ allow( unused_imports ) ] +use super::*; + +#[ test ] +fn basic() +{ +} diff --git a/module/blank/minopengl/tests/inc/mod.rs b/module/blank/minopengl/tests/inc/mod.rs new file mode 100644 index 0000000000..dde9de6f94 --- /dev/null +++ b/module/blank/minopengl/tests/inc/mod.rs @@ -0,0 +1,4 @@ +#[ allow( unused_imports ) ] +use super::*; + +mod basic_test; diff --git a/module/blank/minopengl/tests/smoke_test.rs b/module/blank/minopengl/tests/smoke_test.rs new file mode 100644 index 0000000000..828e9b016b --- /dev/null +++ b/module/blank/minopengl/tests/smoke_test.rs @@ -0,0 +1,14 @@ + + +#[ test ] +fn local_smoke_test() +{ + ::test_tools::smoke_test_for_local_run(); +} + + +#[ test ] +fn published_smoke_test() +{ + ::test_tools::smoke_test_for_published_run(); +} diff --git a/module/blank/minopengl/tests/tests.rs b/module/blank/minopengl/tests/tests.rs new file mode 100644 index 0000000000..8a64879a19 --- /dev/null +++ b/module/blank/minopengl/tests/tests.rs @@ -0,0 +1,10 @@ + +include!( "../../../../module/step/meta/src/module/terminal.rs" ); + +#[ allow( unused_imports ) ] +use minopengl as the_module; +#[ allow( unused_imports ) ] +use test_tools::exposed::*; + +#[ cfg( feature = "enabled" ) ] +mod inc; diff --git a/module/blank/minvulkan/Cargo.toml b/module/blank/minvulkan/Cargo.toml new file mode 100644 index 0000000000..69ce9bda5d --- /dev/null +++ b/module/blank/minvulkan/Cargo.toml @@ -0,0 +1,34 @@ +[package] +name = "minvulkan" +version = "0.1.0" +edition = "2021" +authors = [ + "Kostiantyn Wandalen ", +] +license = "MIT" +readme = "Readme.md" +documentation = "https://docs.rs/minvulkan" +repository = "https://github.com/Wandalen/wTools/tree/master/module/core/minvulkan" +homepage = "https://github.com/Wandalen/wTools/tree/master/module/core/minvulkan" +description = """ +Draw language. +""" +categories = [ "algorithms", "development-tools" ] +keywords = [ "fundamental", "general-purpose" ] + +[lints] +workspace = true + +[package.metadata.docs.rs] +features = [ "full" ] +all-features = false + +[features] +default = [ "enabled" ] +full = [ "enabled" ] +enabled = [] + +[dependencies] + +[dev-dependencies] +test_tools = { workspace = true } diff --git a/module/blank/minvulkan/License b/module/blank/minvulkan/License new file mode 100644 index 0000000000..6d5ef8559f --- /dev/null +++ b/module/blank/minvulkan/License @@ -0,0 +1,22 @@ +Copyright Kostiantyn W and Out of the Box Systems (c) 2013-2024 + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. diff --git a/module/blank/minvulkan/Readme.md b/module/blank/minvulkan/Readme.md new file mode 100644 index 0000000000..69b19ad55c --- /dev/null +++ b/module/blank/minvulkan/Readme.md @@ -0,0 +1,33 @@ + + +# Module :: minvulkan +[![experimental](https://raster.shields.io/static/v1?label=stability&message=experimental&color=orange&logoColor=eee)](https://github.com/emersion/stability-badges#experimental) [![rust-status](https://github.com/Wandalen/wTools/actions/workflows/ModuleminvulkanPush.yml/badge.svg)](https://github.com/Wandalen/wTools/actions/workflows/ModuleminvulkanPush.yml) [![docs.rs](https://img.shields.io/docsrs/minvulkan?color=e3e8f0&logo=docs.rs)](https://docs.rs/minvulkan) [![discord](https://img.shields.io/discord/872391416519737405?color=eee&logo=discord&logoColor=eee&label=ask)](https://discord.gg/m3YfbXpUUY) + +Draw language. + + diff --git a/module/blank/minvulkan/src/lib.rs b/module/blank/minvulkan/src/lib.rs new file mode 100644 index 0000000000..8736456366 --- /dev/null +++ b/module/blank/minvulkan/src/lib.rs @@ -0,0 +1,10 @@ + +#![ doc( html_logo_url = "https://raw.githubusercontent.com/Wandalen/wTools/master/asset/img/logo_v3_trans_square.png" ) ] +#![ doc( html_favicon_url = "https://raw.githubusercontent.com/Wandalen/wTools/alpha/asset/img/logo_v3_trans_square_icon_small_v2.ico" ) ] +#![ doc = include_str!( concat!( env!( "CARGO_MANIFEST_DIR" ), "/", "Readme.md" ) ) ] + +/// Function description. +#[ cfg( feature = "enabled" ) ] +pub fn f1() +{ +} diff --git a/module/blank/minvulkan/tests/inc/basic_test.rs b/module/blank/minvulkan/tests/inc/basic_test.rs new file mode 100644 index 0000000000..60c9a81cfb --- /dev/null +++ b/module/blank/minvulkan/tests/inc/basic_test.rs @@ -0,0 +1,7 @@ +#[ allow( unused_imports ) ] +use super::*; + +#[ test ] +fn basic() +{ +} diff --git a/module/blank/minvulkan/tests/inc/mod.rs b/module/blank/minvulkan/tests/inc/mod.rs new file mode 100644 index 0000000000..dde9de6f94 --- /dev/null +++ b/module/blank/minvulkan/tests/inc/mod.rs @@ -0,0 +1,4 @@ +#[ allow( unused_imports ) ] +use super::*; + +mod basic_test; diff --git a/module/blank/minvulkan/tests/smoke_test.rs b/module/blank/minvulkan/tests/smoke_test.rs new file mode 100644 index 0000000000..828e9b016b --- /dev/null +++ b/module/blank/minvulkan/tests/smoke_test.rs @@ -0,0 +1,14 @@ + + +#[ test ] +fn local_smoke_test() +{ + ::test_tools::smoke_test_for_local_run(); +} + + +#[ test ] +fn published_smoke_test() +{ + ::test_tools::smoke_test_for_published_run(); +} diff --git a/module/blank/minvulkan/tests/tests.rs b/module/blank/minvulkan/tests/tests.rs new file mode 100644 index 0000000000..d2d5f19233 --- /dev/null +++ b/module/blank/minvulkan/tests/tests.rs @@ -0,0 +1,10 @@ + +include!( "../../../../module/step/meta/src/module/terminal.rs" ); + +#[ allow( unused_imports ) ] +use minvulkan as the_module; +#[ allow( unused_imports ) ] +use test_tools::exposed::*; + +#[ cfg( feature = "enabled" ) ] +mod inc; diff --git a/module/blank/minwebgl/Cargo.toml b/module/blank/minwebgl/Cargo.toml new file mode 100644 index 0000000000..06d52581fb --- /dev/null +++ b/module/blank/minwebgl/Cargo.toml @@ -0,0 +1,34 @@ +[package] +name = "minwebgl" +version = "0.1.0" +edition = "2021" +authors = [ + "Kostiantyn Wandalen ", +] +license = "MIT" +readme = "Readme.md" +documentation = "https://docs.rs/minwebgl" +repository = "https://github.com/Wandalen/wTools/tree/master/module/core/minwebgl" +homepage = "https://github.com/Wandalen/wTools/tree/master/module/core/minwebgl" +description = """ +Draw language. +""" +categories = [ "algorithms", "development-tools" ] +keywords = [ "fundamental", "general-purpose" ] + +[lints] +workspace = true + +[package.metadata.docs.rs] +features = [ "full" ] +all-features = false + +[features] +default = [ "enabled" ] +full = [ "enabled" ] +enabled = [] + +[dependencies] + +[dev-dependencies] +test_tools = { workspace = true } diff --git a/module/blank/minwebgl/License b/module/blank/minwebgl/License new file mode 100644 index 0000000000..6d5ef8559f --- /dev/null +++ b/module/blank/minwebgl/License @@ -0,0 +1,22 @@ +Copyright Kostiantyn W and Out of the Box Systems (c) 2013-2024 + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. diff --git a/module/blank/minwebgl/Readme.md b/module/blank/minwebgl/Readme.md new file mode 100644 index 0000000000..5b92138916 --- /dev/null +++ b/module/blank/minwebgl/Readme.md @@ -0,0 +1,33 @@ + + +# Module :: minwebgl +[![experimental](https://raster.shields.io/static/v1?label=stability&message=experimental&color=orange&logoColor=eee)](https://github.com/emersion/stability-badges#experimental) [![rust-status](https://github.com/Wandalen/wTools/actions/workflows/ModuleminwebglPush.yml/badge.svg)](https://github.com/Wandalen/wTools/actions/workflows/ModuleminwebglPush.yml) [![docs.rs](https://img.shields.io/docsrs/minwebgl?color=e3e8f0&logo=docs.rs)](https://docs.rs/minwebgl) [![discord](https://img.shields.io/discord/872391416519737405?color=eee&logo=discord&logoColor=eee&label=ask)](https://discord.gg/m3YfbXpUUY) + +Draw language. + + diff --git a/module/blank/minwebgl/src/lib.rs b/module/blank/minwebgl/src/lib.rs new file mode 100644 index 0000000000..8736456366 --- /dev/null +++ b/module/blank/minwebgl/src/lib.rs @@ -0,0 +1,10 @@ + +#![ doc( html_logo_url = "https://raw.githubusercontent.com/Wandalen/wTools/master/asset/img/logo_v3_trans_square.png" ) ] +#![ doc( html_favicon_url = "https://raw.githubusercontent.com/Wandalen/wTools/alpha/asset/img/logo_v3_trans_square_icon_small_v2.ico" ) ] +#![ doc = include_str!( concat!( env!( "CARGO_MANIFEST_DIR" ), "/", "Readme.md" ) ) ] + +/// Function description. +#[ cfg( feature = "enabled" ) ] +pub fn f1() +{ +} diff --git a/module/blank/minwebgl/tests/inc/basic_test.rs b/module/blank/minwebgl/tests/inc/basic_test.rs new file mode 100644 index 0000000000..60c9a81cfb --- /dev/null +++ b/module/blank/minwebgl/tests/inc/basic_test.rs @@ -0,0 +1,7 @@ +#[ allow( unused_imports ) ] +use super::*; + +#[ test ] +fn basic() +{ +} diff --git a/module/blank/minwebgl/tests/inc/mod.rs b/module/blank/minwebgl/tests/inc/mod.rs new file mode 100644 index 0000000000..dde9de6f94 --- /dev/null +++ b/module/blank/minwebgl/tests/inc/mod.rs @@ -0,0 +1,4 @@ +#[ allow( unused_imports ) ] +use super::*; + +mod basic_test; diff --git a/module/blank/minwebgl/tests/smoke_test.rs b/module/blank/minwebgl/tests/smoke_test.rs new file mode 100644 index 0000000000..828e9b016b --- /dev/null +++ b/module/blank/minwebgl/tests/smoke_test.rs @@ -0,0 +1,14 @@ + + +#[ test ] +fn local_smoke_test() +{ + ::test_tools::smoke_test_for_local_run(); +} + + +#[ test ] +fn published_smoke_test() +{ + ::test_tools::smoke_test_for_published_run(); +} diff --git a/module/blank/minwebgl/tests/tests.rs b/module/blank/minwebgl/tests/tests.rs new file mode 100644 index 0000000000..f830fcaa61 --- /dev/null +++ b/module/blank/minwebgl/tests/tests.rs @@ -0,0 +1,10 @@ + +include!( "../../../../module/step/meta/src/module/terminal.rs" ); + +#[ allow( unused_imports ) ] +use minwebgl as the_module; +#[ allow( unused_imports ) ] +use test_tools::exposed::*; + +#[ cfg( feature = "enabled" ) ] +mod inc; diff --git a/module/blank/minwebgpu/Cargo.toml b/module/blank/minwebgpu/Cargo.toml new file mode 100644 index 0000000000..c543c5be36 --- /dev/null +++ b/module/blank/minwebgpu/Cargo.toml @@ -0,0 +1,34 @@ +[package] +name = "minwebgpu" +version = "0.1.0" +edition = "2021" +authors = [ + "Kostiantyn Wandalen ", +] +license = "MIT" +readme = "Readme.md" +documentation = "https://docs.rs/minwebgpu" +repository = "https://github.com/Wandalen/wTools/tree/master/module/core/minwebgpu" +homepage = "https://github.com/Wandalen/wTools/tree/master/module/core/minwebgpu" +description = """ +Draw language. +""" +categories = [ "algorithms", "development-tools" ] +keywords = [ "fundamental", "general-purpose" ] + +[lints] +workspace = true + +[package.metadata.docs.rs] +features = [ "full" ] +all-features = false + +[features] +default = [ "enabled" ] +full = [ "enabled" ] +enabled = [] + +[dependencies] + +[dev-dependencies] +test_tools = { workspace = true } diff --git a/module/blank/minwebgpu/License b/module/blank/minwebgpu/License new file mode 100644 index 0000000000..6d5ef8559f --- /dev/null +++ b/module/blank/minwebgpu/License @@ -0,0 +1,22 @@ +Copyright Kostiantyn W and Out of the Box Systems (c) 2013-2024 + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. diff --git a/module/blank/minwebgpu/Readme.md b/module/blank/minwebgpu/Readme.md new file mode 100644 index 0000000000..259dee0ab2 --- /dev/null +++ b/module/blank/minwebgpu/Readme.md @@ -0,0 +1,33 @@ + + +# Module :: minwebgpu +[![experimental](https://raster.shields.io/static/v1?label=stability&message=experimental&color=orange&logoColor=eee)](https://github.com/emersion/stability-badges#experimental) [![rust-status](https://github.com/Wandalen/wTools/actions/workflows/ModuleminwebgpuPush.yml/badge.svg)](https://github.com/Wandalen/wTools/actions/workflows/ModuleminwebgpuPush.yml) [![docs.rs](https://img.shields.io/docsrs/minwebgpu?color=e3e8f0&logo=docs.rs)](https://docs.rs/minwebgpu) [![discord](https://img.shields.io/discord/872391416519737405?color=eee&logo=discord&logoColor=eee&label=ask)](https://discord.gg/m3YfbXpUUY) + +Draw language. + + diff --git a/module/blank/minwebgpu/src/lib.rs b/module/blank/minwebgpu/src/lib.rs new file mode 100644 index 0000000000..8736456366 --- /dev/null +++ b/module/blank/minwebgpu/src/lib.rs @@ -0,0 +1,10 @@ + +#![ doc( html_logo_url = "https://raw.githubusercontent.com/Wandalen/wTools/master/asset/img/logo_v3_trans_square.png" ) ] +#![ doc( html_favicon_url = "https://raw.githubusercontent.com/Wandalen/wTools/alpha/asset/img/logo_v3_trans_square_icon_small_v2.ico" ) ] +#![ doc = include_str!( concat!( env!( "CARGO_MANIFEST_DIR" ), "/", "Readme.md" ) ) ] + +/// Function description. +#[ cfg( feature = "enabled" ) ] +pub fn f1() +{ +} diff --git a/module/blank/minwebgpu/tests/inc/basic_test.rs b/module/blank/minwebgpu/tests/inc/basic_test.rs new file mode 100644 index 0000000000..60c9a81cfb --- /dev/null +++ b/module/blank/minwebgpu/tests/inc/basic_test.rs @@ -0,0 +1,7 @@ +#[ allow( unused_imports ) ] +use super::*; + +#[ test ] +fn basic() +{ +} diff --git a/module/blank/minwebgpu/tests/inc/mod.rs b/module/blank/minwebgpu/tests/inc/mod.rs new file mode 100644 index 0000000000..dde9de6f94 --- /dev/null +++ b/module/blank/minwebgpu/tests/inc/mod.rs @@ -0,0 +1,4 @@ +#[ allow( unused_imports ) ] +use super::*; + +mod basic_test; diff --git a/module/blank/minwebgpu/tests/smoke_test.rs b/module/blank/minwebgpu/tests/smoke_test.rs new file mode 100644 index 0000000000..828e9b016b --- /dev/null +++ b/module/blank/minwebgpu/tests/smoke_test.rs @@ -0,0 +1,14 @@ + + +#[ test ] +fn local_smoke_test() +{ + ::test_tools::smoke_test_for_local_run(); +} + + +#[ test ] +fn published_smoke_test() +{ + ::test_tools::smoke_test_for_published_run(); +} diff --git a/module/blank/minwebgpu/tests/tests.rs b/module/blank/minwebgpu/tests/tests.rs new file mode 100644 index 0000000000..849473f639 --- /dev/null +++ b/module/blank/minwebgpu/tests/tests.rs @@ -0,0 +1,10 @@ + +include!( "../../../../module/step/meta/src/module/terminal.rs" ); + +#[ allow( unused_imports ) ] +use minwebgpu as the_module; +#[ allow( unused_imports ) ] +use test_tools::exposed::*; + +#[ cfg( feature = "enabled" ) ] +mod inc; diff --git a/module/blank/minwgpu/Cargo.toml b/module/blank/minwgpu/Cargo.toml new file mode 100644 index 0000000000..25841190ba --- /dev/null +++ b/module/blank/minwgpu/Cargo.toml @@ -0,0 +1,34 @@ +[package] +name = "minwgpu" +version = "0.1.0" +edition = "2021" +authors = [ + "Kostiantyn Wandalen ", +] +license = "MIT" +readme = "Readme.md" +documentation = "https://docs.rs/minwgpu" +repository = "https://github.com/Wandalen/wTools/tree/master/module/core/minwgpu" +homepage = "https://github.com/Wandalen/wTools/tree/master/module/core/minwgpu" +description = """ +Draw language. +""" +categories = [ "algorithms", "development-tools" ] +keywords = [ "fundamental", "general-purpose" ] + +[lints] +workspace = true + +[package.metadata.docs.rs] +features = [ "full" ] +all-features = false + +[features] +default = [ "enabled" ] +full = [ "enabled" ] +enabled = [] + +[dependencies] + +[dev-dependencies] +test_tools = { workspace = true } diff --git a/module/blank/minwgpu/License b/module/blank/minwgpu/License new file mode 100644 index 0000000000..6d5ef8559f --- /dev/null +++ b/module/blank/minwgpu/License @@ -0,0 +1,22 @@ +Copyright Kostiantyn W and Out of the Box Systems (c) 2013-2024 + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. diff --git a/module/blank/minwgpu/Readme.md b/module/blank/minwgpu/Readme.md new file mode 100644 index 0000000000..1c8ca98d8a --- /dev/null +++ b/module/blank/minwgpu/Readme.md @@ -0,0 +1,33 @@ + + +# Module :: minwgpu +[![experimental](https://raster.shields.io/static/v1?label=stability&message=experimental&color=orange&logoColor=eee)](https://github.com/emersion/stability-badges#experimental) [![rust-status](https://github.com/Wandalen/wTools/actions/workflows/ModuleminwgpuPush.yml/badge.svg)](https://github.com/Wandalen/wTools/actions/workflows/ModuleminwgpuPush.yml) [![docs.rs](https://img.shields.io/docsrs/minwgpu?color=e3e8f0&logo=docs.rs)](https://docs.rs/minwgpu) [![discord](https://img.shields.io/discord/872391416519737405?color=eee&logo=discord&logoColor=eee&label=ask)](https://discord.gg/m3YfbXpUUY) + +Draw language. + + diff --git a/module/blank/minwgpu/src/lib.rs b/module/blank/minwgpu/src/lib.rs new file mode 100644 index 0000000000..8736456366 --- /dev/null +++ b/module/blank/minwgpu/src/lib.rs @@ -0,0 +1,10 @@ + +#![ doc( html_logo_url = "https://raw.githubusercontent.com/Wandalen/wTools/master/asset/img/logo_v3_trans_square.png" ) ] +#![ doc( html_favicon_url = "https://raw.githubusercontent.com/Wandalen/wTools/alpha/asset/img/logo_v3_trans_square_icon_small_v2.ico" ) ] +#![ doc = include_str!( concat!( env!( "CARGO_MANIFEST_DIR" ), "/", "Readme.md" ) ) ] + +/// Function description. +#[ cfg( feature = "enabled" ) ] +pub fn f1() +{ +} diff --git a/module/blank/minwgpu/tests/inc/basic_test.rs b/module/blank/minwgpu/tests/inc/basic_test.rs new file mode 100644 index 0000000000..60c9a81cfb --- /dev/null +++ b/module/blank/minwgpu/tests/inc/basic_test.rs @@ -0,0 +1,7 @@ +#[ allow( unused_imports ) ] +use super::*; + +#[ test ] +fn basic() +{ +} diff --git a/module/blank/minwgpu/tests/inc/mod.rs b/module/blank/minwgpu/tests/inc/mod.rs new file mode 100644 index 0000000000..dde9de6f94 --- /dev/null +++ b/module/blank/minwgpu/tests/inc/mod.rs @@ -0,0 +1,4 @@ +#[ allow( unused_imports ) ] +use super::*; + +mod basic_test; diff --git a/module/blank/minwgpu/tests/smoke_test.rs b/module/blank/minwgpu/tests/smoke_test.rs new file mode 100644 index 0000000000..828e9b016b --- /dev/null +++ b/module/blank/minwgpu/tests/smoke_test.rs @@ -0,0 +1,14 @@ + + +#[ test ] +fn local_smoke_test() +{ + ::test_tools::smoke_test_for_local_run(); +} + + +#[ test ] +fn published_smoke_test() +{ + ::test_tools::smoke_test_for_published_run(); +} diff --git a/module/blank/minwgpu/tests/tests.rs b/module/blank/minwgpu/tests/tests.rs new file mode 100644 index 0000000000..9bcb27960e --- /dev/null +++ b/module/blank/minwgpu/tests/tests.rs @@ -0,0 +1,10 @@ + +include!( "../../../../module/step/meta/src/module/terminal.rs" ); + +#[ allow( unused_imports ) ] +use minwgpu as the_module; +#[ allow( unused_imports ) ] +use test_tools::exposed::*; + +#[ cfg( feature = "enabled" ) ] +mod inc; diff --git a/module/core/meta_tools/src/lib.rs b/module/core/meta_tools/src/lib.rs index cd49c9841e..352f7e0f3b 100644 --- a/module/core/meta_tools/src/lib.rs +++ b/module/core/meta_tools/src/lib.rs @@ -29,12 +29,15 @@ pub mod dependency } +mod private {} + // // qqq : meta interface should be optional dependancy. please fix writing equivalent code manually #[ cfg( feature = "enabled" ) ] mod_interface::mod_interface! { + // #![ debug ] layer meta; diff --git a/module/core/mod_interface/Cargo.toml b/module/core/mod_interface/Cargo.toml index 438141b1e3..4c7f49cca8 100644 --- a/module/core/mod_interface/Cargo.toml +++ b/module/core/mod_interface/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "mod_interface" -version = "0.24.0" +version = "0.25.0" edition = "2021" authors = [ "Kostiantyn Wandalen ", diff --git a/module/core/mod_interface/Readme.md b/module/core/mod_interface/Readme.md index 72573cc881..47115efb68 100644 --- a/module/core/mod_interface/Readme.md +++ b/module/core/mod_interface/Readme.md @@ -5,109 +5,164 @@ [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) [![rust-status](https://github.com/Wandalen/wTools/actions/workflows/module_mod_interface_push.yml/badge.svg)](https://github.com/Wandalen/wTools/actions/workflows/module_mod_interface_push.yml) [![docs.rs](https://img.shields.io/docsrs/mod_interface?color=e3e8f0&logo=docs.rs)](https://docs.rs/mod_interface) [![discord](https://img.shields.io/discord/872391416519737405?color=eee&logo=discord&logoColor=eee&label=ask)](https://discord.gg/m3YfbXpUUY) -Protocol of modularity unifying interface of a module and introducing layers. +Protocol of modularity unifying interface. -### Basic use-case +### Problem Solved -Library file with code `inner.rs`: +The `mod_interface` crate provides a structured approach to modularity, addressing two key challenges in software development: -```rust ignore -mod private +1. **Meaningful Namespace Structuring**: The crate enables developers to organize program entities into meaningful namespaces ( read modules ) without additional development overhead. This is achieved through a set of auto-importing rules and a flexible inversion of control mechanism, allowing parent namespaces to delegate control over its items to child namespaces. This approach ensures that each namespace is self-contained and meaningful, promoting better organization and modularity. + +2. **Enhanced Readability and Tooling Independence**: By requiring a `mod private` section that lists all items ( read functions, structures, traits, types ) the `mod_interface` macro encourages developers to create a concise list of items at the beginning or end of a file. This improves readability, encourages refactoring, and reduces cognitive load by providing a clear, high-level grouping of items. Code tooling is not always reliable and can sometimes be counterproductive by automating tasks that should be done manually to achieve more concise code. While code tooling like `rust_analyzer` are useful, this approach minimizes reliance on them, making the program's structure easier to understand and manage. + +While some may argue that inversion of control over namespaces may not always achieve the desired outcome, and code tooling can be sufficient, the `mod_interface` crate offers a cathartic solution for designing complex systems where tooling and triditional structuring often fall short. By promoting a clear and organized structure, it helps developers grasp the semantics of their programs more holistically. + +### Example : Trivial + +This example demonstrates how to use the `mod_interface` crate to organize a Rust program into structured namespaces. The code is divided into a library file (`child.rs`) and a main function. The library file defines a module with private functions and uses the `mod_interface` macro to specify which functions should be exposed in different namespaces. The main function then tests the visibility and accessibility of these functions. + +```rust +use mod_interface::mod_interface; + +// Define a module named `child`. +mod child { - /// Routine of inner module. - pub fn inner_is() -> bool + + // Define a private namespace for all its items. + mod private { - true + /// Only my thing. + pub fn my_thing() -> bool { true } + /// Parent module should also has this thing. + pub fn orphan_thing() -> bool { true } + /// This thing should be exposed. + pub fn exposed_thing() -> bool { true } + /// This thing should be in prelude. + pub fn prelude_thing() -> bool { true } } -} -// + // + + crate::mod_interface! + { + own use my_thing; + orphan use orphan_thing; + exposed use exposed_thing; + prelude use prelude_thing; + } -mod_interface::mod_interface! -{ - prelude use inner_is; } -``` -Main file that generates modules and namespaces `main.rs` : +// Priave namespaces is necessary. +mod private {} -```rust ignore -use mod_interface::mod_interface; +crate::mod_interface! +{ + /// Inner. + use super::child; +} -// fn main() { - assert_eq!( prelude::inner_is(), inner::prelude::inner_is() ); -} -// + assert!( child::prelude_thing(), "prelude thing of child is there" ); + assert!( prelude_thing(), "and here" ); + assert!( own::prelude_thing(), "and here" ); + assert!( orphan::prelude_thing(), "and here" ); + assert!( exposed::prelude_thing(), "and here" ); + assert!( prelude::prelude_thing(), "and here" ); + + assert!( child::exposed_thing(), "exposed thing of child is there" ); + assert!( exposed_thing(), "and here" ); + assert!( own::exposed_thing(), "and here" ); + assert!( orphan::exposed_thing(), "and here" ); + assert!( exposed::exposed_thing(), "and here" ); + // assert!( prelude::exposed_thing(), "but not here" ); + + assert!( child::orphan_thing(), "orphan thing of child is there" ); + assert!( orphan_thing(), "orphan thing of child is here" ); + assert!( own::orphan_thing(), "and here" ); + // assert!( orphan::orphan_thing(), "but not here" ); + // assert!( exposed::orphan_thing(), "and not here" ); + // assert!( prelude::orphan_thing(), "and not here" ); + + assert!( child::my_thing(), "own thing of child is only there" ); + // assert!( my_thing(), "and not here" ); + // assert!( own::my_thing(), "and not here" ); + // assert!( orphan::my_thing(), "and not here" ); + // assert!( exposed::my_thing(), "and not here" ); + // assert!( prelude::my_thing(), "and not here" ); -mod_interface::mod_interface! -{ - /// Inner. - layer inner; } + ``` -It generates code : +
+The code above will be expanded to this ```rust use mod_interface::mod_interface; -// - -fn main() -{ - assert_eq!( prelude::inner_is(), inner::prelude::inner_is() ); -} - -// - -/// Inner. -pub mod inner +// Define a module named `child` +pub mod child { + // Define a private namespace for all its items. mod private { - /// Routine of inner module. - pub fn inner_is() -> bool { true } + /// Only my thing. + pub fn my_thing() -> bool { true } + /// Parent module should also has this thing. + pub fn orphan_thing() -> bool { true } + /// This thing should be exposed. + pub fn exposed_thing() -> bool { true } + /// This thing should be in prelude. + pub fn prelude_thing() -> bool { true } } + pub use own::*; + /// Own namespace of the module. pub mod own { - pub use orphan::*; + pub use super::orphan::*; + pub use super::private::my_thing; } - pub use own::*; /// Orphan namespace of the module. pub mod orphan { - pub use exposed::*; + pub use super::exposed::*; + pub use super::private::orphan_thing; } /// Exposed namespace of the module. pub mod exposed { - pub use prelude::*; + pub use super::prelude::*; + pub use super::private::exposed_thing; } /// Prelude to use essentials: `use my_module::prelude::*`. pub mod prelude { - pub use private::inner_is; + pub use super::private::prelude_thing; } } +// Priave namespaces is necessary. +mod private {} + +pub use own::*; + /// Own namespace of the module. #[ allow( unused_imports ) ] pub mod own { use super::*; pub use orphan::*; - pub use super::inner::orphan::*; + pub use super::child::orphan::*; } -pub use own::*; /// Orphan namespace of the module. #[ allow( unused_imports ) ] @@ -123,7 +178,7 @@ pub mod exposed { use super::*; pub use prelude::*; - pub use super::inner::exposed::*; + pub use super::child::exposed::*; } /// Prelude to use essentials: `use my_module::prelude::*`. @@ -131,10 +186,48 @@ pub mod exposed pub mod prelude { use super::*; - pub use super::inner::prelude::*; + pub use super::child::prelude::*; +} + +// + +fn main() +{ + + assert!( child::prelude_thing(), "prelude thing of child is there" ); + assert!( prelude_thing(), "and here" ); + assert!( own::prelude_thing(), "and here" ); + assert!( orphan::prelude_thing(), "and here" ); + assert!( exposed::prelude_thing(), "and here" ); + assert!( prelude::prelude_thing(), "and here" ); + + assert!( child::exposed_thing(), "exposed thing of child is there" ); + assert!( exposed_thing(), "and here" ); + assert!( own::exposed_thing(), "and here" ); + assert!( orphan::exposed_thing(), "and here" ); + assert!( exposed::exposed_thing(), "and here" ); + // assert!( prelude::exposed_thing(), "but not here" ); + + assert!( child::orphan_thing(), "orphan thing of child is there" ); + assert!( orphan_thing(), "orphan thing of child is here" ); + assert!( own::orphan_thing(), "and here" ); + // assert!( orphan::orphan_thing(), "but not here" ); + // assert!( exposed::orphan_thing(), "and not here" ); + // assert!( prelude::orphan_thing(), "and not here" ); + + assert!( child::my_thing(), "own thing of child is only there" ); + // assert!( my_thing(), "and not here" ); + // assert!( own::my_thing(), "and not here" ); + // assert!( orphan::my_thing(), "and not here" ); + // assert!( exposed::my_thing(), "and not here" ); + // assert!( prelude::my_thing(), "and not here" ); + } + ``` +
+ ### Debugging To debug module interface use directive `#![ debug ]` in macro `mod_interface`. Let's update the main file of the example : @@ -144,7 +237,7 @@ mod_interface::mod_interface! { #![ debug ] /// Inner. - layer inner; + layer child; } ``` diff --git a/module/core/mod_interface/examples/mod_interface_debug/Readme.md b/module/core/mod_interface/examples/mod_interface_debug/Readme.md index 6cc31966fb..d57023c6a5 100644 --- a/module/core/mod_interface/examples/mod_interface_debug/Readme.md +++ b/module/core/mod_interface/examples/mod_interface_debug/Readme.md @@ -6,8 +6,10 @@ A sample demonstrates basic usage of macro `mod_interface`. -In file `inner.rs` demonstrated how to generate module interface from namespace `private` and its public routine. +In file `child.rs` demonstrated how to generate module interface from namespace `private` and its public routine. In file `main.rs` demonstrated how to generate module interface from layer ( file with full module interface ). The directive `#![ debug ]` in declaration of macro `mod_interface` allow to show generated module interface as the standard output in compile time. + + \ No newline at end of file diff --git a/module/core/mod_interface/examples/mod_interface_debug/src/inner.rs b/module/core/mod_interface/examples/mod_interface_debug/src/child.rs similarity index 80% rename from module/core/mod_interface/examples/mod_interface_debug/src/inner.rs rename to module/core/mod_interface/examples/mod_interface_debug/src/child.rs index cc62b2c56b..dd734212d9 100644 --- a/module/core/mod_interface/examples/mod_interface_debug/src/inner.rs +++ b/module/core/mod_interface/examples/mod_interface_debug/src/child.rs @@ -1,6 +1,6 @@ mod private { - /// Routine of inner module. + /// Routine of child module. pub fn inner_is() -> bool { true diff --git a/module/core/mod_interface/examples/mod_interface_debug/src/main.rs b/module/core/mod_interface/examples/mod_interface_debug/src/main.rs index e316b7acd6..985f4f49f7 100644 --- a/module/core/mod_interface/examples/mod_interface_debug/src/main.rs +++ b/module/core/mod_interface/examples/mod_interface_debug/src/main.rs @@ -3,16 +3,18 @@ use mod_interface::mod_interface; // -fn main() +mod private {} +mod_interface! { - assert_eq!( prelude::inner_is(), inner::prelude::inner_is() ); + // Uncomment to see expanded code. + // #![ debug ] + /// Child. + layer child; } // -mod_interface! +fn main() { - #![ debug ] - /// Inner. - layer inner; + assert_eq!( prelude::inner_is(), child::prelude::inner_is() ); } diff --git a/module/core/mod_interface/examples/mod_interface_trivial/src/child.rs b/module/core/mod_interface/examples/mod_interface_trivial/src/child.rs new file mode 100644 index 0000000000..4ea0121559 --- /dev/null +++ b/module/core/mod_interface/examples/mod_interface_trivial/src/child.rs @@ -0,0 +1,23 @@ + +// Define a private namespace for all its items. +mod private +{ + /// Only my thing. + pub fn my_thing() -> bool { true } + /// Parent module should also has this thing. + pub fn orphan_thing() -> bool { true } + /// This thing should be exposed. + pub fn exposed_thing() -> bool { true } + /// This thing should be in prelude. + pub fn prelude_thing() -> bool { true } +} + +// + +crate::mod_interface! +{ + own use my_thing; + orphan use orphan_thing; + exposed use exposed_thing; + prelude use prelude_thing; +} diff --git a/module/core/mod_interface/examples/mod_interface_trivial/src/inner.rs b/module/core/mod_interface/examples/mod_interface_trivial/src/inner.rs deleted file mode 100644 index cc62b2c56b..0000000000 --- a/module/core/mod_interface/examples/mod_interface_trivial/src/inner.rs +++ /dev/null @@ -1,15 +0,0 @@ -mod private -{ - /// Routine of inner module. - pub fn inner_is() -> bool - { - true - } -} - -// - -mod_interface::mod_interface! -{ - prelude use inner_is; -} diff --git a/module/core/mod_interface/examples/mod_interface_trivial/src/main.rs b/module/core/mod_interface/examples/mod_interface_trivial/src/main.rs index 03b8905594..420a6c9fb4 100644 --- a/module/core/mod_interface/examples/mod_interface_trivial/src/main.rs +++ b/module/core/mod_interface/examples/mod_interface_trivial/src/main.rs @@ -1,21 +1,49 @@ -//! qqq : write proper descriptionuse mod_interface::mod_interface; - -// +//! This example demonstrates how to use the `mod_interface` crate to organize a Rust program into structured namespaces. The code is divided into a library file (`child.rs`) and a main function. The library file defines a module with private functions and uses the `mod_interface` macro to specify which functions should be exposed in different namespaces. The main function then tests the visibility and accessibility of these functions. use mod_interface::mod_interface; -fn main() -{ - assert_eq!( prelude::inner_is(), prelude::inner_is() ); -} +/// Children. +mod child; -// +// Priave namespaces is necessary. +mod private {} -mod_interface! +crate::mod_interface! { /// Inner. - layer inner; + use super::child; } -// qqq : rewrite sample -/* aaa : Dmytro : sample with layer */ + +fn main() +{ + + assert!( child::prelude_thing(), "prelude thing of child is there" ); + assert!( prelude_thing(), "and here" ); + assert!( own::prelude_thing(), "and here" ); + assert!( orphan::prelude_thing(), "and here" ); + assert!( exposed::prelude_thing(), "and here" ); + assert!( prelude::prelude_thing(), "and here" ); + + assert!( child::exposed_thing(), "exposed thing of child is there" ); + assert!( exposed_thing(), "and here" ); + assert!( own::exposed_thing(), "and here" ); + assert!( orphan::exposed_thing(), "and here" ); + assert!( exposed::exposed_thing(), "and here" ); + // assert!( prelude::exposed_thing(), "but not here" ); + + assert!( child::orphan_thing(), "orphan thing of child is there" ); + assert!( orphan_thing(), "orphan thing of child is here" ); + assert!( own::orphan_thing(), "and here" ); + // assert!( orphan::orphan_thing(), "but not here" ); + // assert!( exposed::orphan_thing(), "and not here" ); + // assert!( prelude::orphan_thing(), "and not here" ); + + assert!( child::my_thing(), "own thing of child is only there" ); + // assert!( my_thing(), "and not here" ); + // assert!( own::my_thing(), "and not here" ); + // assert!( orphan::my_thing(), "and not here" ); + // assert!( exposed::my_thing(), "and not here" ); + // assert!( prelude::my_thing(), "and not here" ); + +} diff --git a/module/core/mod_interface/tests/inc/derive/attr_debug/mod.rs b/module/core/mod_interface/tests/inc/derive/attr_debug/mod.rs index 939ebcdfb3..57b54aff39 100644 --- a/module/core/mod_interface/tests/inc/derive/attr_debug/mod.rs +++ b/module/core/mod_interface/tests/inc/derive/attr_debug/mod.rs @@ -1,9 +1,11 @@ use super::*; +mod private {} + mod_interface! { - #![ debug ] + // #![ debug ] /// layer_a layer layer_a; diff --git a/module/core/mod_interface/tests/inc/derive/micro_modules/mod.rs b/module/core/mod_interface/tests/inc/derive/micro_modules/mod.rs index 09c94a139e..80d4c7218a 100644 --- a/module/core/mod_interface/tests/inc/derive/micro_modules/mod.rs +++ b/module/core/mod_interface/tests/inc/derive/micro_modules/mod.rs @@ -8,6 +8,7 @@ mod private mod_interface! { + // #![ debug ] /// mod_own own mod mod_own; diff --git a/module/core/mod_interface/tests/inc/derive/micro_modules/mod_protected.rs b/module/core/mod_interface/tests/inc/derive/micro_modules/mod_own.rs similarity index 100% rename from module/core/mod_interface/tests/inc/derive/micro_modules/mod_protected.rs rename to module/core/mod_interface/tests/inc/derive/micro_modules/mod_own.rs diff --git a/module/core/mod_interface/tests/inc/manual/micro_modules/mod_protected.rs b/module/core/mod_interface/tests/inc/derive/micro_modules_glob/child.rs similarity index 100% rename from module/core/mod_interface/tests/inc/manual/micro_modules/mod_protected.rs rename to module/core/mod_interface/tests/inc/derive/micro_modules_glob/child.rs diff --git a/module/core/mod_interface/tests/inc/derive/micro_modules_glob/mod.rs b/module/core/mod_interface/tests/inc/derive/micro_modules_glob/mod.rs new file mode 100644 index 0000000000..40ccb61f64 --- /dev/null +++ b/module/core/mod_interface/tests/inc/derive/micro_modules_glob/mod.rs @@ -0,0 +1,28 @@ + +// use super::*; + +/// Internal namespace. +mod private +{ + pub struct Struct1; + pub struct Struct2; +} + +// + +crate::mod_interface! +{ + own use + { + * + }; +} + +// + +#[ test ] +fn basic() +{ + let _s1 = Struct1; + let _s2 = Struct2; +} diff --git a/module/core/mod_interface/tests/inc/derive/micro_modules_two/mod_protected1.rs b/module/core/mod_interface/tests/inc/derive/micro_modules_two/mod_own1.rs similarity index 100% rename from module/core/mod_interface/tests/inc/derive/micro_modules_two/mod_protected1.rs rename to module/core/mod_interface/tests/inc/derive/micro_modules_two/mod_own1.rs diff --git a/module/core/mod_interface/tests/inc/derive/micro_modules_two/mod_protected2.rs b/module/core/mod_interface/tests/inc/derive/micro_modules_two/mod_own2.rs similarity index 100% rename from module/core/mod_interface/tests/inc/derive/micro_modules_two/mod_protected2.rs rename to module/core/mod_interface/tests/inc/derive/micro_modules_two/mod_own2.rs diff --git a/module/core/mod_interface/tests/inc/derive/micro_modules_two_joined/mod_protected1.rs b/module/core/mod_interface/tests/inc/derive/micro_modules_two_joined/mod_own1.rs similarity index 100% rename from module/core/mod_interface/tests/inc/derive/micro_modules_two_joined/mod_protected1.rs rename to module/core/mod_interface/tests/inc/derive/micro_modules_two_joined/mod_own1.rs diff --git a/module/core/mod_interface/tests/inc/derive/micro_modules_two_joined/mod_protected2.rs b/module/core/mod_interface/tests/inc/derive/micro_modules_two_joined/mod_own2.rs similarity index 100% rename from module/core/mod_interface/tests/inc/derive/micro_modules_two_joined/mod_protected2.rs rename to module/core/mod_interface/tests/inc/derive/micro_modules_two_joined/mod_own2.rs diff --git a/module/core/mod_interface/tests/inc/derive/reuse_basic/child.rs b/module/core/mod_interface/tests/inc/derive/reuse_basic/child.rs new file mode 100644 index 0000000000..d94a40b5bd --- /dev/null +++ b/module/core/mod_interface/tests/inc/derive/reuse_basic/child.rs @@ -0,0 +1,15 @@ +mod private +{ + pub struct Own; + pub struct Orphan; + pub struct Exposed; + pub struct Prelude; +} + +crate::mod_interface! +{ + own use Own; + orphan use Orphan; + exposed use Exposed; + prelude use Prelude; +} diff --git a/module/core/mod_interface/tests/inc/derive/reuse_basic/mod.rs b/module/core/mod_interface/tests/inc/derive/reuse_basic/mod.rs new file mode 100644 index 0000000000..55daeb2a1c --- /dev/null +++ b/module/core/mod_interface/tests/inc/derive/reuse_basic/mod.rs @@ -0,0 +1,34 @@ + +// use super::*; + +/// Internal namespace. +mod private +{ +} + +mod child; + +// + +crate::mod_interface! +{ + reuse child; +} + +// + +#[ test ] +fn basic() +{ + + let _ = child::Own; + let _ = child::Orphan; + let _ = child::Exposed; + let _ = child::Prelude; + + let _ = Own; + let _ = Orphan; + let _ = Exposed; + let _ = Prelude; + +} diff --git a/module/core/mod_interface/tests/inc/derive/use_as/derive.rs b/module/core/mod_interface/tests/inc/derive/use_as/derive.rs index 4c452702b0..a70260fc3c 100644 --- a/module/core/mod_interface/tests/inc/derive/use_as/derive.rs +++ b/module/core/mod_interface/tests/inc/derive/use_as/derive.rs @@ -4,6 +4,8 @@ use super::*; /// Layer X pub mod layer_x; +mod private {} + mod_interface! { // #![ debug ] @@ -13,7 +15,7 @@ mod_interface! // /// layer_a // pub use super::layer_x as layer_a; - // xxx : make that working + // zzz : make that working } diff --git a/module/core/mod_interface/tests/inc/derive/use_as/manual_only.rs b/module/core/mod_interface/tests/inc/derive/use_as/manual_only.rs index f6fcb2f162..9ce347e8fb 100644 --- a/module/core/mod_interface/tests/inc/derive/use_as/manual_only.rs +++ b/module/core/mod_interface/tests/inc/derive/use_as/manual_only.rs @@ -3,7 +3,7 @@ use layer_x as layer_a; #[doc(inline)] #[allow(unused_imports)] -pub use protected :: * ; +pub use own :: * ; #[doc = r" Own namespace of the module."] #[ allow( unused_imports ) ] diff --git a/module/core/mod_interface/tests/inc/derive/use_basic/mod.rs b/module/core/mod_interface/tests/inc/derive/use_basic/mod.rs index 3cfbb3ad53..fe3862d5b3 100644 --- a/module/core/mod_interface/tests/inc/derive/use_basic/mod.rs +++ b/module/core/mod_interface/tests/inc/derive/use_basic/mod.rs @@ -4,6 +4,8 @@ use super::*; mod layer_a; mod layer_b; +mod private {} + mod_interface! { diff --git a/module/core/mod_interface/tests/inc/derive/use_layer/mod.rs b/module/core/mod_interface/tests/inc/derive/use_layer/mod.rs index 17247d2d07..a7f1790c60 100644 --- a/module/core/mod_interface/tests/inc/derive/use_layer/mod.rs +++ b/module/core/mod_interface/tests/inc/derive/use_layer/mod.rs @@ -6,11 +6,6 @@ mod tools pub use super::super::*; } -// /// Private namespace of the module. -// mod private -// { -// } - mod layer_a; /// SuperStruct1. @@ -19,6 +14,8 @@ pub struct SuperStruct1 { } +mod private {} + mod_interface! { diff --git a/module/core/mod_interface/tests/inc/manual/micro_modules/mod_own.rs b/module/core/mod_interface/tests/inc/manual/micro_modules/mod_own.rs new file mode 100644 index 0000000000..a6619cc0c4 --- /dev/null +++ b/module/core/mod_interface/tests/inc/manual/micro_modules/mod_own.rs @@ -0,0 +1,5 @@ +/// has_own +pub fn has_own() -> bool +{ + true +} \ No newline at end of file diff --git a/module/core/mod_interface/tests/inc/manual/micro_modules_two/mod_protected1.rs b/module/core/mod_interface/tests/inc/manual/micro_modules_two/mod_own1.rs similarity index 100% rename from module/core/mod_interface/tests/inc/manual/micro_modules_two/mod_protected1.rs rename to module/core/mod_interface/tests/inc/manual/micro_modules_two/mod_own1.rs diff --git a/module/core/mod_interface/tests/inc/manual/micro_modules_two/mod_protected2.rs b/module/core/mod_interface/tests/inc/manual/micro_modules_two/mod_own2.rs similarity index 100% rename from module/core/mod_interface/tests/inc/manual/micro_modules_two/mod_protected2.rs rename to module/core/mod_interface/tests/inc/manual/micro_modules_two/mod_own2.rs diff --git a/module/core/mod_interface/tests/inc/mod.rs b/module/core/mod_interface/tests/inc/mod.rs index 5d8aaa7045..f838c57b50 100644 --- a/module/core/mod_interface/tests/inc/mod.rs +++ b/module/core/mod_interface/tests/inc/mod.rs @@ -22,6 +22,7 @@ mod derive mod micro_modules; mod micro_modules_two; mod micro_modules_two_joined; + mod micro_modules_glob; // layer mod layer; @@ -33,6 +34,7 @@ mod derive mod layer_use_cfg; mod layer_use_macro; + // use mod use_layer; mod use_basic; #[ path = "./use_as/derive.rs" ] @@ -40,9 +42,14 @@ mod derive #[ path = "./use_as/manual.rs" ] mod use_as_manual; + // reuse + mod reuse_basic; + // attr mod attr_debug; } -mod trybuild_test; +// mod trybuild_test; + +// xxx : enable \ No newline at end of file diff --git a/module/core/mod_interface_meta/Cargo.toml b/module/core/mod_interface_meta/Cargo.toml index b3de1c7f95..687f54ddbe 100644 --- a/module/core/mod_interface_meta/Cargo.toml +++ b/module/core/mod_interface_meta/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "mod_interface_meta" -version = "0.24.0" +version = "0.25.0" edition = "2021" authors = [ "Kostiantyn Wandalen ", diff --git a/module/core/mod_interface_meta/src/impls.rs b/module/core/mod_interface_meta/src/impls.rs index 737dab4d78..58fec18e93 100644 --- a/module/core/mod_interface_meta/src/impls.rs +++ b/module/core/mod_interface_meta/src/impls.rs @@ -16,7 +16,7 @@ mod private // = ? // x - // protected protected1; + // own own1; // orphan orphan1; // exposed exposed1; // prelude prelude1; @@ -33,7 +33,7 @@ mod private // x // orphan macromod mod_orphan1; - // : protected -> protected + // : own -> own // : orphan -> orphan // : exposed -> orphan // : prelude -> orphan @@ -42,14 +42,14 @@ mod private // x // prelude exposed macromod mod_own1; - // : protected -> exposed + // : own -> exposed // : orphan -> exposed // : exposed -> exposed // : prelude -> prelude // x - // prelude protected macromod mod_exposed1; - // : protected -> protected + // prelude own macromod mod_exposed1; + // : own -> own // : orphan -> orphan // : exposed -> exposed // : prelude -> prelude @@ -58,14 +58,14 @@ mod private // x // exposed exposed macromod mod_exposed1; - // : protected -> exposed + // : own -> exposed // : orphan -> exposed // : exposed -> exposed // : prelude -> exposed // x // exposed orphan macromod mod_exposed1; - // : protected -> orphan + // : own -> orphan // : orphan -> orphan // : exposed -> exposed // : prelude -> exposed @@ -102,12 +102,10 @@ mod private /// /// Handle record "use" with implicit visibility. /// - #[ allow ( dead_code ) ] - fn record_use_implicit + fn record_reuse_implicit ( record : &Record, c : &'_ mut RecordContext< '_ >, - // clauses_map : &mut HashMap< u32, Vec< proc_macro2::TokenStream > >, ) -> syn::Result< () > @@ -115,35 +113,88 @@ mod private let attrs1 = &record.attrs; let path = record.use_elements.as_ref().unwrap(); - // let vis = record.vis.clone(); - // if vis == Visibility::Inherited + let path = if let Some( rename ) = &path.rename + { + let pure_path = path.pure_without_super_path()?; + c.clauses_map.get_mut( &ClauseImmediates::Kind() ).unwrap().push( qt! + { + pub use #pure_path as #rename; + }); + parse_qt!{ #rename } + } + else + { + path.clone() + }; - // xxx + let adjsuted_path = path.prefixed_with_all(); - // let _path; - // let path2 = if path.prefix_is_needed() - // { - // _path = parse_qt!{ super::private::#path }; - // &_path - // } - // else - // { - // path - // }; + c.clauses_map.get_mut( &VisOwn::Kind() ).unwrap().push( qt! + { + #[ doc( inline ) ] + #[ allow( unused_imports ) ] + #attrs1 + pub use #adjsuted_path::own::*; + }); - let adjsuted_path = path.adjsuted_implicit_path()?; + c.clauses_map.get_mut( &VisOrphan::Kind() ).unwrap().push( qt! + { + #[ doc( inline ) ] + #[ allow( unused_imports ) ] + #attrs1 + pub use #adjsuted_path::orphan::*; + }); - // println!( "adjsuted_path : {}", qt!{ #adjsuted_path } ); + c.clauses_map.get_mut( &VisExposed::Kind() ).unwrap().push( qt! + { + #[ doc( inline ) ] + #[ allow( unused_imports ) ] + #attrs1 + pub use #adjsuted_path::exposed::*; + }); - if let Some( rename ) = &path.rename + c.clauses_map.get_mut( &VisPrelude::Kind() ).unwrap().push( qt! + { + #[ doc( inline ) ] + #[ allow( unused_imports ) ] + #attrs1 + pub use #adjsuted_path::prelude::*; + }); + + Ok( () ) + } + + /// + /// Handle record "use" with implicit visibility. + /// + fn record_use_implicit + ( + record : &Record, + c : &'_ mut RecordContext< '_ >, + ) + -> + syn::Result< () > + { + + let attrs1 = &record.attrs; + let path = record.use_elements.as_ref().unwrap(); + + let path = if let Some( rename ) = &path.rename { let pure_path = path.pure_without_super_path()?; c.clauses_map.get_mut( &ClauseImmediates::Kind() ).unwrap().push( qt! { pub use #pure_path as #rename; }); + parse_qt!{ #rename } } + else + { + path.clone() + }; + + let adjsuted_path = path.prefixed_with_all(); c.clauses_map.get_mut( &VisOwn::Kind() ).unwrap().push( qt! { @@ -175,12 +226,10 @@ mod private /// /// Handle record "use" with explicit visibility. /// - #[ allow ( dead_code ) ] fn record_use_explicit ( record : &Record, c : &'_ mut RecordContext< '_ >, - // clauses_map : &mut HashMap< u32, Vec< proc_macro2::TokenStream > >, ) -> syn::Result< () > @@ -200,8 +249,7 @@ mod private )); } - let adjsuted_path = path.adjsuted_explicit_path(); - + let adjsuted_path = path.prefixed_with_all(); let vis2 = if vis.restriction().is_some() { qt!{ pub( crate ) } @@ -248,13 +296,16 @@ mod private if !record.vis.valid_sub_namespace() { - return Err( syn_err! + return Err ( - record, - "To include a non-standard module use either {} visibility:\n {}", - VALID_VISIBILITY_LIST_STR, - qt!{ #record }, - )); + syn_err! + ( + record, + "To include a non-standard module use either {} visibility:\n {}", + VALID_VISIBILITY_LIST_STR, + qt!{ #record }, + ) + ); } c.clauses_map.get_mut( &record.vis.kind() ).unwrap().push( qt! @@ -263,7 +314,8 @@ mod private #[ allow( unused_imports ) ] #attrs1 #attrs2 - pub use #path; + pub use __all__::#path; + // pub use super::#path; // xxx : remove super? }); @@ -310,7 +362,7 @@ mod private #[ allow( unused_imports ) ] #attrs1 #attrs2 - pub use #path::orphan::*; + pub use __all__::#path::orphan::*; }); c.clauses_map.get_mut( &VisExposed::Kind() ).unwrap().push( qt! @@ -319,7 +371,7 @@ mod private #[ allow( unused_imports ) ] #attrs1 #attrs2 - pub use #path::exposed::*; + pub use __all__::#path::exposed::*; }); c.clauses_map.get_mut( &VisPrelude::Kind() ).unwrap().push( qt! @@ -328,7 +380,7 @@ mod private #[ allow( unused_imports ) ] #attrs1 #attrs2 - pub use #path::prelude::*; + pub use __all__::#path::prelude::*; }); Ok( () ) @@ -383,6 +435,23 @@ mod private record_use_explicit( record, &mut record_context )?; } }, + Reuse( _ ) => + { + let vis = &record.vis; + if vis == &Visibility::Inherited + { + record_reuse_implicit( record, &mut record_context )?; + } + else + { + return Err( syn_err! + ( + record, + "Using visibility usesd before `reuse` is illegal\n{}", + qt!{ #record }, + )); + } + }, _ => { record.elements.iter().try_for_each( | element | -> syn::Result::< () > @@ -397,8 +466,9 @@ mod private { record_layer( record, element, &mut record_context )?; }, - Use( _ ) => + _ => { + panic!( "Unexpected" ) }, } syn::Result::Ok( () ) @@ -410,7 +480,7 @@ mod private })?; let immediates_clause = clauses_map.get( &ClauseImmediates::Kind() ).unwrap(); - let protected_clause = clauses_map.get( &VisOwn::Kind() ).unwrap(); + let own_clause = clauses_map.get( &VisOwn::Kind() ).unwrap(); let orphan_clause = clauses_map.get( &VisOrphan::Kind() ).unwrap(); let exposed_clause = clauses_map.get( &VisExposed::Kind() ).unwrap(); let prelude_clause = clauses_map.get( &VisPrelude::Kind() ).unwrap(); @@ -420,6 +490,8 @@ mod private #( #immediates_clause )* + // use private as __private__; // this line is necessary for readable error in case private namespace is not present + #[ doc( inline ) ] #[ allow( unused_imports ) ] pub use own::*; @@ -428,19 +500,34 @@ mod private #[ allow( unused_imports ) ] pub mod own { - use super::*; + // There must be internal private namespace + // Because it's not possible to direcly make `use super::*;` + // Because then items from super can't be exposed publicly complaining: + // `error[E0428]: the name `mod1` is defined multiple times` + // use super::*; + use super::private; // this line is necessary for readable error in case private namespace is not present + mod __all__ + { + pub use super::super::*; + pub use super::super::private::*; + } #[ doc( inline ) ] - pub use orphan::*; - #( #protected_clause )* + pub use super::orphan::*; + #( #own_clause )* } /// Orphan namespace of the module. #[ allow( unused_imports ) ] pub mod orphan { - use super::*; + // use super::*; + mod __all__ + { + pub use super::super::*; + pub use super::super::private::*; + } #[ doc( inline ) ] - pub use exposed::*; + pub use super::exposed::*; #( #orphan_clause )* } @@ -448,9 +535,14 @@ mod private #[ allow( unused_imports ) ] pub mod exposed { - use super::*; + // use super::*; + mod __all__ + { + pub use super::super::*; + pub use super::super::private::*; + } #[ doc( inline ) ] - pub use prelude::*; + pub use super::prelude::*; #( #exposed_clause )* } @@ -458,7 +550,12 @@ mod private #[ allow( unused_imports ) ] pub mod prelude { - use super::*; + // use super::*; + mod __all__ + { + pub use super::super::*; + pub use super::super::private::*; + } #( #prelude_clause )* } diff --git a/module/core/mod_interface_meta/src/lib.rs b/module/core/mod_interface_meta/src/lib.rs index bb595ba9a2..0505641d53 100644 --- a/module/core/mod_interface_meta/src/lib.rs +++ b/module/core/mod_interface_meta/src/lib.rs @@ -1,9 +1,10 @@ #![ doc( html_logo_url = "https://raw.githubusercontent.com/Wandalen/wTools/master/asset/img/logo_v3_trans_square.png" ) ] #![ doc( html_favicon_url = "https://raw.githubusercontent.com/Wandalen/wTools/alpha/asset/img/logo_v3_trans_square_icon_small_v2.ico" ) ] #![ doc( html_root_url = "https://docs.rs/mod_interface_meta/latest/mod_interface_meta/" ) ] -#![ deny( dead_code ) ] #![ doc = include_str!( concat!( env!( "CARGO_MANIFEST_DIR" ), "/", "Readme.md" ) ) ] +#![ warn( dead_code ) ] + // xxx : clean up, ad solve problems // - example based on simpified version of test::layer_have_layer with single sublayer // - example with attribute `#![ debug ]` diff --git a/module/core/mod_interface_meta/src/record.rs b/module/core/mod_interface_meta/src/record.rs index c60b0bb55c..70e8f289ec 100644 --- a/module/core/mod_interface_meta/src/record.rs +++ b/module/core/mod_interface_meta/src/record.rs @@ -11,6 +11,7 @@ mod private pub mod kw { super::syn::custom_keyword!( layer ); + super::syn::custom_keyword!( reuse ); } /// @@ -23,6 +24,7 @@ mod private MicroModule( syn::token::Mod ), Layer( kw::layer ), Use( syn::token::Use ), + Reuse( kw::reuse ), } // @@ -47,6 +49,10 @@ mod private { ElementType::Layer( input.parse()? ) }, + _case if lookahead.peek( kw::reuse ) => + { + ElementType::Reuse( input.parse()? ) + }, _default => { return Err( lookahead.error() ) @@ -69,6 +75,7 @@ mod private MicroModule( e ) => e.to_tokens( tokens ), Use( e ) => e.to_tokens( tokens ), Layer( e ) => e.to_tokens( tokens ), + Reuse( e ) => e.to_tokens( tokens ), } } } @@ -104,7 +111,7 @@ mod private match element_type { - ElementType::Use( _ ) => + ElementType::Use( _ ) | ElementType::Reuse( _ ) => { use_elements = Some( input.parse()? ); elements = syn::punctuated::Punctuated::new(); diff --git a/module/core/mod_interface_meta/src/use_tree.rs b/module/core/mod_interface_meta/src/use_tree.rs index f87ee133ad..de6805dd90 100644 --- a/module/core/mod_interface_meta/src/use_tree.rs +++ b/module/core/mod_interface_meta/src/use_tree.rs @@ -105,32 +105,50 @@ mod private Ok( path ) } - /// Adjusted path. - /// Add `super::private::` to path unless it starts from `::` or `super` or `crate`. - pub fn adjsuted_implicit_path( &self ) -> syn::Result< syn::punctuated::Punctuated< syn::Ident, Token![::] > > - { - // use syn::UseTree::*; - let pure_path = self.pure_path()?; - if self.prefix_is_needed() - { - Ok( parse_qt!{ super::private::#pure_path } ) - } - else - { - Ok( pure_path ) - } - } +// /// Adjusted path. +// /// Add `super::private::` to path unless it starts from `::` or `super` or `crate`. +// pub fn adjsuted_implicit_path( &self ) -> syn::Result< syn::punctuated::Punctuated< syn::Ident, Token![::] > > +// { +// // use syn::UseTree::*; +// let pure_path = self.pure_path()?; +// if self.prefix_is_needed() +// { +// Ok( parse_qt!{ super::private::#pure_path } ) +// } +// else +// { +// Ok( pure_path ) +// } +// } +// +// /// Adjusted path. +// /// Add `super::private::` to path unless it starts from `::` or `super` or `crate`. +// // pub fn adjsuted_explicit_path( &self ) -> syn::UseTree +// pub fn adjsuted_explicit_path( &self ) -> Self +// { +// // use syn::UseTree::*; +// if self.prefix_is_needed() +// { +// let mut clone = self.clone(); +// let tree = parse_qt!{ super::private::#self }; +// clone.tree = tree; +// clone +// } +// else +// { +// self.clone() +// } +// } - /// Adjusted path. - /// Add `super::private::` to path unless it starts from `::` or `super` or `crate`. - // pub fn adjsuted_explicit_path( &self ) -> syn::UseTree - pub fn adjsuted_explicit_path( &self ) -> Self + /// Prefix path with __all__ if it's appropriate. + pub fn prefixed_with_all( &self ) -> Self { + // use syn::UseTree::*; if self.prefix_is_needed() { let mut clone = self.clone(); - let tree = parse_qt!{ super::private::#self }; + let tree = parse_qt!{ __all__::#self }; clone.tree = tree; clone } @@ -138,6 +156,7 @@ mod private { self.clone() } + } } diff --git a/module/core/process_tools/src/lib.rs b/module/core/process_tools/src/lib.rs index ceb35389ea..2f91e2f714 100644 --- a/module/core/process_tools/src/lib.rs +++ b/module/core/process_tools/src/lib.rs @@ -7,6 +7,8 @@ #[ cfg( feature = "enabled" ) ] use mod_interface::mod_interface; +mod private {} + #[ cfg( feature = "enabled" ) ] mod_interface! { diff --git a/module/core/proper_path_tools/src/lib.rs b/module/core/proper_path_tools/src/lib.rs index fe0f646c03..297c2aeff8 100644 --- a/module/core/proper_path_tools/src/lib.rs +++ b/module/core/proper_path_tools/src/lib.rs @@ -11,6 +11,8 @@ use mod_interface::mod_interface; #[ macro_use ] extern crate alloc; +mod private {} + #[ cfg( feature = "enabled" ) ] mod_interface! { diff --git a/module/core/test_tools/src/lib.rs b/module/core/test_tools/src/lib.rs index 8baa4d9530..efe6e6d7f0 100644 --- a/module/core/test_tools/src/lib.rs +++ b/module/core/test_tools/src/lib.rs @@ -49,6 +49,10 @@ pub mod dependency } +mod private {} + +// + #[ cfg( feature = "enabled" ) ] // #[ cfg( not( feature = "no_std" ) ) ] ::meta_tools::mod_interface! diff --git a/module/core/test_tools/src/test/compiletime.rs b/module/core/test_tools/src/test/compiletime.rs index 77f9d362ac..4f29ec998e 100644 --- a/module/core/test_tools/src/test/compiletime.rs +++ b/module/core/test_tools/src/test/compiletime.rs @@ -10,10 +10,72 @@ mod private pub use ::trybuild::*; } +// // // +// #[ doc( inline ) ] +// #[ allow( unused_imports ) ] +// pub use own::*; +// +// #[ doc = r" Own namespace of the module." ] +// #[ allow( unused_imports ) ] +// pub mod own +// { +// use super::private; +// mod __all__ +// { +// pub use super::super::*; +// pub use super::super::private::*; +// } +// #[ doc( inline ) ] +// pub use super::orphan::*; +// #[ doc( inline ) ] +// #[ allow( unused_imports ) ] +// pub use private::{*}; +// } +// +// #[ doc = r" Orphan namespace of the module." ] +// #[ allow( unused_imports ) ] +// pub mod orphan +// { +// mod __all__ +// { +// pub use super::super::*; +// pub use super::super::private::*; +// } +// #[ doc( inline ) ] +// pub use super::exposed::*; +// } +// +// #[ doc = r" Exposed namespace of the module." ] +// #[ allow( unused_imports ) ] +// pub mod exposed +// { +// mod __all__ +// { +// pub use super::super::*; +// pub use super::super::private::*; +// } +// #[ doc( inline ) ] +// pub use super::prelude::*; +// #[ doc( inline ) ] +// #[ allow( unused_imports ) ] +// pub use super::super::compiletime; +// } +// +// #[ doc = r" Prelude to use essentials: `use my_module::prelude::*`." ] +// #[ allow( unused_imports ) ] +// pub mod prelude +// { +// mod __all__ +// { +// pub use super::super::*; +// pub use super::super::private::*; +// } +// } crate::mod_interface! { + // #![ debug ] // xxx : make it working // exposed use super; exposed use super::super::compiletime; diff --git a/module/core/test_tools/src/test/helper.rs b/module/core/test_tools/src/test/helper.rs index fe79e69784..49675e2ada 100644 --- a/module/core/test_tools/src/test/helper.rs +++ b/module/core/test_tools/src/test/helper.rs @@ -75,10 +75,10 @@ mod private pub use doc_file_test; } -// - crate::mod_interface! { + // xxx + // #![ debug ] // exposed use super; exposed use super::super::helper; diff --git a/module/core/test_tools/src/test/mod.rs b/module/core/test_tools/src/test/mod.rs index 60ec182ac4..158406fbd1 100644 --- a/module/core/test_tools/src/test/mod.rs +++ b/module/core/test_tools/src/test/mod.rs @@ -3,6 +3,8 @@ //! Tools for testing. //! +mod private {} + // #[ cfg( not( feature = "no_std" ) ) ] crate::mod_interface! { diff --git a/module/move/wca/src/ca/executor/mod.rs b/module/move/wca/src/ca/executor/mod.rs index 77789544d0..1793a9d23f 100644 --- a/module/move/wca/src/ca/executor/mod.rs +++ b/module/move/wca/src/ca/executor/mod.rs @@ -1,3 +1,5 @@ +mod private {} + crate::mod_interface! { diff --git a/module/move/wca/src/ca/grammar/mod.rs b/module/move/wca/src/ca/grammar/mod.rs index f31e992b38..28a87f9e2b 100644 --- a/module/move/wca/src/ca/grammar/mod.rs +++ b/module/move/wca/src/ca/grammar/mod.rs @@ -1,3 +1,5 @@ +mod private {} + crate::mod_interface! { /// User grammar settings. diff --git a/module/move/wca/src/ca/mod.rs b/module/move/wca/src/ca/mod.rs index 3526d2c8fa..66c6832f28 100644 --- a/module/move/wca/src/ca/mod.rs +++ b/module/move/wca/src/ca/mod.rs @@ -2,6 +2,8 @@ //! Commands aggregator library. //! +mod private {} + crate::mod_interface! { diff --git a/module/move/wca/src/ca/parser/mod.rs b/module/move/wca/src/ca/parser/mod.rs index 6d21385d36..50322eee12 100644 --- a/module/move/wca/src/ca/parser/mod.rs +++ b/module/move/wca/src/ca/parser/mod.rs @@ -1,3 +1,5 @@ +mod private {} + crate::mod_interface! { /// This module defines a raw representation of parsed commands, providing a foundation for further processing and @@ -5,7 +7,7 @@ crate::mod_interface! /// a straightforward and easy-to-work-with format, allowing for efficient manipulation and subsequent conversion to /// other representations. layer command; - + /// This module is responsible for processing command-line arguments and parsing them into a raw representation of a /// program containing multiple parsed commands. The input list of arguments is transformed into a structured format, /// allowing the program to efficiently handle and manipulate the parsed commands. diff --git a/module/move/wca/src/ca/tool/mod.rs b/module/move/wca/src/ca/tool/mod.rs index 116804b97d..91290592a7 100644 --- a/module/move/wca/src/ca/tool/mod.rs +++ b/module/move/wca/src/ca/tool/mod.rs @@ -1,3 +1,5 @@ +mod private {} + crate::mod_interface! { diff --git a/module/move/wca/src/ca/verifier/mod.rs b/module/move/wca/src/ca/verifier/mod.rs index 7ed35ae7b9..4723d0bdcc 100644 --- a/module/move/wca/src/ca/verifier/mod.rs +++ b/module/move/wca/src/ca/verifier/mod.rs @@ -1,3 +1,5 @@ +mod private {} + crate::mod_interface! { /// Represents a grammatically correct command with a phrase descriptor, a list of command subjects, and a set of command options.. diff --git a/module/move/wca/src/lib.rs b/module/move/wca/src/lib.rs index c318aa58fd..7f42a20e14 100644 --- a/module/move/wca/src/lib.rs +++ b/module/move/wca/src/lib.rs @@ -11,6 +11,8 @@ use mod_interface::mod_interface; pub mod ca; +mod private {} + crate::mod_interface! { use super::ca; diff --git a/module/move/willbe/src/action/mod.rs b/module/move/willbe/src/action/mod.rs index 728271c2a5..c824bfd6f7 100644 --- a/module/move/willbe/src/action/mod.rs +++ b/module/move/willbe/src/action/mod.rs @@ -1,3 +1,5 @@ +mod private {} + crate::mod_interface! { /// Deploy new. diff --git a/module/move/willbe/src/entity/mod.rs b/module/move/willbe/src/entity/mod.rs index 6f26700128..100b331e89 100644 --- a/module/move/willbe/src/entity/mod.rs +++ b/module/move/willbe/src/entity/mod.rs @@ -1,3 +1,5 @@ +mod private {} + crate::mod_interface! { /// Rust toolchain channel: stable/nightly. diff --git a/module/move/willbe/src/tool/mod.rs b/module/move/willbe/src/tool/mod.rs index 060a323c2f..719b616b4b 100644 --- a/module/move/willbe/src/tool/mod.rs +++ b/module/move/willbe/src/tool/mod.rs @@ -1,3 +1,5 @@ +mod private {} + crate::mod_interface! { From 42857672255cb63f85886ec270e9cac89512ff77 Mon Sep 17 00:00:00 2001 From: Petro Shvayko Date: Fri, 13 Sep 2024 09:56:44 +0300 Subject: [PATCH 20/67] fix: checkmate behavior --- .github/workflows/standard_rust_push.yml | 17 ++++++++++++++--- .../template/workflow/standard_rust_push.yml | 15 +++++++++++++-- 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/.github/workflows/standard_rust_push.yml b/.github/workflows/standard_rust_push.yml index 310c7fa765..a243d1affe 100644 --- a/.github/workflows/standard_rust_push.yml +++ b/.github/workflows/standard_rust_push.yml @@ -64,21 +64,32 @@ jobs : - name: Build module run: cd ${{ steps.rootpath.outputs.path }} && cargo build && cd - - name: Audit the modules + id: audit run: make audit continue-on-error: true - name: Generate documentation for the modules + id: documentation run: make doc open=no manifest_path=${{ inputs.manifest_path }} continue-on-error: true - name: Lint the modules + id: lint run: make lint manifest_path=${{ inputs.manifest_path }} warnings=no continue-on-error: true - name: Check the modules + id: check run: make check manifest_path=${{ inputs.manifest_path }} continue-on-error: true - name: Check the modules dependencies + id: udeps run: cargo +nightly udeps --all-targets --manifest-path ${{ inputs.manifest_path }} -# This part means that all previous steps will be executed and workflow will not stop if they fail, but not this one. - continue-on-error: false + continue-on-error: true + # Added IDs for each step in the test job: This allows referencing the result of each step later. + # + # "Check for errors" step: Now checks the outcome status for each step. + # If any of them have a value of 'failure', this step will fail the job by returning exit 1. + - name: Check for errors + if: steps.audit.outcome != 'success' || steps.documentation.outcome != 'success' || steps.lint.outcome != 'success' || steps.check.outcome != 'success' || steps.udeps.outcome != 'success' + run: exit 1 # release: # if: contains( inputs.commit_message, '+test' ) || contains( inputs.commit_message, 'merge' ) @@ -127,7 +138,7 @@ jobs : will_test : # This section ensures that `job` `will_test` will only be executed after `checkmate` and if `checkmate` fails, no tests will be run. - needs: + needs : - checkmate if : contains( inputs.commit_message, '+test' ) || inputs.commiter_username == 'web-flow' || startsWith( inputs.commit_message, 'merge' ) concurrency : diff --git a/module/move/willbe/template/workflow/standard_rust_push.yml b/module/move/willbe/template/workflow/standard_rust_push.yml index 2f6103ebc2..a243d1affe 100644 --- a/module/move/willbe/template/workflow/standard_rust_push.yml +++ b/module/move/willbe/template/workflow/standard_rust_push.yml @@ -64,21 +64,32 @@ jobs : - name: Build module run: cd ${{ steps.rootpath.outputs.path }} && cargo build && cd - - name: Audit the modules + id: audit run: make audit continue-on-error: true - name: Generate documentation for the modules + id: documentation run: make doc open=no manifest_path=${{ inputs.manifest_path }} continue-on-error: true - name: Lint the modules + id: lint run: make lint manifest_path=${{ inputs.manifest_path }} warnings=no continue-on-error: true - name: Check the modules + id: check run: make check manifest_path=${{ inputs.manifest_path }} continue-on-error: true - name: Check the modules dependencies + id: udeps run: cargo +nightly udeps --all-targets --manifest-path ${{ inputs.manifest_path }} - # This part means that all previous steps will be executed and workflow will not stop if they fail, but not this one. - continue-on-error: false + continue-on-error: true + # Added IDs for each step in the test job: This allows referencing the result of each step later. + # + # "Check for errors" step: Now checks the outcome status for each step. + # If any of them have a value of 'failure', this step will fail the job by returning exit 1. + - name: Check for errors + if: steps.audit.outcome != 'success' || steps.documentation.outcome != 'success' || steps.lint.outcome != 'success' || steps.check.outcome != 'success' || steps.udeps.outcome != 'success' + run: exit 1 # release: # if: contains( inputs.commit_message, '+test' ) || contains( inputs.commit_message, 'merge' ) From 19f187e27cb3d46814c754f68cb8ad8baa392090 Mon Sep 17 00:00:00 2001 From: ".Barsik" Date: Sun, 13 Oct 2024 00:10:15 +0300 Subject: [PATCH 21/67] unitore: we can define async command in prettier way (#1464) Logic is unchanged. Compiles to the same code. Looks prettier. --- module/move/unitore/src/command/config.rs | 114 +++++++++------------- 1 file changed, 47 insertions(+), 67 deletions(-) diff --git a/module/move/unitore/src/command/config.rs b/module/move/unitore/src/command/config.rs index bbd436ccb9..b1e678a732 100644 --- a/module/move/unitore/src/command/config.rs +++ b/module/move/unitore/src/command/config.rs @@ -18,7 +18,28 @@ impl ConfigCommand /// Create command for adding config. pub fn add() -> Result< Command > { - let rt = tokio::runtime::Runtime::new()?; + #[ tokio::main ] + async fn add_command( o : VerifiedCommand ) + { + // qqq: could we print something on None value? + let Some( path ) = o.args.get_owned::< PathBuf >( 0 ) else { return; }; + + let path_to_storage = std::env::var( "UNITORE_STORAGE_PATH" ).ok() + .unwrap_or_else( || String::from( "./_data" ) ); + let config = Config::default().path( path_to_storage ); + + let res = ( || async + { + let feed_storage = FeedStorage::init_storage( &config ).await?; + config_add( feed_storage, &path ).await + } )().await; + + match res + { + Ok( report ) => report.report(), + Err( err ) => println!( "{:?}", err ), + } + } Ok ( @@ -37,38 +58,7 @@ impl ConfigCommand " link = \"https://feeds.bbci.co.uk/news/world/rss.xml\"\n", )) .subject().hint( "Path" ).kind( Type::Path ).optional( false ).end() - .routine( move | o : VerifiedCommand | - { - let path_arg = o.args - .get_owned::< wca::Value >( 0 ); - - if let Some( path ) = path_arg - { - let path : PathBuf = path.into(); - - let res = rt.block_on - ( async move - { - let path_to_storage = std::env::var( "UNITORE_STORAGE_PATH" ) - .unwrap_or( String::from( "./_data" ) ) - ; - - let config = Config::default() - .path( path_to_storage ) - ; - - let feed_storage = FeedStorage::init_storage( &config ).await?; - config_add( feed_storage, &path ).await - } - ); - - match res - { - Ok( report ) => report.report(), - Err( err ) => println!( "{:?}", err ), - } - } - }) + .routine( add_command ) .end() ) } @@ -76,8 +66,29 @@ impl ConfigCommand /// Create command for deleting config. pub fn delete() -> Result< Command > { - let rt = tokio::runtime::Runtime::new()?; - + #[ tokio::main ] + async fn delete_command( o : VerifiedCommand ) + { + // qqq: could we print something on None value? + let Some( path ) = o.args.get_owned::< PathBuf >( 0 ) else { return; }; + + let path_to_storage = std::env::var( "UNITORE_STORAGE_PATH" ).ok() + .unwrap_or_else( || String::from( "./_data" ) ); + let config = Config::default().path( path_to_storage ); + + let res = ( || async + { + let feed_storage = FeedStorage::init_storage( &config ).await?; + config_delete( feed_storage, &path ).await + } )().await; + + match res + { + Ok( report ) => report.report(), + Err( err ) => println!( "{:?}", err ), + } + } + Ok( Command::former() .phrase( "config.delete" ) @@ -87,38 +98,7 @@ impl ConfigCommand " Example: .config.delete ./config/feeds.toml", )) .subject().hint( "Path" ).kind( Type::Path ).optional( false ).end() - .routine( move | o : VerifiedCommand | - { - let path_arg = o.args - .get_owned::< wca::Value >( 0 ); - - if let Some( path ) = path_arg - { - let path : PathBuf = path.into(); - - let res = rt.block_on - ( async move - { - let path_to_storage = std::env::var( "UNITORE_STORAGE_PATH" ) - .unwrap_or( String::from( "./_data" ) ) - ; - - let config = Config::default() - .path( path_to_storage ) - ; - - let feed_storage = FeedStorage::init_storage( &config ).await?; - config_delete( feed_storage, &path ).await - } - ); - - match res - { - Ok( report ) => report.report(), - Err( err ) => println!( "{:?}", err ), - } - } - }) + .routine( delete_command ) .end() ) } From ebc194a031e8dacaa71c9c49d28bbccf3204ced8 Mon Sep 17 00:00:00 2001 From: SRetip <56289352+SRetip@users.noreply.github.com> Date: Sun, 13 Oct 2024 00:10:33 +0300 Subject: [PATCH 22/67] READY: make more informative description (#1462) * feat: make more informative description * rewrite --- module/move/willbe/src/command/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/module/move/willbe/src/command/mod.rs b/module/move/willbe/src/command/mod.rs index 6b87d5877b..bae53834e1 100644 --- a/module/move/willbe/src/command/mod.rs +++ b/module/move/willbe/src/command/mod.rs @@ -128,8 +128,8 @@ with_gitpod: If set to 1, a column with a link to Gitpod will be added. Clicking .end() .command( "test" ) - .hint( "execute tests in specific packages" ) - .long_hint( "this command runs tests in designated packages based on the provided path. It allows for inclusion and exclusion of features, testing on different Rust version channels, parallel execution, and feature combination settings." ) + .hint( "List crate features to run tests for each combination, aiming for full test coverage of the crate." ) + .long_hint( "List crate features, different optimization level (Release & Debug) and toolchain (stable & nightly) to run tests for each combination. Сan be used for packages as well as workspaces. Supports parallel execution." ) .subject().hint( "A path to directories with packages. If no path is provided, the current directory is used." ).kind( Type::Path ).optional( true ).end() .property( "dry" ).hint( "Enables 'dry run'. Does not run tests, only simulates. Default is `true`." ).kind( Type::Bool ).optional( true ).end() .property( "temp" ).hint( "If flag is `true` all test will be running in temporary directories. Default `true`." ).kind( Type::Bool ).optional( true ).end() From 4a090ae91f22cd3ada9a1624864f60c1fca0afe6 Mon Sep 17 00:00:00 2001 From: wbot <69343704+wtools-bot@users.noreply.github.com> Date: Sun, 13 Oct 2024 00:11:19 +0300 Subject: [PATCH 23/67] AUTO : Forward from format_tools_evolving_5 to alpha (#1460) evolve format_tools --- Cargo.toml | 26 +- module/core/diagnostics_tools/Cargo.toml | 2 +- module/core/for_each/Cargo.toml | 2 +- module/core/format_tools/src/format.rs | 78 ++++-- .../core/format_tools/src/format/as_table.rs | 76 ++--- .../core/format_tools/src/format/md_math.rs | 2 + .../format_tools/src/format/output_format.rs | 10 +- .../src/format/output_format/keys.rs | 107 ++++++++ .../output_format/{ordinary.rs => table.rs} | 22 +- module/core/format_tools/src/format/print.rs | 27 +- module/core/format_tools/src/format/table.rs | 61 +++-- .../src/format/test_object_without_impl.rs | 155 +++++++++++ .../format_tools/tests/inc/collection_test.rs | 32 +-- .../format_tools/tests/inc/fields_test.rs | 32 ++- .../tests/inc/format_records_test.rs | 2 +- ..._ordinary_test.rs => format_table_test.rs} | 16 +- module/core/format_tools/tests/inc/mod.rs | 7 +- .../tests/inc/tabe_foreign_test.rs | 131 +++++++++ .../core/format_tools/tests/inc/table_test.rs | 259 +++++++++++++++++- .../format_tools/tests/inc/test_object.rs | 104 ++++--- module/core/format_tools/tests/smoke_test.rs | 5 +- module/core/format_tools/tests/tests.rs | 8 +- module/core/implements/Cargo.toml | 2 +- module/core/impls_index/Cargo.toml | 2 +- module/core/impls_index_meta/Cargo.toml | 2 +- module/core/inspect_type/Cargo.toml | 2 +- module/core/inspect_type/src/lib.rs | 2 + .../tests/inc/inspect_type_test.rs | 1 + module/core/inspect_type/tests/tests.rs | 2 + module/core/is_slice/Cargo.toml | 2 +- module/core/mem_tools/Cargo.toml | 2 +- module/core/meta_tools/Cargo.toml | 2 +- module/core/mod_interface/Cargo.toml | 4 +- module/core/mod_interface/src/lib.rs | 2 +- module/core/mod_interface_meta/src/lib.rs | 13 + .../reflect_tools/src/reflect/fields/vec.rs | 4 + .../core/reflect_tools/src/reflect/wrapper.rs | 4 +- .../wrapper/{maybe_as.rs => optional_cow.rs} | 25 ++ .../tests/inc/fundamental/fields_bset.rs | 1 - module/core/test_tools/Cargo.toml | 2 +- module/core/test_tools/src/lib.rs | 3 + module/core/typing_tools/Cargo.toml | 2 +- module/move/assistant/Cargo.toml | 3 +- module/move/assistant/Readme.md | 4 +- module/move/assistant/api/list.http | 8 + module/move/assistant/src/client.rs | 60 +--- module/move/assistant/src/debug.rs | 28 ++ .../assistant/src/debug/assistant_object.rs | 83 ++++++ module/move/assistant/src/debug/file_data.rs | 49 ++++ module/move/assistant/src/lib.rs | 68 +---- module/move/assistant/src/main.rs | 30 +- module/move/deterministic_rand/src/lib.rs | 2 + module/move/graphs_tools/Readme.md | 6 +- module/move/plot_interface/Readme.md | 7 +- 54 files changed, 1237 insertions(+), 354 deletions(-) create mode 100644 module/core/format_tools/src/format/output_format/keys.rs rename module/core/format_tools/src/format/output_format/{ordinary.rs => table.rs} (93%) create mode 100644 module/core/format_tools/src/format/test_object_without_impl.rs rename module/core/format_tools/tests/inc/{format_ordinary_test.rs => format_table_test.rs} (96%) create mode 100644 module/core/format_tools/tests/inc/tabe_foreign_test.rs rename module/core/reflect_tools/src/reflect/wrapper/{maybe_as.rs => optional_cow.rs} (88%) create mode 100644 module/move/assistant/src/debug.rs create mode 100644 module/move/assistant/src/debug/assistant_object.rs create mode 100644 module/move/assistant/src/debug/file_data.rs diff --git a/Cargo.toml b/Cargo.toml index 00ffdb5cdf..522f6e750a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -185,7 +185,7 @@ features = [ "enabled" ] ## mem [workspace.dependencies.mem_tools] -version = "~0.6.0" +version = "~0.7.0" path = "module/core/mem_tools" default-features = false @@ -193,7 +193,7 @@ default-features = false ## diagnostics [workspace.dependencies.diagnostics_tools] -version = "~0.8.0" +version = "~0.9.0" path = "module/core/diagnostics_tools" default-features = false @@ -209,12 +209,12 @@ default-features = false ## meta [workspace.dependencies.meta_tools] -version = "~0.10.0" +version = "~0.11.0" path = "module/core/meta_tools" default-features = false [workspace.dependencies.for_each] -version = "~0.8.0" +version = "~0.9.0" path = "module/core/for_each" default-features = false @@ -239,16 +239,16 @@ path = "module/core/former_types" default-features = false [workspace.dependencies.impls_index] -version = "~0.7.0" +version = "~0.8.0" path = "module/core/impls_index" default-features = false [workspace.dependencies.impls_index_meta] -version = "~0.7.0" +version = "~0.8.0" path = "module/core/impls_index_meta" [workspace.dependencies.mod_interface] -version = "~0.25.0" +version = "~0.26.0" path = "module/core/mod_interface" default-features = false @@ -305,12 +305,12 @@ default-features = false ## typing [workspace.dependencies.typing_tools] -version = "~0.8.0" +version = "~0.9.0" path = "module/core/typing_tools" default-features = false [workspace.dependencies.implements] -version = "~0.8.0" +version = "~0.9.0" path = "module/core/implements" default-features = false @@ -320,12 +320,12 @@ path = "module/alias/instance_of" default-features = false [workspace.dependencies.inspect_type] -version = "~0.10.0" +version = "~0.11.0" path = "module/core/inspect_type" default-features = false [workspace.dependencies.is_slice] -version = "~0.9.0" +version = "~0.10.0" path = "module/core/is_slice" default-features = false @@ -381,7 +381,7 @@ default-features = false [workspace.dependencies.process_tools_published] package = "process_tools" -version = "~0.8.0" +version = "~0.9.0" default-features = false @@ -392,7 +392,7 @@ version = "~0.4.0" path = "module/alias/wtest" [workspace.dependencies.test_tools] -version = "~0.9.0" +version = "~0.10.0" path = "module/core/test_tools" [workspace.dependencies.wtest_basic] diff --git a/module/core/diagnostics_tools/Cargo.toml b/module/core/diagnostics_tools/Cargo.toml index c2b7d7e994..f8f93610c9 100644 --- a/module/core/diagnostics_tools/Cargo.toml +++ b/module/core/diagnostics_tools/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "diagnostics_tools" -version = "0.8.0" +version = "0.9.0" edition = "2021" authors = [ "Kostiantyn Wandalen ", diff --git a/module/core/for_each/Cargo.toml b/module/core/for_each/Cargo.toml index 877ccbe184..c6387ef2cc 100644 --- a/module/core/for_each/Cargo.toml +++ b/module/core/for_each/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "for_each" -version = "0.8.0" +version = "0.9.0" edition = "2021" authors = [ "Kostiantyn Wandalen ", diff --git a/module/core/format_tools/src/format.rs b/module/core/format_tools/src/format.rs index 4e55517dcc..2abf7f18a4 100644 --- a/module/core/format_tools/src/format.rs +++ b/module/core/format_tools/src/format.rs @@ -17,8 +17,8 @@ mod private macro_rules! _field_with_key { ( + $path : expr, $key : ident, - $src : expr, $how : ty, $fallback1 : ty, $fallback2 : ty @@ -28,9 +28,10 @@ mod private {{ ( ::core::stringify!( $key ), - $crate::OptionalCow::< '_, str, $how >::from + // $crate::OptionalCow::< '_, str, $how >::from + Option::Some ( - $crate::to_string_with_fallback!( $how, $fallback1, $fallback2, $src ) + $crate::to_string_with_fallback!( $how, $fallback1, $fallback2, $path ) ), ) }}; @@ -47,32 +48,39 @@ mod private macro_rules! _field { - ( & $path:ident.$( $key:ident )+, $how : ty, $fallback1 : ty, $fallback2 : ty $(,)? ) => + // dst.push( field!( &self.id ) ); + ( ( & $pre:ident.$( $key:tt )+ ), $how : ty, $fallback1 : ty, $fallback2 : ty $(,)? ) => {{ - $crate::_field!( # ( & $path . ) ( $( $key )+ ) ( $how, $fallback1, $fallback2 ) ) + $crate::_field!( # ( & $pre . ) ( $( $key )+ ) ( $how, $fallback1, $fallback2 ) ) }}; - ( $path:ident.$( $key:ident )+, $how : ty, $fallback1 : ty, $fallback2 : ty $(,)? ) => + // dst.push( field!( self.id ) ); + ( ( $pre:ident.$( $key:tt )+ ), $how : ty, $fallback1 : ty, $fallback2 : ty $(,)? ) => {{ - $crate::_field!( # ( $path . ) ( $( $key )+ ) ( $how, $fallback1, $fallback2 ) ) + $crate::_field!( # ( $pre . ) ( $( $key )+ ) ( $how, $fallback1, $fallback2 ) ) }}; - ( & $key:ident, $how : ty, $fallback1 : ty, $fallback2 : ty $(,)? ) => + // dst.push( field!( &tools ) ); + ( ( & $key:ident ), $how : ty, $fallback1 : ty, $fallback2 : ty $(,)? ) => {{ - $crate::_field!( # () ( $key ) ( $how, $fallback1, $fallback2 ) ) + $crate::_field!( # () ( & $key ) ( $how, $fallback1, $fallback2 ) ) }}; - ( $key:ident, $how : ty, $fallback1 : ty, $fallback2 : ty $(,)? ) => + // dst.push( field!( tools ) ); + ( ( $key:ident ), $how : ty, $fallback1 : ty, $fallback2 : ty $(,)? ) => {{ $crate::_field!( # () ( $key ) ( $how, $fallback1, $fallback2 ) ) }}; // private + // ( a.b. ) + // ( c.d ) + // ( $crate::WithRef, $crate::WithDebug, $crate::WithDebug ) ( # ( $( $prefix:tt )* ) - ( $prekey:ident.$( $field:ident )+ ) + ( $prekey:ident.$( $field:tt )+ ) ( $how : ty, $fallback1 : ty, $fallback2 : ty ) ) => @@ -80,6 +88,23 @@ mod private $crate::_field!( # ( $( $prefix )* $prekey . ) ( $( $field )+ ) ( $how, $fallback1, $fallback2 ) ) }}; + // ( a.b. ) + // ( 0.d ) + // ( $crate::WithRef, $crate::WithDebug, $crate::WithDebug ) + ( + # + ( $( $prefix:tt )* ) + ( $prekey:tt.$( $field:tt )+ ) + ( $how : ty, $fallback1 : ty, $fallback2 : ty ) + ) + => + {{ + $crate::_field!( # ( $( $prefix )* $prekey . ) ( $( $field )+ ) ( $how, $fallback1, $fallback2 ) ) + }}; + + // ( a.b.c. ) + // ( d ) + // ( $crate::WithRef, $crate::WithDebug, $crate::WithDebug ) ( # ( $( $prefix:tt )* ) @@ -91,6 +116,9 @@ mod private $crate::_field!( # # ( $( $prefix )* ) ( $key ) ( $how, $fallback1, $fallback2 ) ) }}; + // ( a.b.c ) + // ( d ) + // ( $crate::WithRef, $crate::WithDebug, $crate::WithDebug ) ( # # ( $( $prefix:tt )* ) @@ -99,7 +127,8 @@ mod private ) => {{ - $crate::_field_with_key!( $key, $( $prefix )* $key, $how, $fallback1, $fallback2 ) + // _field_with_key!( id, &self. id, $crate::WithRef, $crate::WithDisplay, $crate::WithDebugMultiline ) + $crate::_field_with_key!( $( $prefix )* $key, $key, $how, $fallback1, $fallback2 ) }}; } @@ -129,7 +158,7 @@ mod private ) => {{ - $crate::_field_with_key!( $key, $src, $crate::WithRef, $crate::WithDisplay, $crate::WithDebugMultiline ) + $crate::_field_with_key!( $src, $key, $crate::WithRef, $crate::WithDisplay, $crate::WithDebugMultiline ) }}; } @@ -144,7 +173,7 @@ mod private ( $( $t:tt )+ ) => {{ - $crate::_field!( $( $t )+, $crate::WithRef, $crate::WithDisplay, $crate::WithDebugMultiline ) + $crate::_field!( ( $( $t )+ ), $crate::WithRef, $crate::WithDisplay, $crate::WithDebugMultiline ) }} } @@ -178,7 +207,7 @@ mod private ) => {{ - $crate::_field_with_key!( $key, $src, $crate::WithRef, $crate::WithDisplay, $crate::WithDebug ) + $crate::_field_with_key!( $src, $key, $crate::WithRef, $crate::WithDisplay, $crate::WithDebug ) }}; } @@ -193,7 +222,7 @@ mod private ( $( $t:tt )+ ) => {{ - $crate::_field!( $( $t )+, $crate::WithRef, $crate::WithDisplay, $crate::WithDebug ) + $crate::_field!( ( $( $t )+ ), $crate::WithRef, $crate::WithDisplay, $crate::WithDebug ) }} } @@ -226,7 +255,7 @@ mod private ) => {{ - $crate::_field_with_key!( $key, $src, $crate::WithRef, $crate::WithDebug, $crate::WithDebug ) + $crate::_field_with_key!( $src, $key, $crate::WithRef, $crate::WithDebug, $crate::WithDebug ) }}; } @@ -240,7 +269,7 @@ mod private ( $( $t:tt )+ ) => {{ - $crate::_field!( $( $t )+, $crate::WithRef, $crate::WithDebug, $crate::WithDebug ) + $crate::_field!( ( $( $t )+ ), $crate::WithRef, $crate::WithDebug, $crate::WithDebug ) }} } @@ -261,6 +290,11 @@ pub mod table; pub mod to_string; pub mod to_string_with_fallback; +/// A strucutre for diagnostic and demonstration purpose. +#[ doc( hidden ) ] +#[ cfg( debug_assertions ) ] +pub mod test_object_without_impl; + #[ doc( inline ) ] #[ allow( unused_imports ) ] pub use own::*; @@ -304,6 +338,14 @@ pub mod orphan ref_or_debug, }; + #[ doc( hidden ) ] + #[ cfg( debug_assertions ) ] + pub use test_object_without_impl:: + { + TestObjectWithoutImpl, + test_objects_gen, + }; + } /// Exposed namespace of the module. diff --git a/module/core/format_tools/src/format/as_table.rs b/module/core/format_tools/src/format/as_table.rs index 4e70ba233d..b1c48c159f 100644 --- a/module/core/format_tools/src/format/as_table.rs +++ b/module/core/format_tools/src/format/as_table.rs @@ -21,29 +21,29 @@ mod private /// #[ repr( transparent ) ] #[ derive( Clone, Copy ) ] - pub struct AsTable< 'table, Table, RowKey, Row, CellKey, CellRepr > + pub struct AsTable< 'table, Table, RowKey, Row, CellKey> ( &'table Table, ::core::marker::PhantomData <( &'table (), - fn() -> ( &'table RowKey, Row, &'table CellKey, CellRepr ), + fn() -> ( &'table RowKey, Row, &'table CellKey ), )>, ) where RowKey : table::RowKey, - Row : Cells< CellKey, CellRepr >, + Row : Cells< CellKey>, CellKey : table::CellKey + ?Sized, - CellRepr : table::CellRepr + // CellRepr : table::CellRepr ; - impl< 'table, Table, RowKey, Row, CellKey, CellRepr > - AsTable< 'table, Table, RowKey, Row, CellKey, CellRepr > + impl< 'table, Table, RowKey, Row, CellKey> + AsTable< 'table, Table, RowKey, Row, CellKey> where RowKey : table::RowKey, - Row : Cells< CellKey, CellRepr >, + Row : Cells< CellKey>, CellKey : table::CellKey + ?Sized, - CellRepr : table::CellRepr, + // CellRepr : table::CellRepr, { /// Just a constructor. pub fn new( src : &'table Table ) -> Self @@ -52,13 +52,13 @@ mod private } } - impl< 'table, Table, RowKey, Row, CellKey, CellRepr > AsRef< Table > - for AsTable< 'table, Table, RowKey, Row, CellKey, CellRepr > + impl< 'table, Table, RowKey, Row, CellKey> AsRef< Table > + for AsTable< 'table, Table, RowKey, Row, CellKey> where RowKey : table::RowKey, - Row : Cells< CellKey, CellRepr >, + Row : Cells< CellKey>, CellKey : table::CellKey + ?Sized, - CellRepr : table::CellRepr, + // CellRepr : table::CellRepr, { fn as_ref( &self ) -> &Table { @@ -66,13 +66,13 @@ mod private } } - impl< 'table, Table, RowKey, Row, CellKey, CellRepr > Deref - for AsTable< 'table, Table, RowKey, Row, CellKey, CellRepr > + impl< 'table, Table, RowKey, Row, CellKey> Deref + for AsTable< 'table, Table, RowKey, Row, CellKey> where RowKey : table::RowKey, - Row : Cells< CellKey, CellRepr >, + Row : Cells< CellKey>, CellKey : table::CellKey + ?Sized, - CellRepr : table::CellRepr, + // CellRepr : table::CellRepr, { type Target = Table; @@ -82,13 +82,13 @@ mod private } } - impl< 'table, Table, RowKey, Row, CellKey, CellRepr > From< &'table Table > - for AsTable< 'table, Table, RowKey, Row, CellKey, CellRepr > + impl< 'table, Table, RowKey, Row, CellKey> From< &'table Table > + for AsTable< 'table, Table, RowKey, Row, CellKey> where RowKey : table::RowKey, - Row : Cells< CellKey, CellRepr >, + Row : Cells< CellKey>, CellKey : table::CellKey + ?Sized, - CellRepr : table::CellRepr, + // CellRepr : table::CellRepr, { fn from( table : &'table Table ) -> Self { @@ -96,14 +96,14 @@ mod private } } - impl< 'table, Table, RowKey, Row, CellKey, CellRepr > fmt::Debug - for AsTable< 'table, Table, RowKey, Row, CellKey, CellRepr > + impl< 'table, Table, RowKey, Row, CellKey> fmt::Debug + for AsTable< 'table, Table, RowKey, Row, CellKey> where Table : fmt::Debug, RowKey : table::RowKey, - Row : Cells< CellKey, CellRepr >, + Row : Cells< CellKey>, CellKey : table::CellKey + ?Sized, - CellRepr : table::CellRepr, + // CellRepr : table::CellRepr, { fn fmt( &self, f : &mut fmt::Formatter< '_ > ) -> fmt::Result { @@ -130,25 +130,25 @@ mod private type RowKey : table::RowKey; /// The type representing a row, must implement `Cells`. - type Row : Cells< Self::CellKey, Self::CellRepr >; + type Row : Cells< Self::CellKey >; /// 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; + // /// 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 >; + fn as_table( &self ) -> AsTable< '_, Self::Table, Self::RowKey, Self::Row, Self::CellKey >; } - impl< 'table, Table, RowKey, Row, CellKey, CellRepr > IntoAsTable - for AsTable< 'table, Table, RowKey, Row, CellKey, CellRepr > + impl< 'table, Table, RowKey, Row, CellKey> IntoAsTable + for AsTable< 'table, Table, RowKey, Row, CellKey> where RowKey : table::RowKey, - Row : Cells< CellKey, CellRepr >, + Row : Cells< CellKey>, CellKey : table::CellKey + ?Sized, - CellRepr : table::CellRepr, + // CellRepr : table::CellRepr, Self : Copy, { @@ -156,9 +156,9 @@ mod private type RowKey = RowKey; type Row = Row; type CellKey = CellKey; - type CellRepr = CellRepr; + // type CellRepr = CellRepr; - fn as_table( &self ) -> AsTable< '_, Self::Table, Self::RowKey, Self::Row, Self::CellKey, Self::CellRepr > + fn as_table( &self ) -> AsTable< '_, Self::Table, Self::RowKey, Self::Row, Self::CellKey > { *self } @@ -168,9 +168,9 @@ mod private // impl< Row > IntoAsTable // for Vec< Row > // where -// Row : Cells< Self::CellKey, Self::CellRepr >, +// Row : Cells< Self::CellKey >, // // CellKey : table::CellKey + ?Sized, -// // CellRepr : table::CellRepr, +// // // CellRepr : table::CellRepr, // { // // type Table = Self; @@ -179,14 +179,14 @@ mod private // type CellKey = str; // type CellRepr = WithRef; // -// fn as_table( &self ) -> AsTable< '_, Self::Table, Self::RowKey, Self::Row, Self::CellKey, Self::CellRepr > +// fn as_table( &self ) -> AsTable< '_, Self::Table, Self::RowKey, Self::Row, Self::CellKey > // { // AsTable::from( self ) // } // // } - // pub struct AsTable< 'table, Table, RowKey, Row, CellKey, CellRepr > + // pub struct AsTable< 'table, Table, RowKey, Row, CellKey> } diff --git a/module/core/format_tools/src/format/md_math.rs b/module/core/format_tools/src/format/md_math.rs index d4de6ae6af..196b0ee811 100644 --- a/module/core/format_tools/src/format/md_math.rs +++ b/module/core/format_tools/src/format/md_math.rs @@ -3,6 +3,8 @@ //! Provides functionality for converting multidimensional indices into flat offsets, //! useful for operations involving multidimensional arrays or grids. +// xxx : use crate mdmath + /// Internal namespace. mod private { diff --git a/module/core/format_tools/src/format/output_format.rs b/module/core/format_tools/src/format/output_format.rs index 511d12db79..69acca8515 100644 --- a/module/core/format_tools/src/format/output_format.rs +++ b/module/core/format_tools/src/format/output_format.rs @@ -1,6 +1,6 @@ //! Customizable format of printing table. //! -//! # Example of ordinary format +//! # Example of table format //! //! ```text //! sid | sname | gap @@ -74,14 +74,15 @@ mod private #[ inline( always ) ] fn default() -> Self { - super::ordinary::Ordinary::instance() + super::table::Table::instance() } } } -mod ordinary; +mod table; mod records; +mod keys; #[ allow( unused_imports ) ] pub use own::*; @@ -97,8 +98,9 @@ pub mod own #[ doc( inline ) ] pub use { - ordinary::Ordinary, + table::Table, records::Records, + keys::Keys, }; #[ doc( inline ) ] diff --git a/module/core/format_tools/src/format/output_format/keys.rs b/module/core/format_tools/src/format/output_format/keys.rs new file mode 100644 index 0000000000..55ee27b023 --- /dev/null +++ b/module/core/format_tools/src/format/output_format/keys.rs @@ -0,0 +1,107 @@ +//! Implement keys list output format. +//! +//! # Example +//! +//! ```text +//! ``` +//! + +use crate::*; +use print:: +{ + InputExtract, + Context, +}; +use core:: +{ + fmt, +}; +use std::sync::OnceLock; + +/// A struct representing the list of keys output format. +#[derive( Debug )] +pub struct Keys +{ + // /// Prefix added to each row. + // pub table_prefix : String, + // /// Postfix added to each row. + // pub table_postfix : String, + // /// Separator used between rows. + // pub table_separator : String, + // /// Prefix added to each row. + // pub row_prefix : String, + // /// Postfix added to each row. + // pub row_postfix : String, + // /// Separator used between rows. + // pub row_separator : String, + // /// Prefix added to each cell. + // pub cell_prefix : String, + // /// Postfix added to each cell. + // pub cell_postfix : String, + // /// Separator used between table columns. + // pub cell_separator : String, +} + +impl Keys +{ + /// Returns a reference to a static instance of `Keys`. + pub fn instance() -> &'static dyn TableOutputFormat + { + static INSTANCE : OnceLock< Keys > = OnceLock::new(); + INSTANCE.get_or_init( || Keys::default() ) + } +} + +impl Default for Keys +{ + fn default() -> Self + { + + // 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 table_prefix = "".to_string(); + // let table_postfix = "".to_string(); + // let table_separator = "\n".to_string(); + + Self + { + // table_prefix, + // table_postfix, + // table_separator, + // row_prefix, + // row_postfix, + // row_separator, + // cell_prefix, + // cell_postfix, + // cell_separator, + } + } +} + +impl TableOutputFormat for Keys +{ + + fn extract_write< 'buf, 'data >( + &self, + x : &InputExtract< 'data >, + c : &mut Context< 'buf >, + ) -> fmt::Result + { + + // dbg!( &x ); + + for col in &x.col_descriptors + { + write!( c.buf, " - {}\n", col.label )?; + } + + write!( c.buf, " {} fields\n", x.col_descriptors.len() )?; + + Ok(()) + } + +} diff --git a/module/core/format_tools/src/format/output_format/ordinary.rs b/module/core/format_tools/src/format/output_format/table.rs similarity index 93% rename from module/core/format_tools/src/format/output_format/ordinary.rs rename to module/core/format_tools/src/format/output_format/table.rs index 23606111f3..579ba6c783 100644 --- a/module/core/format_tools/src/format/output_format/ordinary.rs +++ b/module/core/format_tools/src/format/output_format/table.rs @@ -24,7 +24,7 @@ use std::sync::OnceLock; /// A struct representing the classic table output format. /// -/// `Ordinary` provides a standard implementation for table formatting, +/// `Table` provides a standard implementation for table formatting, /// supporting a classic style with default settings. /// /// # Example @@ -37,7 +37,7 @@ use std::sync::OnceLock; /// 10 | Boris | 5 /// ``` #[ derive( Debug ) ] -pub struct Ordinary +pub struct Table { /// Delimitting header with grid line or not. pub delimitting_header : bool, @@ -77,7 +77,7 @@ pub struct Ordinary pub corner_rb : char, } -impl Default for Ordinary +impl Default for Table { fn default() -> Self { @@ -127,30 +127,30 @@ impl Default for Ordinary } } -impl Default for &'static Ordinary +impl Default for &'static Table { fn default() -> Self { // qqq : find a better solution - static STYLES : OnceLock< Ordinary > = OnceLock::new(); + static STYLES : OnceLock< Table > = OnceLock::new(); STYLES.get_or_init( || { - Ordinary::default() + Table::default() }) } } -impl Ordinary +impl Table { - /// Returns a reference to a static instance of `Ordinary`. + /// Returns a reference to a static instance of `Table`. /// - /// This method provides access to a single shared instance of `Ordinary`, + /// This method provides access to a single shared instance of `Table`, /// ensuring efficient reuse of the classic table output format. pub fn instance() -> & 'static dyn TableOutputFormat { - static INSTANCE : OnceLock< Ordinary > = OnceLock::new(); + static INSTANCE : OnceLock< Table > = OnceLock::new(); INSTANCE.get_or_init( || { Self::default() @@ -159,7 +159,7 @@ impl Ordinary } } -impl TableOutputFormat for Ordinary +impl TableOutputFormat for Table { fn extract_write< 'buf, 'data >( &self, x : &InputExtract< 'data >, c : &mut Context< 'buf > ) -> fmt::Result { diff --git a/module/core/format_tools/src/format/print.rs b/module/core/format_tools/src/format/print.rs index 29437961b8..858bd7dd57 100644 --- a/module/core/format_tools/src/format/print.rs +++ b/module/core/format_tools/src/format/print.rs @@ -166,7 +166,7 @@ mod private /// A `String` containing the formatted table. fn table_to_string( &'data self ) -> String { - self.table_to_string_with_format( &output_format::Ordinary::default() ) + self.table_to_string_with_format( &output_format::Table::default() ) } /// Converts the table to a string representation specifying printer. @@ -197,15 +197,15 @@ mod private } /// A trait for formatting tables. - impl< 'data, T, RowKey, Row, CellKey, CellRepr > TableFormatter< 'data > - for AsTable< 'data, T, RowKey, Row, CellKey, CellRepr > + impl< 'data, T, RowKey, Row, CellKey> TableFormatter< 'data > + for AsTable< 'data, T, RowKey, Row, CellKey> where - Self : TableRows< CellKey = CellKey, CellRepr = CellRepr, RowKey = RowKey, Row = Row >, + Self : TableRows< CellKey = CellKey, RowKey = RowKey, Row = Row >, Self : TableHeader< CellKey = CellKey >, RowKey : table::RowKey, - Row : Cells< CellKey, CellRepr >, + Row : Cells< CellKey>, CellKey : table::CellKey + ?Sized, - CellRepr : table::CellRepr, + // CellRepr : table::CellRepr, { fn fmt< 'a >( &'data self, c : &mut Context< 'a > ) -> fmt::Result @@ -365,7 +365,7 @@ mod private } } /// Extract input data from and collect it in a format consumable by output formatter. - pub fn extract< 't, 'context, Table, RowKey, Row, CellKey, CellRepr > + pub fn extract< 't, 'context, Table, RowKey, Row, CellKey> ( table : &'t Table, filter_col : &'context ( dyn FilterCol + 'context ), @@ -376,12 +376,12 @@ mod private where 'data : 't, // 't : 'data, - Table : TableRows< RowKey = RowKey, Row = Row, CellKey = CellKey, CellRepr = CellRepr >, + Table : TableRows< RowKey = RowKey, Row = Row, CellKey = CellKey >, Table : TableHeader< CellKey = CellKey >, RowKey : table::RowKey, - Row : Cells< CellKey, CellRepr > + 'data, + Row : Cells< CellKey> + 'data, CellKey : table::CellKey + ?Sized + 'data, - CellRepr : table::CellRepr, + // CellRepr : table::CellRepr, { use md_math::MdOffset; @@ -508,7 +508,7 @@ mod private | ( key, val ) | { - let val = match val.0 + let val = match val { Some( val ) => { @@ -583,7 +583,10 @@ mod private slices[ x.slices_dim.md_offset( md_index ) ] = s; }) ; - x.col_descriptors[ icol ].label = cell.0.as_ref(); + if irow == 0 + { + x.col_descriptors[ icol ].label = cell.0.as_ref(); + } } } diff --git a/module/core/format_tools/src/format/table.rs b/module/core/format_tools/src/format/table.rs index 9ea6b1aecd..d3dc8bd71c 100644 --- a/module/core/format_tools/src/format/table.rs +++ b/module/core/format_tools/src/format/table.rs @@ -12,7 +12,7 @@ mod private // fmt, borrow::Borrow, }; - // use std::borrow::Cow; + use std::borrow::Cow; use reflect_tools:: { IteratorTrait, @@ -78,20 +78,21 @@ mod private // = /// A trait for iterating over all cells of a row. - pub trait Cells< CellKey, CellRepr > + pub trait Cells< CellKey > where - CellRepr : table::CellRepr, + // CellRepr : table::CellRepr, CellKey : table::CellKey + ?Sized, { /// Returns an iterator over all cells of the row. - fn cells< 'a, 'b >( &'a self ) -> impl IteratorTrait< Item = ( &'b CellKey, OptionalCow< 'b, str, CellRepr > ) > + // fn cells< 'a, 'b >( &'a self ) -> impl IteratorTrait< Item = ( &'b CellKey, OptionalCow< 'b, str > ) > + fn cells< 'a, 'b >( &'a self ) -> impl IteratorTrait< Item = ( &'b CellKey, Option< Cow< 'b, str > > ) > where 'a : 'b, CellKey : 'b, ; } - impl< Row, CellKey, CellRepr > Cells< CellKey, CellRepr > + impl< Row, CellKey > Cells< CellKey > for Row where CellKey : table::CellKey + ?Sized, @@ -99,14 +100,17 @@ mod private Row : TableWithFields + Fields < &'ckv CellKey, - OptionalCow< 'ckv, str, CellRepr >, + // OptionalCow< 'ckv, str >, + Option< Cow< 'ckv, str > >, Key< 'ckv > = &'ckv CellKey, - Val< 'ckv > = OptionalCow< 'ckv, str, CellRepr >, + // Val< 'ckv > = OptionalCow< 'ckv, str >, + Val< 'ckv > = Option< Cow< 'ckv, str > >, > + 'ckv, // xxx - CellRepr : table::CellRepr, + // CellRepr : table::CellRepr, { - fn cells< 'a, 'b >( &'a self ) -> impl IteratorTrait< Item = ( &'b CellKey, OptionalCow< 'b, str, CellRepr > ) > + // fn cells< 'a, 'b >( &'a self ) -> impl IteratorTrait< Item = ( &'b CellKey, OptionalCow< 'b, str > ) > + fn cells< 'a, 'b >( &'a self ) -> impl IteratorTrait< Item = ( &'b CellKey, Option< Cow< 'b, str > > ) > where 'a : 'b, CellKey : 'b, @@ -154,15 +158,15 @@ mod private /// /// The type representing a row, which must implement `Cells` /// for the specified `CellKey` and `CellRepr`. - type Row : Cells< Self::CellKey, Self::CellRepr >; + type Row : Cells< Self::CellKey >; /// /// 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; + // /// 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( &self ) -> impl IteratorTrait< Item = &Self::Row >; @@ -171,9 +175,8 @@ mod private // Self::Row : 'a; } - impl< T, RowKey, Row, CellKey, CellRepr > - TableRows<> - for AsTable< '_, T, RowKey, Row, CellKey, CellRepr > + impl< T, RowKey, Row, CellKey > TableRows<> + for AsTable< '_, T, RowKey, Row, CellKey > where for< 'k, 'v > T : Fields @@ -185,14 +188,14 @@ mod private > + 'k + 'v, RowKey : table::RowKey, - Row : TableWithFields + Cells< CellKey, CellRepr >, + Row : TableWithFields + Cells< CellKey >, CellKey : table::CellKey + ?Sized, - CellRepr : table::CellRepr, + // CellRepr : table::CellRepr, { type RowKey = RowKey; type Row = Row; type CellKey = CellKey; - type CellRepr = CellRepr; + // type CellRepr = CellRepr; fn rows( &self ) -> impl IteratorTrait< Item = &Self::Row > // fn rows< 'a >( &'a self ) -> impl IteratorTrait< Item = &'a Self::Row > @@ -217,14 +220,14 @@ mod private // fn mcells( &self ) -> [ usize ; 2 ]; // } // -// impl< T, RowKey, Row, CellKey, CellRepr > TableSize -// for AsTable< '_, T, RowKey, Row, CellKey, CellRepr > +// impl< T, RowKey, Row, CellKey > TableSize +// for AsTable< '_, T, RowKey, Row, CellKey > // where -// Self : TableRows< RowKey = RowKey, Row = Row, CellKey = CellKey, CellRepr = CellRepr >, +// Self : TableRows< RowKey = RowKey, Row = Row, CellKey = CellKey >, // RowKey : table::RowKey, -// Row : Cells< CellKey, CellRepr >, +// Row : Cells< CellKey >, // CellKey : table::CellKey + ?Sized, -// CellRepr : table::CellRepr, +// // CellRepr : table::CellRepr, // { // fn mcells( &self ) -> [ usize ; 2 ] // { @@ -256,14 +259,14 @@ mod private fn header( &self ) -> Option< impl IteratorTrait< Item = ( &Self::CellKey, &'_ str ) > >; } - impl< T, RowKey, Row, CellKey, CellRepr > TableHeader - for AsTable< '_, T, RowKey, Row, CellKey, CellRepr > + impl< T, RowKey, Row, CellKey > TableHeader + for AsTable< '_, T, RowKey, Row, CellKey > where - Self : TableRows< RowKey = RowKey, Row = Row, CellKey = CellKey, CellRepr = CellRepr >, + Self : TableRows< RowKey = RowKey, Row = Row, CellKey = CellKey >, RowKey : table::RowKey, - Row : TableWithFields + Cells< CellKey, CellRepr >, + Row : TableWithFields + Cells< CellKey >, CellKey : table::CellKey + ?Sized, - CellRepr : table::CellRepr, + // CellRepr : table::CellRepr, { type CellKey = CellKey; diff --git a/module/core/format_tools/src/format/test_object_without_impl.rs b/module/core/format_tools/src/format/test_object_without_impl.rs new file mode 100644 index 0000000000..f61b3fe588 --- /dev/null +++ b/module/core/format_tools/src/format/test_object_without_impl.rs @@ -0,0 +1,155 @@ +//! A strucutre for diagnostic and demonstration purpose. + +// use super::*; + +// use crate:: +// { +// Fields, +// IteratorTrait, +// TableWithFields, +// WithRef, +// OptionalCow, +// }; + +use std:: +{ + collections::HashMap, + hash::Hasher, + hash::Hash, + cmp::Ordering, + // borrow::Cow, +}; + +/// Struct representing a test object with various fields. +#[ derive( Clone, Debug, PartialEq, Eq ) ] +pub struct TestObjectWithoutImpl +{ + pub id : String, + pub created_at : i64, + pub file_ids : Vec< String >, + pub tools : Option< Vec< HashMap< String, String > > >, +} + +// TableWithFields is not implemented for TestObjectWithoutImpl intentionally + +// impl TableWithFields for TestObjectWithoutImpl {} +// +// impl Fields< &'_ str, Option< Cow< '_, str > > > +// for TestObjectWithoutImpl +// { +// type Key< 'k > = &'k str; +// type Val< 'v > = Option< Cow< 'v, str > >; +// +// fn fields( &self ) -> impl IteratorTrait< Item = ( &'_ str, Option< Cow< '_, str > > ) > +// { +// use format_tools::ref_or_display_or_debug_multiline::field; +// // use format_tools::ref_or_display_or_debug::field; +// let mut dst : Vec< ( &'_ str, Option< Cow< '_, str > > ) > = 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", Option::None ) ); +// } +// +// dst.into_iter() +// } +// +// } + +impl Hash for TestObjectWithoutImpl +{ + + fn hash< H: Hasher >( &self, state: &mut H ) + { + self.id.hash( state ); + self.created_at.hash( state ); + self.file_ids.hash( state ); + + if let Some( tools ) = &self.tools + { + for tool in tools + { + for ( key, value ) in tool + { + key.hash( state ); + value.hash( state ); + } + } + } + else + { + state.write_u8( 0 ); + } + } + +} + +impl PartialOrd for TestObjectWithoutImpl +{ + + fn partial_cmp( &self, other: &Self ) -> Option< Ordering > + { + Some( self.cmp( other ) ) + } + +} + +impl Ord for TestObjectWithoutImpl +{ + + fn cmp( &self, other: &Self ) -> Ordering + { + self.id + .cmp( &other.id ) + .then_with( | | self.created_at.cmp( &other.created_at ) ) + .then_with( | | self.file_ids.cmp( &other.file_ids ) ) + } + +} + +/// Generate a dynamic array of test objects. +pub fn test_objects_gen() -> Vec< TestObjectWithoutImpl > +{ + + vec! + [ + TestObjectWithoutImpl + { + id : "1".to_string(), + created_at : 1627845583, + file_ids : vec![ "file1".to_string(), "file2".to_string() ], + tools : None + }, + TestObjectWithoutImpl + { + 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 + } + ] + ), + }, + ] + +} diff --git a/module/core/format_tools/tests/inc/collection_test.rs b/module/core/format_tools/tests/inc/collection_test.rs index 7e1607820e..6b17ce0975 100644 --- a/module/core/format_tools/tests/inc/collection_test.rs +++ b/module/core/format_tools/tests/inc/collection_test.rs @@ -6,7 +6,7 @@ use the_module:: AsTable, TableRows, WithRef, - print, + // the_module::print, }; use std:: @@ -56,14 +56,14 @@ fn dlist_basic() }; use the_module::TableFormatter; - let _as_table : AsTable< '_, Vec< TestObject >, &str, TestObject, str, WithRef > = AsTable::new( &data ); + let _as_table : AsTable< '_, Vec< TestObject >, &str, TestObject, str> = AsTable::new( &data ); let as_table = AsTable::new( &data ); let rows = TableRows::rows( &as_table ); assert_eq!( rows.len(), 2 ); let mut output = String::new(); - let mut context = print::Context::new( &mut output, Default::default() ); + let mut context = the_module::print::Context::new( &mut output, Default::default() ); let _got = the_module::TableFormatter::fmt( &as_table, &mut context ); let got = as_table.table_to_string(); assert!( got.contains( "│ id │ created_at │ file_ids │ tools │" ) ); @@ -112,14 +112,14 @@ fn hmap_basic() }; use the_module::TableFormatter; - let _as_table : AsTable< '_, HashMap< &str, TestObject >, &str, TestObject, str, WithRef > = AsTable::new( &data ); + let _as_table : AsTable< '_, HashMap< &str, TestObject >, &str, TestObject, str> = AsTable::new( &data ); let as_table = AsTable::new( &data ); let rows = TableRows::rows( &as_table ); assert_eq!( rows.len(), 2 ); let mut output = String::new(); - let mut context = print::Context::new( &mut output, Default::default() ); + let mut context = the_module::print::Context::new( &mut output, Default::default() ); let _got = the_module::TableFormatter::fmt( &as_table, &mut context ); let got = as_table.table_to_string(); assert!( got.contains( "│ id │ created_at │ file_ids │ tools │" ) ); @@ -168,14 +168,14 @@ fn bmap_basic() }; use the_module::TableFormatter; - let _as_table : AsTable< '_, Bmap< &str, TestObject >, &str, TestObject, str, WithRef > = AsTable::new( &data ); + let _as_table : AsTable< '_, Bmap< &str, TestObject >, &str, TestObject, str> = AsTable::new( &data ); let as_table = AsTable::new( &data ); let rows = TableRows::rows( &as_table ); assert_eq!( rows.len(), 2 ); let mut output = String::new(); - let mut context = print::Context::new( &mut output, Default::default() ); + let mut context = the_module::print::Context::new( &mut output, Default::default() ); let _got = the_module::TableFormatter::fmt( &as_table, &mut context ); let got = as_table.table_to_string(); assert!( got.contains( "│ id │ created_at │ file_ids │ tools │" ) ); @@ -222,14 +222,14 @@ fn bset_basic() }; use the_module::TableFormatter; - let _as_table : AsTable< '_, BTreeSet< TestObject >, &str, TestObject, str, WithRef > = AsTable::new( &data ); + let _as_table : AsTable< '_, BTreeSet< TestObject >, &str, TestObject, str> = AsTable::new( &data ); let as_table = AsTable::new( &data ); let rows = TableRows::rows( &as_table ); assert_eq!( rows.len(), 2 ); let mut output = String::new(); - let mut context = print::Context::new( &mut output, Default::default() ); + let mut context = the_module::print::Context::new( &mut output, Default::default() ); let _got = the_module::TableFormatter::fmt( &as_table, &mut context ); let got = as_table.table_to_string(); assert!( got.contains( "│ id │ created_at │ file_ids │ tools │" ) ); @@ -276,14 +276,14 @@ fn deque_basic() }; use the_module::TableFormatter; - let _as_table : AsTable< '_, VecDeque< TestObject >, &str, TestObject, str, WithRef > = AsTable::new( &data ); + let _as_table : AsTable< '_, VecDeque< TestObject >, &str, TestObject, str> = AsTable::new( &data ); let as_table = AsTable::new( &data ); let rows = TableRows::rows( &as_table ); assert_eq!( rows.len(), 2 ); let mut output = String::new(); - let mut context = print::Context::new( &mut output, Default::default() ); + let mut context = the_module::print::Context::new( &mut output, Default::default() ); let _got = the_module::TableFormatter::fmt( &as_table, &mut context ); let got = as_table.table_to_string(); assert!( got.contains( "│ id │ created_at │ file_ids │ tools │" ) ); @@ -328,16 +328,16 @@ fn hset_basic() ), }, }; - + use the_module::TableFormatter; - let _as_table : AsTable< '_, HashSet< TestObject >, &str, TestObject, str, WithRef > = AsTable::new( &data ); + let _as_table : AsTable< '_, HashSet< TestObject >, &str, TestObject, str> = AsTable::new( &data ); let as_table = AsTable::new( &data ); let rows = TableRows::rows( &as_table ); assert_eq!( rows.len(), 2 ); let mut output = String::new(); - let mut context = print::Context::new( &mut output, Default::default() ); + let mut context = the_module::print::Context::new( &mut output, Default::default() ); let _got = the_module::TableFormatter::fmt( &as_table, &mut context ); let got = as_table.table_to_string(); assert!( got.contains( "│ id │ created_at │ file_ids │ tools │" ) ); @@ -384,14 +384,14 @@ fn llist_basic() }; use the_module::TableFormatter; - let _as_table : AsTable< '_, LinkedList< TestObject >, &str, TestObject, str, WithRef > = AsTable::new( &data ); + let _as_table : AsTable< '_, LinkedList< TestObject >, &str, TestObject, str> = AsTable::new( &data ); let as_table = AsTable::new( &data ); let rows = TableRows::rows( &as_table ); assert_eq!( rows.len(), 2 ); let mut output = String::new(); - let mut context = print::Context::new( &mut output, Default::default() ); + let mut context = the_module::print::Context::new( &mut output, Default::default() ); let _got = the_module::TableFormatter::fmt( &as_table, &mut context ); let got = as_table.table_to_string(); assert!( got.contains( "│ id │ created_at │ file_ids │ tools │" ) ); diff --git a/module/core/format_tools/tests/inc/fields_test.rs b/module/core/format_tools/tests/inc/fields_test.rs index 48ba295e20..32d921bed0 100644 --- a/module/core/format_tools/tests/inc/fields_test.rs +++ b/module/core/format_tools/tests/inc/fields_test.rs @@ -26,16 +26,16 @@ pub struct TestObject pub tools : Option< Vec< HashMap< String, String > > >, } -impl Fields< &'static str, OptionalCow< '_, str, WithRef > > +impl Fields< &'_ str, Option< Cow< '_, str > > > for TestObject { - type Key< 'k > = &'static str; - type Val< 'v > = OptionalCow< 'v, str, WithRef >; + type Key< 'k > = &'k str; + type Val< 'v > = Option< Cow< 'v, str > >; - fn fields( &self ) -> impl IteratorTrait< Item = ( &'static str, OptionalCow< '_, str, WithRef > ) > + fn fields( &self ) -> impl IteratorTrait< Item = ( &'_ str, Option< Cow< '_, str > > ) > { use format_tools::ref_or_display_or_debug::field; - let mut dst : Vec< ( &'static str, OptionalCow< '_, str, WithRef > ) > = Vec::new(); + let mut dst : Vec< ( &'_ str, Option< Cow< '_, str > > ) > = Vec::new(); dst.push( field!( &self.id ) ); dst.push( field!( &self.created_at ) ); @@ -47,11 +47,17 @@ for TestObject } else { - dst.push( ( "tools", OptionalCow::none() ) ); + dst.push( ( "tools", Option::None ) ); } dst.into_iter() } + +} + +pub fn is_borrowed( cow : &Option< Cow< '_, str > > ) -> bool +{ + matches!( cow, Some( Cow::Borrowed( _ ) ) ) } // @@ -76,16 +82,16 @@ fn basic_with_ref_display_debug() ), }; - let fields : Vec< ( &str, OptionalCow< '_, str, WithRef > ) > = - Fields::< &'static str, OptionalCow< '_, str, WithRef > >::fields( &test_object ).collect(); + let fields : Vec< ( &str, Option< Cow< '_, str > > ) > = + Fields::< &'static str, Option< Cow< '_, str > > >::fields( &test_object ).collect(); - // let fields : Vec< ( &str, OptionalCow< '_, str, WithRef > ) > = test_object.fields().collect(); + // let fields : Vec< ( &str, Option< Cow< '_, str > > ) > = test_object.fields().collect(); assert_eq!( fields.len(), 4 ); - assert!( fields[ 0 ].1.is_borrowed() ); - assert!( !fields[ 1 ].1.is_borrowed() ); - assert!( !fields[ 2 ].1.is_borrowed() ); - assert!( !fields[ 3 ].1.is_borrowed() ); + assert!( is_borrowed( &fields[ 0 ].1 ) ); + assert!( !is_borrowed( &fields[ 1 ].1 ) ); + assert!( !is_borrowed( &fields[ 2 ].1 ) ); + assert!( !is_borrowed( &fields[ 3 ].1 ) ); assert_eq!( fields[ 0 ], ( "id", Some( Cow::Borrowed( "12345" ) ).into() ) ); assert_eq!( fields[ 0 ], ( "id", Some( Cow::Owned( "12345".to_string() ) ).into() ) ); assert_eq!( fields[ 1 ], ( "created_at", Some( Cow::Owned( "1627845583".to_string() ) ).into() ) ); diff --git a/module/core/format_tools/tests/inc/format_records_test.rs b/module/core/format_tools/tests/inc/format_records_test.rs index 8325dea020..72f23a5ff5 100644 --- a/module/core/format_tools/tests/inc/format_records_test.rs +++ b/module/core/format_tools/tests/inc/format_records_test.rs @@ -23,7 +23,7 @@ fn basic() { let test_objects = test_object::test_objects_gen(); - let _as_table : AsTable< '_, Vec< test_object::TestObject >, usize, test_object::TestObject, str, WithRef > = AsTable::new( &test_objects ); + let _as_table : AsTable< '_, Vec< test_object::TestObject >, usize, test_object::TestObject, str> = AsTable::new( &test_objects ); let as_table = AsTable::new( &test_objects ); let mut output = String::new(); diff --git a/module/core/format_tools/tests/inc/format_ordinary_test.rs b/module/core/format_tools/tests/inc/format_table_test.rs similarity index 96% rename from module/core/format_tools/tests/inc/format_ordinary_test.rs rename to module/core/format_tools/tests/inc/format_table_test.rs index 5dbe2a95d2..eb8a3b17dd 100644 --- a/module/core/format_tools/tests/inc/format_ordinary_test.rs +++ b/module/core/format_tools/tests/inc/format_table_test.rs @@ -23,7 +23,7 @@ fn basic() { let test_objects = test_object::test_objects_gen(); - let _as_table : AsTable< '_, Vec< test_object::TestObject >, usize, test_object::TestObject, str, WithRef > = AsTable::new( &test_objects ); + let _as_table : AsTable< '_, Vec< test_object::TestObject >, usize, test_object::TestObject, str> = AsTable::new( &test_objects ); let as_table = AsTable::new( &test_objects ); let mut output = String::new(); @@ -70,7 +70,7 @@ fn table_to_string() // with explicit arguments - let as_table : AsTable< '_, Vec< test_object::TestObject >, usize, test_object::TestObject, str, WithRef > = AsTable::new( &test_objects ); + let as_table : AsTable< '_, Vec< test_object::TestObject >, usize, test_object::TestObject, str> = AsTable::new( &test_objects ); let table_string = as_table.table_to_string(); println!( "\ntable_string\n{table_string}" ); assert!( table_string.contains( "id" ) ); @@ -99,7 +99,7 @@ fn custom_format() // use the_module::TableFormatter; let test_objects = test_object::test_objects_gen(); - let mut format = output_format::Ordinary::default(); + let mut format = output_format::Table::default(); format.cell_prefix = "( ".into(); format.cell_postfix = " )".into(); format.cell_separator = "|".into(); @@ -140,7 +140,7 @@ fn custom_format() use the_module::TableFormatter; - let mut format = output_format::Ordinary::default(); + let mut format = output_format::Table::default(); format.cell_prefix = "( ".into(); format.cell_postfix = " )".into(); format.cell_separator = "|".into(); @@ -175,7 +175,7 @@ fn filter_col_none() { let test_objects = test_object::test_objects_gen(); - let mut format = output_format::Ordinary::default(); + let mut format = output_format::Table::default(); format.cell_prefix = "( ".into(); format.cell_postfix = " )".into(); format.cell_separator = "|".into(); @@ -210,7 +210,7 @@ fn filter_col_callback() { let test_objects = test_object::test_objects_gen(); - let mut format = output_format::Ordinary::default(); + let mut format = output_format::Table::default(); format.cell_prefix = "( ".into(); format.cell_postfix = " )".into(); format.cell_separator = "|".into(); @@ -254,7 +254,7 @@ fn filter_row_none() { let test_objects = test_object::test_objects_gen(); - let mut format = output_format::Ordinary::default(); + let mut format = output_format::Table::default(); format.cell_prefix = "( ".into(); format.cell_postfix = " )".into(); format.cell_separator = "|".into(); @@ -286,7 +286,7 @@ fn filter_row_callback() { let test_objects = test_object::test_objects_gen(); - let mut format = output_format::Ordinary::default(); + let mut format = output_format::Table::default(); format.cell_prefix = "( ".into(); format.cell_postfix = " )".into(); format.cell_separator = "|".into(); diff --git a/module/core/format_tools/tests/inc/mod.rs b/module/core/format_tools/tests/inc/mod.rs index 5188190547..5f35b24fcc 100644 --- a/module/core/format_tools/tests/inc/mod.rs +++ b/module/core/format_tools/tests/inc/mod.rs @@ -1,18 +1,19 @@ -#[ allow( unused_imports ) ] use super::*; #[ cfg( feature = "enabled" ) ] #[ path = "." ] mod fundamental { - #[ allow( unused_imports ) ] use super::*; mod test_object; mod table_test; - mod format_ordinary_test; + mod tabe_foreign_test; + + mod format_table_test; mod format_records_test; + // mod format_keys_test; // qqq : xxx : implement mod collection_test; mod fields_test; diff --git a/module/core/format_tools/tests/inc/tabe_foreign_test.rs b/module/core/format_tools/tests/inc/tabe_foreign_test.rs new file mode 100644 index 0000000000..6cbfd68249 --- /dev/null +++ b/module/core/format_tools/tests/inc/tabe_foreign_test.rs @@ -0,0 +1,131 @@ +use super::*; + +use the_module:: +{ + AsTable, + Cells, + TableRows, + TableHeader, + WithRef, +}; + +use std:: +{ + borrow::Cow, +}; + +// + +#[ test ] +fn iterator_over_objects_without_impl() +{ + use the_module::TestObjectWithoutImpl as TestObjectWithoutImpl; + use the_module:: + { + Fields, + IteratorTrait, + TableWithFields, + WithRef, + OptionalCow, + output_format, + }; + + // xxx : Clone should not be required + #[ derive( Debug, Clone ) ] + pub struct TestObjecWrap( TestObjectWithoutImpl ); + + impl TableWithFields for TestObjecWrap {} + impl Fields< &'_ str, Option< Cow< '_, str > > > + for TestObjecWrap + { + type Key< 'k > = &'k str; + type Val< 'v > = Option< Cow< 'v, str > >; + + fn fields( &self ) -> impl IteratorTrait< Item = ( &'_ str, Option< Cow< '_, str > > ) > + { + use format_tools::ref_or_display_or_debug_multiline::field; + let mut dst = Vec::new(); + + dst.push( field!( &self.0.id ) ); + dst.push( field!( &self.0.created_at ) ); + dst.push( field!( &self.0.file_ids ) ); + + if let Some( tools ) = &self.0.tools + { + dst.push( field!( tools ) ); + } + else + { + dst.push( ( "tools", Option::None ) ); + } + + dst.into_iter() + } + + } + + let data : collection_tools::Vec< TestObjecWrap > = the_module::test_objects_gen() + .into_iter() + .map( | e | TestObjecWrap( e ) ) + .collect() + ; + + use the_module::TableFormatter; + let _as_table : AsTable< '_, Vec< TestObjecWrap >, &str, TestObjecWrap, str > = AsTable::new( &data ); + let as_table = AsTable::new( &data ); + + let rows = TableRows::rows( &as_table ); + assert_eq!( rows.len(), 2 ); + + let mut output = String::new(); + let mut context = the_module::print::Context::new( &mut output, Default::default() ); + let _result = the_module::TableFormatter::fmt( &as_table, &mut context ); + + // = output as table + + let got = as_table.table_to_string(); + assert!( got.contains( "│ id │ created_at │ file_ids │ tools │" ) ); + assert!( got.contains( "│ 13 │ [ │ [ │" ) ); + assert!( got.contains( "│ 1627845583 │ [ │ │" ) ); + + let got = AsTable::new( &data ).table_to_string(); + assert!( got.contains( "│ id │ created_at │ file_ids │ tools │" ) ); + assert!( got.contains( "│ 13 │ [ │ [ │" ) ); + assert!( got.contains( "│ 1627845583 │ [ │ │" ) ); + + let got = AsTable::new( &data ).table_to_string_with_format( &output_format::Table::default() ); + println!( "{}", &got ); + assert!( got.contains( "│ id │ created_at │ file_ids │ tools │" ) ); + assert!( got.contains( "│ 13 │ [ │ [ │" ) ); + assert!( got.contains( "│ 1627845583 │ [ │ │" ) ); + + // = output as records + + // let format = output_format::Records::default(); + let mut output = String::new(); + let format = the_module::output_format::Records::default(); + let printer = the_module::print::Printer::with_format( &format ); + let mut context = the_module::print::Context::new( &mut output, printer ); + let got = the_module::TableFormatter::fmt( &as_table, &mut context ); + assert!( got.is_ok() ); + + let got = AsTable::new( &data ).table_to_string_with_format( &output_format::Records::default() ); + println!( "{}", &got ); + assert!( got.contains( "│ id │ 1 │" ) ); + assert!( got.contains( "│ created_at │ 1627845583 │" ) ); + assert!( got.contains( "│ id │ 2 │" ) ); + assert!( got.contains( "│ created_at │ 13 │" ) ); + + // = output as keys + + let got = AsTable::new( &data ).table_to_string_with_format( &output_format::Keys::default() ); + println!( "{}", &got ); + assert!( got.contains( "- id" ) ); + assert!( got.contains( "- created_at" ) ); + assert!( got.contains( "- file_ids" ) ); + assert!( got.contains( "- tools" ) ); + assert!( got.contains( "4 fields" ) ); + + // assert!( false ); + +} diff --git a/module/core/format_tools/tests/inc/table_test.rs b/module/core/format_tools/tests/inc/table_test.rs index 2357d44aea..c5fd38a8e9 100644 --- a/module/core/format_tools/tests/inc/table_test.rs +++ b/module/core/format_tools/tests/inc/table_test.rs @@ -10,6 +10,11 @@ use the_module:: WithRef, }; +use std:: +{ + borrow::Cow, +}; + // #[ test ] @@ -19,13 +24,13 @@ fn basic() { let test_objects = test_object::test_objects_gen(); - let cells = Cells::< str, WithRef >::cells( &test_objects[ 0 ] ); + let cells = Cells::< str>::cells( &test_objects[ 0 ] ); assert_eq!( cells.len(), 4 ); - let cells = Cells::< str, WithRef >::cells( &test_objects[ 1 ] ); + let cells = Cells::< str>::cells( &test_objects[ 1 ] ); assert_eq!( cells.len(), 4 ); drop( cells ); - let as_table : AsTable< '_, Vec< test_object::TestObject >, usize, test_object::TestObject, str, WithRef > = AsTable::new( &test_objects ); + let as_table : AsTable< '_, Vec< test_object::TestObject >, usize, test_object::TestObject, str> = AsTable::new( &test_objects ); // let mcells = TableSize::mcells( &as_table ); // assert_eq!( mcells, [ 4, 3 ] ); let rows = TableRows::rows( &as_table ); @@ -45,3 +50,251 @@ fn basic() dbg!( header.collect::< Vec< _ > >() ); } + +// + +#[ test ] +fn iterator_over_optional_cow() +{ + // use test_object::TestObject2 as TestObject2; + use the_module:: + { + Fields, + IteratorTrait, + TableWithFields, + WithRef, + OptionalCow, + }; + + /// Struct representing a test object with various fields. + #[ derive( Clone, Debug, PartialEq, Eq ) ] + pub struct TestObject2 + { + pub id : String, + pub created_at : i64, + pub file_ids : Vec< String >, + pub tools : Option< Vec< HashMap< String, String > > >, + } + + impl TableWithFields for TestObject2 {} + + impl Fields< &'_ str, Option< Cow< '_, str > > > + for TestObject2 + { + type Key< 'k > = &'k str; + type Val< 'v > = Option< Cow< 'v, str > >; + + fn fields( &self ) -> impl IteratorTrait< Item = ( &'_ str, Option< Cow< '_, str > > ) > + { + use format_tools::ref_or_display_or_debug_multiline::field; + // use format_tools::ref_or_display_or_debug::field; + let mut dst : Vec< ( &'_ str, Option< Cow< '_, str > > ) > = Vec::new(); + + // trace_macros!( true ); + dst.push( field!( &self.id ) ); + // trace_macros!( false ); + + 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", Option::None ) ); + } + + dst.into_iter() + } + + } + + let data : collection_tools::Vec< TestObject2 > = dlist! + { + TestObject2 + { + id : "1".to_string(), + created_at : 1627845583, + file_ids : vec![ "file1".to_string(), "file2".to_string() ], + tools : None + }, + TestObject2 + { + 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 + } + ] + ), + }, + }; + + use the_module::TableFormatter; + let _as_table : AsTable< '_, Vec< TestObject2 >, &str, TestObject2, str> = AsTable::new( &data ); + let as_table = AsTable::new( &data ); + + let rows = TableRows::rows( &as_table ); + assert_eq!( rows.len(), 2 ); + + let mut output = String::new(); + let mut context = the_module::print::Context::new( &mut output, Default::default() ); + let _got = the_module::TableFormatter::fmt( &as_table, &mut context ); + let got = as_table.table_to_string(); + assert!( got.contains( "│ id │ created_at │ file_ids │ tools │" ) ); + assert!( got.contains( "│ 13 │ [ │ [ │" ) ); + assert!( got.contains( "│ 1627845583 │ [ │ │" ) ); + + let got = AsTable::new( &data ).table_to_string(); + assert!( got.contains( "│ id │ created_at │ file_ids │ tools │" ) ); + assert!( got.contains( "│ 13 │ [ │ [ │" ) ); + assert!( got.contains( "│ 1627845583 │ [ │ │" ) ); + +} + +// + +#[ test ] +fn iterator_over_strings() +{ + + fn to_owned< 'a, T1 >( src : ( T1, Option< Cow< 'a, str > > ) ) -> ( T1, String ) + { + let val = match src.1 + { + Some( c ) => c.into_owned(), + None => String::default(), + }; + ( src.0, val ) + } + + // fn into< 'a, T1, T2 : Copy >( src : ( T1, OptionalCow< 'a, str, T2 > ) ) -> ( T1, Option< Cow< 'a, str > > ) + // { + // ( src.0, src.1.into() ) + // } + + // use test_object::TestObject as TestObject3; + use the_module:: + { + Fields, + IteratorTrait, + TableWithFields, + WithRef, + OptionalCow, + }; + + use std::borrow::Cow; + + /// Struct representing a test object with various fields. + #[ derive( Clone, Debug, PartialEq, Eq ) ] + pub struct TestObject3 + { + pub id : String, + pub created_at : i64, + pub file_ids : Vec< String >, + pub tools : Option< Vec< HashMap< String, String > > >, + } + + impl TableWithFields for TestObject3 {} + + impl Fields< &'_ str, String > + for TestObject3 + { + type Key< 'k > = &'k str; + type Val< 'v > = String; + + fn fields( &self ) -> impl IteratorTrait< Item = ( &'_ str, String ) > + { + use format_tools::ref_or_display_or_debug_multiline::field; + // use format_tools::ref_or_display_or_debug::field; + let mut dst : Vec< ( &'_ str, String ) > = Vec::new(); + + dst.push( to_owned( field!( &self.id ) ) ); + + dst.push( to_owned( field!( &self.created_at ) ) ); + dst.push( to_owned( field!( &self.file_ids ) ) ); + + if let Some( tools ) = &self.tools + { + dst.push( to_owned( field!( tools ) ) ); + } + else + { + dst.push( ( "tools", String::default() ) ); + } + + dst.into_iter() + } + + } + + let _data : collection_tools::Vec< TestObject3 > = dlist! + { + TestObject3 + { + id : "1".to_string(), + created_at : 1627845583, + file_ids : vec![ "file1".to_string(), "file2".to_string() ], + tools : None + }, + TestObject3 + { + 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 + } + ] + ), + }, + }; + + // no variability in what Fields iterate over by design! + + // use the_module::TableFormatter; + // let _as_table : AsTable< '_, Vec< TestObject3 >, &str, TestObject3, str> = AsTable::new( &data ); + // let as_table = AsTable::new( &data ); + +// let rows = TableRows::rows( &as_table ); +// assert_eq!( rows.len(), 2 ); +// +// let mut output = String::new(); +// let mut context = the_module::print::Context::new( &mut output, Default::default() ); +// let _got = the_module::TableFormatter::fmt( &as_table, &mut context ); +// let got = as_table.table_to_string(); +// assert!( got.contains( "│ id │ created_at │ file_ids │ tools │" ) ); +// assert!( got.contains( "│ 13 │ [ │ [ │" ) ); +// assert!( got.contains( "│ 1627845583 │ [ │ │" ) ); + +// let got = AsTable::new( &data ).table_to_string(); +// assert!( got.contains( "│ id │ created_at │ file_ids │ tools │" ) ); +// assert!( got.contains( "│ 13 │ [ │ [ │" ) ); +// assert!( got.contains( "│ 1627845583 │ [ │ │" ) ); + +} diff --git a/module/core/format_tools/tests/inc/test_object.rs b/module/core/format_tools/tests/inc/test_object.rs index 6bb7555aa8..f710266a4d 100644 --- a/module/core/format_tools/tests/inc/test_object.rs +++ b/module/core/format_tools/tests/inc/test_object.rs @@ -15,12 +15,12 @@ use std:: collections::HashMap, hash::Hasher, hash::Hash, - cmp::Ordering - // borrow::Cow, + cmp::Ordering, + borrow::Cow, }; /// Struct representing a test object with various fields. -#[ derive( Clone, Debug ) ] +#[ derive( Clone, Debug, PartialEq, Eq ) ] pub struct TestObject { pub id : String, @@ -31,19 +31,51 @@ pub struct TestObject impl TableWithFields for TestObject {} -impl Fields< &'_ str, OptionalCow< '_, str, WithRef > > +// impl Fields< &'_ str, Option< Cow< '_, str > > > +// for TestObject +// { +// type Key< 'k > = &'k str; +// type Val< 'v > = OptionalCow< 'v, str>; +// +// fn fields( &self ) -> impl IteratorTrait< Item = ( &'_ str, Option< Cow< '_, str > > ) > +// { +// use format_tools::ref_or_display_or_debug_multiline::field; +// // use format_tools::ref_or_display_or_debug::field; +// let mut dst : Vec< ( &'_ str, Option< Cow< '_, str > > ) > = 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", OptionalCow::none() ) ); +// } +// +// dst.into_iter() +// } +// +// } + +impl Fields< &'_ str, Option< Cow< '_, str > > > for TestObject { type Key< 'k > = &'k str; - type Val< 'v > = OptionalCow< 'v, str, WithRef >; + type Val< 'v > = Option< Cow< 'v, str > >; - fn fields( &self ) -> impl IteratorTrait< Item = ( &'_ str, OptionalCow< '_, str, WithRef > ) > + fn fields( &self ) -> impl IteratorTrait< Item = ( &'_ str, Option< Cow< '_, str > > ) > { use format_tools::ref_or_display_or_debug_multiline::field; // use format_tools::ref_or_display_or_debug::field; - let mut dst : Vec< ( &'_ str, OptionalCow< '_, str, WithRef > ) > = Vec::new(); + let mut dst : Vec< ( &'_ str, Option< Cow< '_, str > > ) > = Vec::new(); + // trace_macros!( true ); dst.push( field!( &self.id ) ); + // trace_macros!( false ); dst.push( field!( &self.created_at ) ); dst.push( field!( &self.file_ids ) ); @@ -53,7 +85,7 @@ for TestObject } else { - dst.push( ( "tools", OptionalCow::none() ) ); + dst.push( ( "tools", Option::None ) ); } dst.into_iter() @@ -61,27 +93,27 @@ for TestObject } -impl Hash for TestObject +impl Hash for TestObject { - fn hash< H: Hasher >( &self, state: &mut H ) + fn hash< H: Hasher >( &self, state: &mut H ) { self.id.hash( state ); self.created_at.hash( state ); self.file_ids.hash( state ); - if let Some( tools ) = &self.tools + if let Some( tools ) = &self.tools { - for tool in tools + for tool in tools { - for ( key, value ) in tool + for ( key, value ) in tool { key.hash( state ); value.hash( state ); } } - } - else + } + else { state.write_u8( 0 ); } @@ -89,44 +121,44 @@ impl Hash for TestObject } -impl PartialEq for TestObject -{ - - fn eq( &self, other: &Self ) -> bool - { - self.id == other.id && - self.created_at == other.created_at && - self.file_ids == other.file_ids && - self.tools == other.tools - } - -} +// impl PartialEq for TestObject +// { +// +// fn eq( &self, other: &Self ) -> bool +// { +// self.id == other.id && +// self.created_at == other.created_at && +// self.file_ids == other.file_ids && +// self.tools == other.tools +// } +// +// } +// +// impl Eq for TestObject +// { +// } -impl Eq for TestObject +impl PartialOrd for TestObject { -} -impl PartialOrd for TestObject -{ - - fn partial_cmp( &self, other: &Self ) -> Option< Ordering > + fn partial_cmp( &self, other: &Self ) -> Option< Ordering > { Some( self.cmp( other ) ) } } -impl Ord for TestObject +impl Ord for TestObject { - fn cmp( &self, other: &Self ) -> Ordering + fn cmp( &self, other: &Self ) -> Ordering { self.id .cmp( &other.id ) .then_with( | | self.created_at.cmp( &other.created_at ) ) .then_with( | | self.file_ids.cmp( &other.file_ids ) ) } - + } // diff --git a/module/core/format_tools/tests/smoke_test.rs b/module/core/format_tools/tests/smoke_test.rs index 828e9b016b..cd7b1f36a8 100644 --- a/module/core/format_tools/tests/smoke_test.rs +++ b/module/core/format_tools/tests/smoke_test.rs @@ -1,12 +1,13 @@ +//! Smoke tests. - +/// Smoke test of local version of the crate. #[ test ] fn local_smoke_test() { ::test_tools::smoke_test_for_local_run(); } - +/// Smoke test of published version of the crate. #[ test ] fn published_smoke_test() { diff --git a/module/core/format_tools/tests/tests.rs b/module/core/format_tools/tests/tests.rs index eae1668ea0..4fca6dbc07 100644 --- a/module/core/format_tools/tests/tests.rs +++ b/module/core/format_tools/tests/tests.rs @@ -1,10 +1,10 @@ -// #![ feature( trace_macros ) ] +//! Primary tests. + +#![ feature( trace_macros ) ] +#![ allow( unused_imports ) ] -#[ allow( unused_imports ) ] use format_tools as the_module; -#[ allow( unused_imports ) ] use test_tools::exposed::*; #[ cfg( feature = "enabled" ) ] mod inc; - diff --git a/module/core/implements/Cargo.toml b/module/core/implements/Cargo.toml index 44a51ad680..283004045e 100644 --- a/module/core/implements/Cargo.toml +++ b/module/core/implements/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "implements" -version = "0.8.0" +version = "0.9.0" edition = "2021" authors = [ "Kostiantyn Wandalen ", diff --git a/module/core/impls_index/Cargo.toml b/module/core/impls_index/Cargo.toml index ea67dfa107..65c5781edd 100644 --- a/module/core/impls_index/Cargo.toml +++ b/module/core/impls_index/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "impls_index" -version = "0.7.0" +version = "0.8.0" edition = "2021" authors = [ "Kostiantyn Wandalen ", diff --git a/module/core/impls_index_meta/Cargo.toml b/module/core/impls_index_meta/Cargo.toml index ce629d78b3..e680f0cc47 100644 --- a/module/core/impls_index_meta/Cargo.toml +++ b/module/core/impls_index_meta/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "impls_index_meta" -version = "0.7.0" +version = "0.8.0" edition = "2021" authors = [ "Kostiantyn Wandalen ", diff --git a/module/core/inspect_type/Cargo.toml b/module/core/inspect_type/Cargo.toml index cebba6d857..5bb17bb058 100644 --- a/module/core/inspect_type/Cargo.toml +++ b/module/core/inspect_type/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "inspect_type" -version = "0.10.0" +version = "0.11.0" edition = "2021" authors = [ "Kostiantyn Wandalen ", diff --git a/module/core/inspect_type/src/lib.rs b/module/core/inspect_type/src/lib.rs index 27e60ce850..8f65d3fb63 100644 --- a/module/core/inspect_type/src/lib.rs +++ b/module/core/inspect_type/src/lib.rs @@ -2,7 +2,9 @@ #![ doc( html_favicon_url = "https://raw.githubusercontent.com/Wandalen/wTools/alpha/asset/img/logo_v3_trans_square_icon_small_v2.ico" ) ] #![ doc( html_root_url = "https://docs.rs/inspect_type/latest/inspect_type/" ) ] #![ doc = include_str!( concat!( env!( "CARGO_MANIFEST_DIR" ), "/", "Readme.md" ) ) ] +#![ allow( unexpected_cfgs ) ] +// #[ allow( unexpected_cfgs ) ] // #[ cfg( RUSTC_IS_NIGHTLY ) ] #[ cfg( not( RUSTC_IS_STABLE ) ) ] mod nightly diff --git a/module/core/inspect_type/tests/inc/inspect_type_test.rs b/module/core/inspect_type/tests/inc/inspect_type_test.rs index 58eb0b82b1..01445f74c4 100644 --- a/module/core/inspect_type/tests/inc/inspect_type_test.rs +++ b/module/core/inspect_type/tests/inc/inspect_type_test.rs @@ -1,3 +1,4 @@ + #[ allow( unused_imports ) ] use super::*; diff --git a/module/core/inspect_type/tests/tests.rs b/module/core/inspect_type/tests/tests.rs index 1b4624edf8..8e5818a1f9 100644 --- a/module/core/inspect_type/tests/tests.rs +++ b/module/core/inspect_type/tests/tests.rs @@ -1,3 +1,5 @@ +// #![ allow( unexpected_cfgs ) ] + // #![ no_std ] // #![ cfg_attr( feature = "no_std", no_std ) ] diff --git a/module/core/is_slice/Cargo.toml b/module/core/is_slice/Cargo.toml index b0945c3613..17dc95d0f2 100644 --- a/module/core/is_slice/Cargo.toml +++ b/module/core/is_slice/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "is_slice" -version = "0.9.0" +version = "0.10.0" edition = "2021" authors = [ "Kostiantyn Wandalen ", diff --git a/module/core/mem_tools/Cargo.toml b/module/core/mem_tools/Cargo.toml index fd90f9d727..79a60f163b 100644 --- a/module/core/mem_tools/Cargo.toml +++ b/module/core/mem_tools/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "mem_tools" -version = "0.6.0" +version = "0.7.0" edition = "2021" authors = [ "Kostiantyn Wandalen ", diff --git a/module/core/meta_tools/Cargo.toml b/module/core/meta_tools/Cargo.toml index 282db2f43f..c34c79a376 100644 --- a/module/core/meta_tools/Cargo.toml +++ b/module/core/meta_tools/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "meta_tools" -version = "0.10.0" +version = "0.11.0" edition = "2021" authors = [ "Kostiantyn Wandalen ", diff --git a/module/core/mod_interface/Cargo.toml b/module/core/mod_interface/Cargo.toml index 4c7f49cca8..6d05d0387c 100644 --- a/module/core/mod_interface/Cargo.toml +++ b/module/core/mod_interface/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "mod_interface" -version = "0.25.0" +version = "0.26.0" edition = "2021" authors = [ "Kostiantyn Wandalen ", @@ -28,8 +28,6 @@ all-features = false [features] default = [ "enabled" ] full = [ "enabled" ] -no_std = [] -use_alloc = [ "no_std" ] enabled = [ "mod_interface_meta/enabled" ] # keep these examples in directories diff --git a/module/core/mod_interface/src/lib.rs b/module/core/mod_interface/src/lib.rs index 30c7731f89..726b166188 100644 --- a/module/core/mod_interface/src/lib.rs +++ b/module/core/mod_interface/src/lib.rs @@ -1,4 +1,4 @@ -#![ cfg_attr( feature = "no_std", no_std ) ] +#![ no_std ] #![ doc( html_logo_url = "https://raw.githubusercontent.com/Wandalen/wTools/master/asset/img/logo_v3_trans_square.png" ) ] #![ doc( html_favicon_url = "https://raw.githubusercontent.com/Wandalen/wTools/alpha/asset/img/logo_v3_trans_square_icon_small_v2.ico" ) ] #![ doc( html_root_url = "https://docs.rs/mod_interface/latest/mod_interface/" ) ] diff --git a/module/core/mod_interface_meta/src/lib.rs b/module/core/mod_interface_meta/src/lib.rs index 0505641d53..f215f7f2ac 100644 --- a/module/core/mod_interface_meta/src/lib.rs +++ b/module/core/mod_interface_meta/src/lib.rs @@ -5,6 +5,19 @@ #![ warn( dead_code ) ] +// /// Derives. +// layer derive; +// own use super::derive; +// // xxx : change to remove need to write explicitly that + +// xxx : change to remove need to write explicitly that +// crate::mod_interface! +// { +// /// Derives. +// layer derive; +// own use super::derive; // xxx : change to remove need to write explicitly that +// } + // xxx : clean up, ad solve problems // - example based on simpified version of test::layer_have_layer with single sublayer // - example with attribute `#![ debug ]` diff --git a/module/core/reflect_tools/src/reflect/fields/vec.rs b/module/core/reflect_tools/src/reflect/fields/vec.rs index bdf3977601..0a18259738 100644 --- a/module/core/reflect_tools/src/reflect/fields/vec.rs +++ b/module/core/reflect_tools/src/reflect/fields/vec.rs @@ -9,6 +9,7 @@ use collection_tools::Vec; impl< V, Borrowed > Fields< usize, &'_ Borrowed > for Vec< V > where Borrowed : std::borrow::ToOwned + 'static + ?Sized, + // Borrowed : ?Sized + 'static, V : std::borrow::Borrow< Borrowed >, { @@ -28,6 +29,7 @@ where impl< V, Borrowed > Fields< usize, Option< Cow< '_, Borrowed > > > for Vec< V > where Borrowed : std::borrow::ToOwned + 'static + ?Sized, + // Borrowed : ?Sized + 'static, V : std::borrow::Borrow< Borrowed >, { @@ -39,6 +41,7 @@ where fn fields< 's >( &'s self ) -> impl IteratorTrait< Item = ( Self::Key< 's >, Self::Val< 's > ) > { + // self.iter().enumerate().map( move | ( key, val ) | ( key, Some( Cow::Borrowed( &val ) ) ) ) self.iter().enumerate().map( move | ( key, val ) | ( key, Some( Cow::Borrowed( val.borrow() ) ) ) ) } @@ -47,6 +50,7 @@ where impl< V, Borrowed, Marker > Fields< usize, OptionalCow< '_, Borrowed, Marker > > for Vec< V > where Borrowed : std::borrow::ToOwned + 'static + ?Sized, + // Borrowed : ?Sized + 'static, V : std::borrow::Borrow< Borrowed >, Marker : Clone + Copy + 'static, { diff --git a/module/core/reflect_tools/src/reflect/wrapper.rs b/module/core/reflect_tools/src/reflect/wrapper.rs index defbe192f1..31ad09a99a 100644 --- a/module/core/reflect_tools/src/reflect/wrapper.rs +++ b/module/core/reflect_tools/src/reflect/wrapper.rs @@ -7,7 +7,7 @@ mod private { } -mod maybe_as; +mod optional_cow; #[ doc( inline ) ] #[ allow( unused_imports ) ] @@ -40,7 +40,7 @@ pub mod exposed #[ allow( unused_imports ) ] pub use super:: { - maybe_as::OptionalCow, + optional_cow::OptionalCow, }; } diff --git a/module/core/reflect_tools/src/reflect/wrapper/maybe_as.rs b/module/core/reflect_tools/src/reflect/wrapper/optional_cow.rs similarity index 88% rename from module/core/reflect_tools/src/reflect/wrapper/maybe_as.rs rename to module/core/reflect_tools/src/reflect/wrapper/optional_cow.rs index f1351f8c92..5f329cc459 100644 --- a/module/core/reflect_tools/src/reflect/wrapper/maybe_as.rs +++ b/module/core/reflect_tools/src/reflect/wrapper/optional_cow.rs @@ -20,6 +20,19 @@ where Marker : Clone + Copy + 'static, { + /// Creates owned data from borrowed data, usually by cloning. + #[ inline( always ) ] + pub fn into_owned( &self ) -> < T as std::borrow::ToOwned >::Owned + where + < T as std::borrow::ToOwned >::Owned : Default, + { + match self.0.as_ref() + { + Some( c ) => c.clone().into_owned(), + None => < T as std::borrow::ToOwned >::Owned::default(), + } + } + /// Check is it borrowed. #[ inline( always ) ] pub fn is_borrowed( &self ) -> bool @@ -212,3 +225,15 @@ where T : Eq, { } + +impl< 'a, T, Marker > From< OptionalCow< 'a, T, Marker > > for Option< Cow< 'a, T > > +where + T : std::borrow::ToOwned + ?Sized, + Marker : Clone + Copy + 'static, +{ + #[ inline( always ) ] + fn from( src : OptionalCow< 'a, T, Marker > ) -> Self + { + src.0 + } +} diff --git a/module/core/reflect_tools/tests/inc/fundamental/fields_bset.rs b/module/core/reflect_tools/tests/inc/fundamental/fields_bset.rs index 5a811ad569..abaee19fd5 100644 --- a/module/core/reflect_tools/tests/inc/fundamental/fields_bset.rs +++ b/module/core/reflect_tools/tests/inc/fundamental/fields_bset.rs @@ -4,7 +4,6 @@ use super::*; use the_module:: { Fields, - }; // xxx : implement for other collections diff --git a/module/core/test_tools/Cargo.toml b/module/core/test_tools/Cargo.toml index 2093e6b0b2..0ac5366bb4 100644 --- a/module/core/test_tools/Cargo.toml +++ b/module/core/test_tools/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "test_tools" -version = "0.9.0" +version = "0.10.0" edition = "2021" authors = [ "Kostiantyn Wandalen ", diff --git a/module/core/test_tools/src/lib.rs b/module/core/test_tools/src/lib.rs index efe6e6d7f0..ba768f7641 100644 --- a/module/core/test_tools/src/lib.rs +++ b/module/core/test_tools/src/lib.rs @@ -87,6 +87,8 @@ mod private {} prelude use ::collection_tools as collection; // prelude use ::process_tools as process; + use ::collection_tools; // xxx : do that for all dependencies + prelude use ::meta_tools:: { impls, @@ -95,6 +97,7 @@ mod private {} tests_impls_optional, tests_index, }; + prelude use ::typing_tools::{ implements }; } diff --git a/module/core/typing_tools/Cargo.toml b/module/core/typing_tools/Cargo.toml index 5b128ba0e5..c61c60c278 100644 --- a/module/core/typing_tools/Cargo.toml +++ b/module/core/typing_tools/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "typing_tools" -version = "0.8.0" +version = "0.9.0" edition = "2021" authors = [ "Kostiantyn Wandalen ", diff --git a/module/move/assistant/Cargo.toml b/module/move/assistant/Cargo.toml index 6e2af1a870..144cfb6557 100644 --- a/module/move/assistant/Cargo.toml +++ b/module/move/assistant/Cargo.toml @@ -34,10 +34,11 @@ enabled = [ [dependencies] # xxx : qqq : optimze features +mod_interface = { workspace = true, features = [ "full" ] } former = { workspace = true, features = [ "full" ] } format_tools = { workspace = true, features = [ "full" ] } reflect_tools = { workspace = true, features = [ "full" ] } -openai-api-rs = { version = "4.0.9" } +openai-api-rs = { version = "=5.0.11" } tokio = { version = "1", features = ["full"] } dotenv = "0.15" diff --git a/module/move/assistant/Readme.md b/module/move/assistant/Readme.md index 0e9402c634..9296447b86 100644 --- a/module/move/assistant/Readme.md +++ b/module/move/assistant/Readme.md @@ -3,7 +3,9 @@ # Module :: assistant [![experimental](https://raster.shields.io/static/v1?label=stability&message=experimental&color=orange&logoColor=eee)](https://github.com/emersion/stability-badges#experimental) [![rust-status](https://github.com/Wandalen/wTools/actions/workflows/ModuleassistantPush.yml/badge.svg)](https://github.com/Wandalen/wTools/actions/workflows/ModuleassistantPush.yml) [![docs.rs](https://img.shields.io/docsrs/assistant?color=e3e8f0&logo=docs.rs)](https://docs.rs/assistant) [![discord](https://img.shields.io/discord/872391416519737405?color=eee&logo=discord&logoColor=eee&label=ask)](https://discord.gg/m3YfbXpUUY) -Assist AI in writing code. +**NOT ready for production** + + -Graphs tools. +**NOT ready for production** + - ```rust #[ cfg( all( feature = "cell_factory", feature = "use_std" ) ) ] { @@ -37,3 +36,4 @@ cd wTools cd examples/graphs_tools_trivial cargo run ``` +--> diff --git a/module/move/plot_interface/Readme.md b/module/move/plot_interface/Readme.md index 0e604acfb8..6102a14813 100644 --- a/module/move/plot_interface/Readme.md +++ b/module/move/plot_interface/Readme.md @@ -5,11 +5,12 @@ [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) [![rust-status](https://github.com/Wandalen/wTools/actions/workflows/module_plot_interface_push.yml/badge.svg)](https://github.com/Wandalen/wTools/actions/workflows/module_plot_interface_push.yml) [![docs.rs](https://img.shields.io/docsrs/plot_interface?color=e3e8f0&logo=docs.rs)](https://docs.rs/plot_interface) [![discord](https://img.shields.io/discord/872391416519737405?color=eee&logo=discord&logoColor=eee&label=ask)](https://discord.gg/m3YfbXpUUY) -Plot interface. +**NOT ready for production** + + ```rust ``` @@ -27,4 +28,4 @@ git clone https://github.com/Wandalen/wTools cd wTools cd examples/plot_interface_trivial cargo run -``` +``` --> From fee816b03a43a96294c031f0fcfeb69af7b7f903 Mon Sep 17 00:00:00 2001 From: wbot <69343704+wtools-bot@users.noreply.github.com> Date: Thu, 17 Oct 2024 18:44:57 +0300 Subject: [PATCH 24/67] feat: redeploy strategy without recreating of the server (#1466) --- module/alias/cargo_will/src/bin/willbe.rs | 2 +- module/move/willbe/src/action/deploy_renew.rs | 14 ++--- .../move/willbe/template/deploy/Makefile.hbs | 4 ++ .../willbe/template/deploy/deploy/Dockerfile | 2 +- .../willbe/template/deploy/deploy/aws/main.tf | 37 +++++++++++-- .../deploy/aws/templates/cloud-init.tpl.hbs | 46 ---------------- .../template/deploy/deploy/aws/variables.tf | 10 ++++ .../templates => }/cloud-init.tpl.hbs | 21 ++------ .../deploy/gar/{main.tf => main.tf.hbs} | 2 +- .../deploy/gce/{main.tf => main.tf.hbs} | 52 ++++++++++++++----- .../deploy/gce/{outputs.tf => outputs.tf.hbs} | 2 +- .../deploy/gce/templates/cloud-init.tpl | 24 --------- .../deploy/deploy/hetzner/main.tf.hbs | 37 ++++++++++++- .../deploy/deploy/hetzner/variables.tf | 10 ++++ .../willbe/template/deploy/deploy/redeploy.sh | 6 +++ .../move/willbe/template/deploy/key/Readme.md | 2 + 16 files changed, 153 insertions(+), 118 deletions(-) delete mode 100644 module/move/willbe/template/deploy/deploy/aws/templates/cloud-init.tpl.hbs rename module/move/willbe/template/deploy/deploy/{hetzner/templates => }/cloud-init.tpl.hbs (65%) rename module/move/willbe/template/deploy/deploy/gar/{main.tf => main.tf.hbs} (82%) rename module/move/willbe/template/deploy/deploy/gce/{main.tf => main.tf.hbs} (62%) rename module/move/willbe/template/deploy/deploy/gce/{outputs.tf => outputs.tf.hbs} (79%) delete mode 100644 module/move/willbe/template/deploy/deploy/gce/templates/cloud-init.tpl create mode 100644 module/move/willbe/template/deploy/deploy/redeploy.sh diff --git a/module/alias/cargo_will/src/bin/willbe.rs b/module/alias/cargo_will/src/bin/willbe.rs index 39d2429139..c2850a237c 100644 --- a/module/alias/cargo_will/src/bin/willbe.rs +++ b/module/alias/cargo_will/src/bin/willbe.rs @@ -6,7 +6,7 @@ #[ allow( unused_imports ) ] use::willbe::*; -fn main() -> Result< (), wtools::error::untyped::Error > +fn main() -> Result< (), error::untyped::Error > { Ok( willbe::run( std::env::args().collect() )? ) } diff --git a/module/move/willbe/src/action/deploy_renew.rs b/module/move/willbe/src/action/deploy_renew.rs index 56bf766550..0f1c965332 100644 --- a/module/move/willbe/src/action/deploy_renew.rs +++ b/module/move/willbe/src/action/deploy_renew.rs @@ -47,34 +47,30 @@ mod private .file().data( include_str!( "../../template/deploy/key/pack.sh" ) ).path( "./key/pack.sh" ).end() .file().data( include_str!( "../../template/deploy/key/Readme.md" ) ).path( "./key/Readme.md" ).end() // /deploy/ + .file().data( include_str!( "../../template/deploy/deploy/redeploy.sh" ) ).path( "./deploy/redeploy.sh" ).end() + .file().data( include_str!( "../../template/deploy/deploy/cloud-init.tpl.hbs" ) ).path( "./deploy/cloud-init.tpl" ).is_template( true ).end() .file().data( include_str!( "../../template/deploy/deploy/Dockerfile" ) ).path( "./deploy/Dockerfile" ).end() .file().data( include_str!( "../../template/deploy/deploy/Readme.md" ) ).path( "./deploy/Readme.md" ).end() // /deploy/gar .file().data( include_str!( "../../template/deploy/deploy/gar/Readme.md" ) ).path( "./deploy/gar/Readme.md" ).end() - .file().data( include_str!( "../../template/deploy/deploy/gar/main.tf" ) ).path( "./deploy/gar/main.tf" ).end() + .file().data( include_str!( "../../template/deploy/deploy/gar/main.tf.hbs" ) ).path( "./deploy/gar/main.tf" ).is_template( true ).end() .file().data( include_str!( "../../template/deploy/deploy/gar/outputs.tf" ) ).path( "./deploy/gar/outputs.tf" ).end() .file().data( include_str!( "../../template/deploy/deploy/gar/variables.tf" ) ).path( "./deploy/gar/variables.tf" ).end() // /deploy/gce .file().data( include_str!( "../../template/deploy/deploy/gce/Readme.md" ) ).path( "./deploy/gce/Readme.md" ).end() - .file().data( include_str!( "../../template/deploy/deploy/gce/main.tf" ) ).path( "./deploy/gce/main.tf" ).end() - .file().data( include_str!( "../../template/deploy/deploy/gce/outputs.tf" ) ).path( "./deploy/gce/outputs.tf" ).end() + .file().data( include_str!( "../../template/deploy/deploy/gce/main.tf.hbs" ) ).path( "./deploy/gce/main.tf" ).is_template( true ).end() + .file().data( include_str!( "../../template/deploy/deploy/gce/outputs.tf.hbs" ) ).path( "./deploy/gce/outputs.tf" ).is_template( true ).end() .file().data( include_str!( "../../template/deploy/deploy/gce/variables.tf" ) ).path( "./deploy/gce/variables.tf" ).end() - // /deploy/gce/templates - .file().data( include_str!( "../../template/deploy/deploy/gce/templates/cloud-init.tpl" ) ).path( "./deploy/gce/templates/cloud-init.tpl" ).end() // /deploy/gcs .file().data( include_str!( "../../template/deploy/deploy/gcs/main.tf" ) ).path( "./deploy/gcs/main.tf" ).end() // /deploy/hetzner .file().data( include_str!( "../../template/deploy/deploy/hetzner/main.tf.hbs" ) ).path( "./deploy/hetzner/main.tf" ).is_template( true ).end() .file().data( include_str!( "../../template/deploy/deploy/hetzner/outputs.tf.hbs" ) ).path( "./deploy/hetzner/outputs.tf" ).is_template( true ).end() .file().data( include_str!( "../../template/deploy/deploy/hetzner/variables.tf" ) ).path( "./deploy/hetzner/variables.tf" ).end() - // /deploy/hetzner/templates - .file().data( include_str!( "../../template/deploy/deploy/hetzner/templates/cloud-init.tpl.hbs" ) ).path( "./deploy/hetzner/templates/cloud-init.tpl" ).end() // /deploy/aws .file().data( include_str!( "../../template/deploy/deploy/aws/main.tf" ) ).path( "./deploy/aws/main.tf" ).end() .file().data( include_str!( "../../template/deploy/deploy/aws/outputs.tf" ) ).path( "./deploy/aws/outputs.tf" ).end() .file().data( include_str!( "../../template/deploy/deploy/aws/variables.tf" ) ).path( "./deploy/aws/variables.tf" ).end() - // /deploy/aws/templates - .file().data( include_str!( "../../template/deploy/deploy/aws/templates/cloud-init.tpl.hbs" ) ).path( "./deploy/aws/templates/cloud-init.tpl" ).is_template( true ).end() .form(); formed.files diff --git a/module/move/willbe/template/deploy/Makefile.hbs b/module/move/willbe/template/deploy/Makefile.hbs index 032dec83b8..7f79a8adad 100644 --- a/module/move/willbe/template/deploy/Makefile.hbs +++ b/module/move/willbe/template/deploy/Makefile.hbs @@ -207,3 +207,7 @@ state_storage_pull: state_storage_init: terraform -chdir=$(tf_dir)/gcs init terraform -chdir=$(tf_dir)/gcs apply + +# Destroys GCS Bucket for terraform states +state_storage_destroy: + terraform -chdir=$(tf_dir)/gcs destroy diff --git a/module/move/willbe/template/deploy/deploy/Dockerfile b/module/move/willbe/template/deploy/deploy/Dockerfile index c196de7aff..1fa8f2bf8b 100644 --- a/module/move/willbe/template/deploy/deploy/Dockerfile +++ b/module/move/willbe/template/deploy/deploy/Dockerfile @@ -3,7 +3,7 @@ ENV TF_VERSION=1.7.4 WORKDIR / -# Installation terraform +# Install terraform RUN apt update --allow-releaseinfo-change \ && apt install wget unzip \ && mkdir -p /usr/lib/terraform/${TF_VERSION} \ diff --git a/module/move/willbe/template/deploy/deploy/aws/main.tf b/module/move/willbe/template/deploy/deploy/aws/main.tf index 4e83260aaf..9572193a66 100644 --- a/module/move/willbe/template/deploy/deploy/aws/main.tf +++ b/module/move/willbe/template/deploy/deploy/aws/main.tf @@ -60,17 +60,16 @@ resource "aws_instance" "web" { associate_public_ip_address = true # Startup script for the instance - # Installs docker, gcloud CLI, downloads docker images and starts the container - user_data = templatefile("${path.module}/templates/cloud-init.tpl", { + # Installs docker and gcloud CLI + user_data = templatefile("${path.module}/../cloud-init.tpl", { location = "${var.REGION}" project_id = "${var.PROJECT_ID}" repo_name = "${var.REPO_NAME}" image_name = "${var.IMAGE_NAME}" service_account_creds = "${replace(data.local_sensitive_file.service_account_creds.content, "\n", "")}" - timestamp = "${timestamp()}" }) - user_data_replace_on_change = true + key_name = aws_key_pair.redeploy.key_name } # Static IP address for the instace that will persist on restarts and redeploys @@ -78,3 +77,33 @@ resource "aws_eip" "static" { instance = aws_instance.web.id domain = "vpc" } + +resource "aws_key_pair" "redeploy" { + public_key = data.local_sensitive_file.ssh_public_key.content +} + +resource "terraform_data" "redeploy" { + triggers_replace = timestamp() + + connection { + type = "ssh" + user = "ubuntu" + private_key = data.local_sensitive_file.ssh_private_key.content + host = aws_eip.static.public_ip + } + + provisioner "file" { + source = "${path.module}/../redeploy.sh" + destination = "/tmp/redeploy.sh" + } + + provisioner "remote-exec" { + inline = [ + "#!/bin/bash", + "( tail -f -n1 /var/log/deploy-init.log & ) | grep -q 'Docker configuration file updated.'", + "source /etc/environment", + "chmod +x /tmp/redeploy.sh", + "sudo /tmp/redeploy.sh" + ] + } +} diff --git a/module/move/willbe/template/deploy/deploy/aws/templates/cloud-init.tpl.hbs b/module/move/willbe/template/deploy/deploy/aws/templates/cloud-init.tpl.hbs deleted file mode 100644 index 78a74e4837..0000000000 --- a/module/move/willbe/template/deploy/deploy/aws/templates/cloud-init.tpl.hbs +++ /dev/null @@ -1,46 +0,0 @@ -#cloud-config - -write_files: -- path: /etc/systemd/system/${image_name}.service - permissions: 0644 - owner: root - content: | - [Unit] - Description=Start ${image_name} docker container. Build: ${timestamp} - Wants=network-online.target - After=network-online.target - - [Service] - Environment="HOME=/root" - ExecStart=/usr/bin/docker run --restart unless-stopped -d -p 80:80 --name=${image_name} ${location}-docker.pkg.dev/${project_id}/${repo_name}/${image_name} -- path: /root/service_account.json - permissions: 0600 - owner: root - content: | - ${service_account_creds} -- path: /root/init.sh - permissions: 0700 - owner: root - content: | - # Install docker - apt update - apt install apt-transport-https ca-certificates curl software-properties-common -y - curl -fsSL https://download.docker.com/linux/ubuntu/gpg | apt-key add - - add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" - apt update - apt install docker-ce -y - # Install gcloud CLI - curl https://packages.cloud.google.com/apt/doc/apt-key.gpg | gpg --dearmor -o /usr/share/keyrings/cloud.google.gpg - echo "deb [signed-by=/usr/share/keyrings/cloud.google.gpg] https://packages.cloud.google.com/apt cloud-sdk main" | tee -a /etc/apt/sources.list.d/google-cloud-sdk.list - apt-get update - apt-get install -y google-cloud-cli - # Configure docker with gcloud - gcloud auth activate-service-account --key-file=/root/service_account.json - gcloud auth configure-docker ${location}-docker.pkg.dev --quiet - # Start docker container - systemctl daemon-reload - systemctl start ${image_name}.service - - -runcmd: -- nohup /root/init.sh > /var/log/{{docker_image_name}}-instance-init.log 2>&1 & diff --git a/module/move/willbe/template/deploy/deploy/aws/variables.tf b/module/move/willbe/template/deploy/deploy/aws/variables.tf index ede2b296f3..ed15e05028 100644 --- a/module/move/willbe/template/deploy/deploy/aws/variables.tf +++ b/module/move/willbe/template/deploy/deploy/aws/variables.tf @@ -22,3 +22,13 @@ variable "IMAGE_NAME" { data "local_sensitive_file" "service_account_creds" { filename = "${path.module}/../../key/service_account.json" } + +# Private key for SSH connection +data "local_sensitive_file" "ssh_private_key" { + filename = "${path.module}/../../key/rsa_ssh_key" +} + +# Public key for SSH connection +data "local_sensitive_file" "ssh_public_key" { + filename = "${path.module}/../../key/rsa_ssh_key.pub" +} diff --git a/module/move/willbe/template/deploy/deploy/hetzner/templates/cloud-init.tpl.hbs b/module/move/willbe/template/deploy/deploy/cloud-init.tpl.hbs similarity index 65% rename from module/move/willbe/template/deploy/deploy/hetzner/templates/cloud-init.tpl.hbs rename to module/move/willbe/template/deploy/deploy/cloud-init.tpl.hbs index 081db47304..ce5dcfc9e2 100644 --- a/module/move/willbe/template/deploy/deploy/hetzner/templates/cloud-init.tpl.hbs +++ b/module/move/willbe/template/deploy/deploy/cloud-init.tpl.hbs @@ -1,18 +1,6 @@ #cloud-config write_files: -- path: /etc/systemd/system/${image_name}.service - permissions: 0644 - owner: root - content: | - [Unit] - Description=Start ${image_name} docker container. Build: ${timestamp} - Wants=network-online.target - After=network-online.target - - [Service] - Environment="HOME=/root" - ExecStart=/usr/bin/docker run -d -p 80:80 --name=${image_name} ${location}-docker.pkg.dev/${project_id}/${repo_name}/${image_name} - path: /root/service_account.json permissions: 0600 owner: root @@ -22,6 +10,10 @@ write_files: permissions: 0700 owner: root content: | + # Configure env for redeploy script + echo "DOCKER_IMAGE=${location}-docker.pkg.dev/${project_id}/${repo_name}/${image_name}" >> /etc/environment + echo "DOCKER_IMAGE_NAME=${image_name}" >> /etc/environment + # Install docker apt update apt install apt-transport-https ca-certificates curl software-properties-common -y @@ -37,10 +29,7 @@ write_files: # Configure docker with gcloud gcloud auth activate-service-account --key-file=/root/service_account.json gcloud auth configure-docker ${location}-docker.pkg.dev --quiet - # Start docker container - systemctl daemon-reload - systemctl start ${image_name}.service runcmd: -- nohup /root/init.sh > /var/log/{{docker_image_name}}-instance-init.log 2>&1 & +- nohup /root/init.sh > /var/log/deploy-init.log 2>&1 & diff --git a/module/move/willbe/template/deploy/deploy/gar/main.tf b/module/move/willbe/template/deploy/deploy/gar/main.tf.hbs similarity index 82% rename from module/move/willbe/template/deploy/deploy/gar/main.tf rename to module/move/willbe/template/deploy/deploy/gar/main.tf.hbs index 77709d13e6..920cd1db1e 100644 --- a/module/move/willbe/template/deploy/deploy/gar/main.tf +++ b/module/move/willbe/template/deploy/deploy/gar/main.tf.hbs @@ -9,7 +9,7 @@ resource "google_artifact_registry_repository" "container-images-repo" { location = var.REGION project = var.PROJECT_ID repository_id = var.REPO_NAME - description = "Docker image registry for the Learn Together web-site" + description = "Docker image registry for the {{docker_image_name}} deployments" # Format of the repository. We are using Docker. format = "DOCKER" } diff --git a/module/move/willbe/template/deploy/deploy/gce/main.tf b/module/move/willbe/template/deploy/deploy/gce/main.tf.hbs similarity index 62% rename from module/move/willbe/template/deploy/deploy/gce/main.tf rename to module/move/willbe/template/deploy/deploy/gce/main.tf.hbs index 9e74a148e1..f2cb1598d0 100644 --- a/module/move/willbe/template/deploy/deploy/gce/main.tf +++ b/module/move/willbe/template/deploy/deploy/gce/main.tf.hbs @@ -1,9 +1,8 @@ locals { # Helper var for formatting docker image name - image_name = format("%s-docker.pkg.dev/%s/%s/%s", var.REGION, var.PROJECT_ID, var.REPO_NAME, var.IMAGE_NAME) + image_name = format("%s-docker.pkg.dev/%s/%s/%s", var.REGION, var.PROJECT_ID, var.REPO_NAME, var.IMAGE_NAME) # Helper var for formatting subnetwork for our instance - subnetwork = format("projects/%s/regions/%s/subnetworks/default", var.PROJECT_ID, var.REGION) - instance_name = format("ltsite-%s", formatdate("YYYYMMDDhhmmss", timestamp())) + subnetwork = format("projects/%s/regions/%s/subnetworks/default", var.PROJECT_ID, var.REGION) } # Provider for resource creation @@ -18,10 +17,10 @@ resource "google_compute_address" "default" { } # GCE instance block. -resource "google_compute_instance" "lts-container-vm" { - project = var.PROJECT_ID +resource "google_compute_instance" "{{docker_image_name}}" { + project = var.PROJECT_ID # Instance name - name = local.instance_name + name = "{{docker_image_name}}" # Instance size. e2-micro is 0.25-2 vCPU & 1GB RAM machine_type = "e2-micro" zone = var.ZONE @@ -29,12 +28,12 @@ resource "google_compute_instance" "lts-container-vm" { # Main disk options boot_disk { initialize_params { - # Disk image name. We're using Container-optimised OS (COS). - image = "projects/cos-cloud/global/images/cos-stable-109-17800-147-15" + # Disk image name. We're using Ubuntu 24.04 distro. + image = "projects/ubuntu-os-cloud/global/images/ubuntu-2404-noble-amd64-v20241004" # Disk size in GB. 10GB is allowed minimum. - size = 10 + size = 10 # Disk type. Possible values: pd-standard, pd-ssd, or pd-balanced. - type = "pd-balanced" + type = "pd-balanced" } } @@ -52,19 +51,20 @@ resource "google_compute_instance" "lts-container-vm" { metadata = { # Cloud-init startup script for configuring the instance with our docker container. user-data = "${data.cloudinit_config.conf.rendered}" + ssh-keys = "root:${data.local_sensitive_file.ssh_public_key.content}" } allow_stopping_for_update = true scheduling { # Restart on failure. - automatic_restart = true + automatic_restart = true # Describes maintenance behavior for the instance. Possible values: MIGRATE or TERMINATE. on_host_maintenance = "MIGRATE" # Configures whether to allow stopping instance at any moment for reduced cost. - preemptible = false + preemptible = false # Configures spot instance. Possible values: SPOT or STANDARD. - provisioning_model = "STANDARD" + provisioning_model = "STANDARD" } # Configues service account scopes. @@ -86,3 +86,29 @@ resource "google_compute_instance" "lts-container-vm" { # Use `https-server` for https traffic on port 443. tags = ["http-server"] } + +resource "terraform_data" "redeploy" { + triggers_replace = timestamp() + + connection { + type = "ssh" + user = "root" + private_key = data.local_sensitive_file.ssh_private_key.content + host = google_compute_instance.{{docker_image_name}}.network_interface[0].access_config[0].nat_ip + } + + provisioner "file" { + source = "${path.module}/../redeploy.sh" + destination = "/tmp/redeploy.sh" + } + + provisioner "remote-exec" { + inline = [ + "#!/bin/bash", + "( tail -f -n1 /var/log/deploy-init.log & ) | grep -q 'Docker configuration file updated.'", + "source /etc/environment", + "chmod +x /tmp/redeploy.sh", + "/tmp/redeploy.sh" + ] + } +} diff --git a/module/move/willbe/template/deploy/deploy/gce/outputs.tf b/module/move/willbe/template/deploy/deploy/gce/outputs.tf.hbs similarity index 79% rename from module/move/willbe/template/deploy/deploy/gce/outputs.tf rename to module/move/willbe/template/deploy/deploy/gce/outputs.tf.hbs index 9228e2fa83..58b076f05b 100644 --- a/module/move/willbe/template/deploy/deploy/gce/outputs.tf +++ b/module/move/willbe/template/deploy/deploy/gce/outputs.tf.hbs @@ -1,5 +1,5 @@ locals { - ip = google_compute_instance.lts-container-vm.network_interface[0].access_config[0].nat_ip + ip = google_compute_instance.{{docker_image_name}}.network_interface[0].access_config[0].nat_ip } # Output that we get after applying. diff --git a/module/move/willbe/template/deploy/deploy/gce/templates/cloud-init.tpl b/module/move/willbe/template/deploy/deploy/gce/templates/cloud-init.tpl deleted file mode 100644 index 5c465968d9..0000000000 --- a/module/move/willbe/template/deploy/deploy/gce/templates/cloud-init.tpl +++ /dev/null @@ -1,24 +0,0 @@ -#cloud-config - -users: -- name: ${image_name} - uid: 2000 - -write_files: -- path: /etc/systemd/system/${image_name}.service - permissions: 0644 - owner: root - content: | - [Unit] - Description=Start the Learn Together ${image_name} docker container - Wants=gcr-online.target - After=gcr-online.target - - [Service] - Environment="HOME=/home/${image_name}" - ExecStartPre=/usr/bin/docker-credential-gcr configure-docker --registries=${location}-docker.pkg.dev - ExecStart=/usr/bin/docker run -d -p 80:80 --name=${image_name} ${location}-docker.pkg.dev/${project_id}/${repo_name}/${image_name} - -runcmd: -- systemctl daemon-reload -- systemctl start ${image_name}.service \ No newline at end of file diff --git a/module/move/willbe/template/deploy/deploy/hetzner/main.tf.hbs b/module/move/willbe/template/deploy/deploy/hetzner/main.tf.hbs index 5611dafc2d..b75e946aab 100644 --- a/module/move/willbe/template/deploy/deploy/hetzner/main.tf.hbs +++ b/module/move/willbe/template/deploy/deploy/hetzner/main.tf.hbs @@ -14,6 +14,12 @@ provider "hcloud" { token = var.HCLOUD_TOKEN } +# Creates an SSH key used for redeploy +resource "hcloud_ssh_key" "redeploy" { + name = "{{docker_image_name}} redeploy key" + public_key = data.local_sensitive_file.ssh_public_key.content +} + # Static IP for the instance resource "hcloud_primary_ip" "primary_ip" { name = "{{docker_image_name}}-ip" @@ -36,14 +42,41 @@ resource "hcloud_server" "{{docker_image_name}}" { ipv6_enabled = false } + ssh_keys = [ hcloud_ssh_key.redeploy.name ] + # Startup script for the instance # Installs docker, gcloud CLI, downloads docker images and starts the container - user_data = templatefile("${path.module}/templates/cloud-init.tpl", { + user_data = templatefile("${path.module}/../cloud-init.tpl", { location = "${var.REGION}" project_id = "${var.PROJECT_ID}" repo_name = "${var.REPO_NAME}" image_name = "${var.IMAGE_NAME}" service_account_creds = "${replace(data.local_sensitive_file.service_account_creds.content, "\n", "")}" - timestamp = "${timestamp()}" }) } + +resource "terraform_data" "redeploy" { + triggers_replace = timestamp() + + connection { + type = "ssh" + user = "root" + private_key = data.local_sensitive_file.ssh_private_key.content + host = hcloud_primary_ip.primary_ip.ip_address + } + + provisioner "file" { + source = "${path.module}/../redeploy.sh" + destination = "/tmp/redeploy.sh" + } + + provisioner "remote-exec" { + inline = [ + "#!/bin/bash", + "( tail -f -n1 /var/log/deploy-init.log & ) | grep -q 'Docker configuration file updated.'", + "source /etc/environment", + "chmod +x /tmp/redeploy.sh", + "/tmp/redeploy.sh" + ] + } +} diff --git a/module/move/willbe/template/deploy/deploy/hetzner/variables.tf b/module/move/willbe/template/deploy/deploy/hetzner/variables.tf index 92e5e44421..4d445400eb 100644 --- a/module/move/willbe/template/deploy/deploy/hetzner/variables.tf +++ b/module/move/willbe/template/deploy/deploy/hetzner/variables.tf @@ -27,3 +27,13 @@ variable "IMAGE_NAME" { data "local_sensitive_file" "service_account_creds" { filename = "${path.module}/../../key/service_account.json" } + +# Private key for SSH connection +data "local_sensitive_file" "ssh_private_key" { + filename = "${path.module}/../../key/rsa_ssh_key" +} + +# Public key for SSH connection +data "local_sensitive_file" "ssh_public_key" { + filename = "${path.module}/../../key/rsa_ssh_key.pub" +} diff --git a/module/move/willbe/template/deploy/deploy/redeploy.sh b/module/move/willbe/template/deploy/deploy/redeploy.sh new file mode 100644 index 0000000000..48695a43e1 --- /dev/null +++ b/module/move/willbe/template/deploy/deploy/redeploy.sh @@ -0,0 +1,6 @@ +#!/bin/sh + +docker rm -f ${DOCKER_IMAGE_NAME} +docker rmi ${DOCKER_IMAGE} +docker pull ${DOCKER_IMAGE} +docker run -d --restart unless-stopped -p 80:80 --name=${DOCKER_IMAGE_NAME} ${DOCKER_IMAGE} diff --git a/module/move/willbe/template/deploy/key/Readme.md b/module/move/willbe/template/deploy/key/Readme.md index 53c085c1cd..84fc765608 100644 --- a/module/move/willbe/template/deploy/key/Readme.md +++ b/module/move/willbe/template/deploy/key/Readme.md @@ -16,6 +16,8 @@ A list of all keys you'd need to deploy your project on different hosts. All secrets can be provided as files in current directory: - [service_account.json](./service_account.json) - default credentials for the service account to use in deployment. +- [rsa_ssh_key](./rsa_ssh_key) - SSH Private key that will be used for redeployment. +- [rsa_ssh_key.pub](./rsa_ssh_key.pub) - SSH Private key that will be used for redeployment. - [`SECRET_STATE_ARCHIVE_KEY`](./SECRET_STATE_ARCHIVE_KEY) - [📃] base64 encoded AES256 key to encrypt and decrypt .tfstate files. - [`SECRET_CSP_HETZNER`](./SECRET_CSP_HETZNER) - [📃] Hetzner token for deploying a server. - [`SECRET_AWS_ACCESS_KEY_ID`](./SECRET_AWS_ACCESS_KEY_ID) - [📃] Access Key ID from AWS Credentials. Created at the same time as the Access Key itself. From a54cd893b54d05abc3a1cf4a7dbc14c10c7a389a Mon Sep 17 00:00:00 2001 From: wandalen Date: Sat, 19 Oct 2024 17:16:05 +0300 Subject: [PATCH 25/67] mod_interface_meta-v0.26.0 --- Cargo.toml | 2 +- module/core/mod_interface_meta/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 522f6e750a..260d866335 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -253,7 +253,7 @@ path = "module/core/mod_interface" default-features = false [workspace.dependencies.mod_interface_meta] -version = "~0.25.0" +version = "~0.26.0" path = "module/core/mod_interface_meta" default-features = false diff --git a/module/core/mod_interface_meta/Cargo.toml b/module/core/mod_interface_meta/Cargo.toml index 687f54ddbe..7c5cf7ad26 100644 --- a/module/core/mod_interface_meta/Cargo.toml +++ b/module/core/mod_interface_meta/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "mod_interface_meta" -version = "0.25.0" +version = "0.26.0" edition = "2021" authors = [ "Kostiantyn Wandalen ", From 59977d2140d9641f81e1b18eacfc081162cd9f41 Mon Sep 17 00:00:00 2001 From: wandalen Date: Sat, 19 Oct 2024 17:16:25 +0300 Subject: [PATCH 26/67] mod_interface-v0.27.0 --- Cargo.toml | 2 +- module/core/mod_interface/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 260d866335..692306d39f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -248,7 +248,7 @@ version = "~0.8.0" path = "module/core/impls_index_meta" [workspace.dependencies.mod_interface] -version = "~0.26.0" +version = "~0.27.0" path = "module/core/mod_interface" default-features = false diff --git a/module/core/mod_interface/Cargo.toml b/module/core/mod_interface/Cargo.toml index 6d05d0387c..66a9f9bdc6 100644 --- a/module/core/mod_interface/Cargo.toml +++ b/module/core/mod_interface/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "mod_interface" -version = "0.26.0" +version = "0.27.0" edition = "2021" authors = [ "Kostiantyn Wandalen ", From 46b197c6395acb5fa6f8db9fa14e76c058a477e5 Mon Sep 17 00:00:00 2001 From: wandalen Date: Sat, 19 Oct 2024 17:16:42 +0300 Subject: [PATCH 27/67] proper_path_tools-v0.11.0 --- Cargo.toml | 2 +- module/core/proper_path_tools/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 692306d39f..2123695355 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -367,7 +367,7 @@ path = "module/alias/file_tools" default-features = false [workspace.dependencies.proper_path_tools] -version = "~0.10.0" +version = "~0.11.0" path = "module/core/proper_path_tools" default-features = false diff --git a/module/core/proper_path_tools/Cargo.toml b/module/core/proper_path_tools/Cargo.toml index 5c6387502b..abcf335fb3 100644 --- a/module/core/proper_path_tools/Cargo.toml +++ b/module/core/proper_path_tools/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "proper_path_tools" -version = "0.10.0" +version = "0.11.0" edition = "2021" authors = [ "Kostiantyn Wandalen ", From 934c77d2d5ec23f051c057a48db7818d7d7fa25a Mon Sep 17 00:00:00 2001 From: wandalen Date: Sat, 19 Oct 2024 17:16:58 +0300 Subject: [PATCH 28/67] process_tools-v0.10.0 --- Cargo.toml | 2 +- module/core/process_tools/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 2123695355..32c0910a6b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -375,7 +375,7 @@ default-features = false ## process tools [workspace.dependencies.process_tools] -version = "~0.9.0" +version = "~0.10.0" path = "module/core/process_tools" default-features = false diff --git a/module/core/process_tools/Cargo.toml b/module/core/process_tools/Cargo.toml index 308881a600..6066e48942 100644 --- a/module/core/process_tools/Cargo.toml +++ b/module/core/process_tools/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "process_tools" -version = "0.9.0" +version = "0.10.0" edition = "2021" authors = [ "Kostiantyn Wandalen ", From 641b7bdd18f3c866a5b9e4071f2bc359ddaf3f24 Mon Sep 17 00:00:00 2001 From: wandalen Date: Sat, 19 Oct 2024 17:17:16 +0300 Subject: [PATCH 29/67] wca-v0.22.0 --- Cargo.toml | 2 +- module/move/wca/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 32c0910a6b..812d9ca326 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -422,7 +422,7 @@ default-features = false ## ca [workspace.dependencies.wca] -version = "~0.21.0" +version = "~0.22.0" path = "module/move/wca" diff --git a/module/move/wca/Cargo.toml b/module/move/wca/Cargo.toml index eab9096bd4..ae882ea833 100644 --- a/module/move/wca/Cargo.toml +++ b/module/move/wca/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wca" -version = "0.21.0" +version = "0.22.0" edition = "2021" authors = [ "Kostiantyn Wandalen ", From c291f9d1a27caf056fc0d1117a818852fe0dd1e1 Mon Sep 17 00:00:00 2001 From: wandalen Date: Sat, 19 Oct 2024 17:18:12 +0300 Subject: [PATCH 30/67] willbe-v0.19.0 --- Cargo.toml | 2 +- module/move/willbe/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 812d9ca326..d34c2a4b57 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -436,7 +436,7 @@ path = "module/move/wcensor" ## willbe [workspace.dependencies.willbe] -version = "~0.18.0" +version = "~0.19.0" path = "module/move/willbe" diff --git a/module/move/willbe/Cargo.toml b/module/move/willbe/Cargo.toml index 1555f1d3fa..e7152f6149 100644 --- a/module/move/willbe/Cargo.toml +++ b/module/move/willbe/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "willbe" -version = "0.18.0" +version = "0.19.0" edition = "2021" authors = [ "Kostiantyn Wandalen ", From 757e9ff69905fcc528c99c657a53bc2ab5626f21 Mon Sep 17 00:00:00 2001 From: wbot <69343704+wtools-bot@users.noreply.github.com> Date: Wed, 30 Oct 2024 10:27:52 +0200 Subject: [PATCH 31/67] AUTO : Forward from format_tools_evolving_6 to alpha (#1465) refactoring --- Cargo.toml | 2 +- module/alias/cargo_will/License | 2 +- module/alias/file_tools/License | 2 +- module/alias/fundamental_data_type/License | 2 +- module/alias/instance_of/License | 2 +- module/alias/multilayer/License | 2 +- module/alias/proc_macro_tools/License | 2 +- module/alias/proper_tools/License | 2 +- module/alias/werror/License | 2 +- module/alias/willbe2/License | 2 +- module/alias/winterval/License | 2 +- module/alias/wproc_macro/License | 2 +- module/alias/wstring_tools/License | 2 +- module/alias/wtest/License | 2 +- module/alias/wtest_basic/License | 2 +- module/blank/brain_tools/License | 2 +- module/blank/draw_lang/License | 2 +- module/blank/drawboard/License | 2 +- module/blank/drawql/License | 2 +- module/blank/exe_tools/License | 2 +- module/blank/graphtools/Cargo.toml | 34 +++++++++++++++++++ module/blank/graphtools/License | 22 ++++++++++++ module/blank/graphtools/Readme.md | 33 ++++++++++++++++++ module/blank/graphtools/src/lib.rs | 11 ++++++ .../blank/graphtools/tests/inc/basic_test.rs | 7 ++++ module/blank/graphtools/tests/inc/mod.rs | 4 +++ module/blank/graphtools/tests/smoke_test.rs | 12 +++++++ module/blank/graphtools/tests/tests.rs | 10 ++++++ module/blank/image_tools/License | 2 +- module/blank/math_tools/License | 2 +- module/blank/mindx12/License | 2 +- module/blank/mingl/License | 2 +- module/blank/minmetal/License | 2 +- module/blank/minopengl/License | 2 +- module/blank/minvulkan/License | 2 +- module/blank/minwebgl/License | 2 +- module/blank/minwebgpu/License | 2 +- module/blank/minwgpu/License | 2 +- module/blank/paths_tools/License | 2 +- module/blank/rustql/License | 2 +- module/blank/second_brain/License | 2 +- module/blank/w4d/License | 2 +- module/blank/wlang/License | 2 +- module/core/clone_dyn/License | 2 +- module/core/clone_dyn_meta/License | 2 +- module/core/clone_dyn_types/License | 2 +- module/core/collection_tools/License | 2 +- module/core/data_type/License | 2 +- module/core/derive_tools/License | 2 +- module/core/derive_tools_meta/License | 2 +- module/core/diagnostics_tools/License | 2 +- module/core/error_tools/License | 2 +- module/core/for_each/License | 2 +- module/core/format_tools/License | 2 +- module/core/former/License | 2 +- module/core/former_meta/License | 2 +- module/core/former_types/License | 2 +- module/core/fs_tools/License | 2 +- module/core/implements/License | 2 +- module/core/impls_index/License | 2 +- module/core/impls_index_meta/License | 2 +- module/core/include_md/License | 2 +- module/core/inspect_type/License | 2 +- module/core/interval_adapter/License | 2 +- module/core/is_slice/License | 2 +- module/core/iter_tools/License | 2 +- module/core/macro_tools/License | 2 +- module/core/mem_tools/License | 2 +- module/core/meta_tools/License | 2 +- module/core/mod_interface/License | 2 +- module/core/mod_interface_meta/License | 2 +- module/core/process_tools/License | 2 +- module/core/program_tools/License | 2 +- module/core/proper_path_tools/License | 2 +- module/core/reflect_tools/License | 2 +- module/core/reflect_tools_meta/License | 2 +- module/core/strs_tools/License | 2 +- module/core/test_tools/License | 2 +- module/core/time_tools/License | 2 +- module/core/typing_tools/License | 2 +- module/core/variadic_from/License | 2 +- module/core/wtools/License | 2 +- module/move/assistant/License | 2 +- module/move/crates_tools/License | 2 +- module/move/deterministic_rand/License | 2 +- module/move/graphs_tools/Cargo.toml | 2 +- module/move/graphs_tools/License | 2 +- module/move/optimization_tools/License | 2 +- module/move/plot_interface/License | 2 +- module/move/refiner/License | 2 +- module/move/sqlx_query/License | 2 +- module/move/wca/License | 2 +- module/move/willbe/License | 2 +- module/move/wplot/License | 2 +- module/postponed/_video_experiment/License | 2 +- module/postponed/automata_tools/License | 2 +- module/postponed/non_std/License | 2 +- module/postponed/std_tools/License | 2 +- module/postponed/std_x/License | 2 +- module/postponed/type_constructor/License | 2 +- module/postponed/wautomata/License | 2 +- module/postponed/wpublisher/License | 2 +- module/template/template_alias/License | 2 +- module/template/template_blank/License | 2 +- .../template_procedural_macro/License | 2 +- .../template_procedural_macro_meta/License | 2 +- .../template_procedural_macro_runtime/License | 2 +- 107 files changed, 232 insertions(+), 99 deletions(-) create mode 100644 module/blank/graphtools/Cargo.toml create mode 100644 module/blank/graphtools/License create mode 100644 module/blank/graphtools/Readme.md create mode 100644 module/blank/graphtools/src/lib.rs create mode 100644 module/blank/graphtools/tests/inc/basic_test.rs create mode 100644 module/blank/graphtools/tests/inc/mod.rs create mode 100644 module/blank/graphtools/tests/smoke_test.rs create mode 100644 module/blank/graphtools/tests/tests.rs diff --git a/Cargo.toml b/Cargo.toml index d34c2a4b57..fefc262f5f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -404,7 +404,7 @@ path = "module/alias/wtest_basic" ## graphs tools [workspace.dependencies.graphs_tools] -version = "~0.2.0" +version = "~0.3.0" path = "module/move/graphs_tools" default-features = false diff --git a/module/alias/cargo_will/License b/module/alias/cargo_will/License index 6d5ef8559f..0804aed8e3 100644 --- a/module/alias/cargo_will/License +++ b/module/alias/cargo_will/License @@ -1,4 +1,4 @@ -Copyright Kostiantyn W and Out of the Box Systems (c) 2013-2024 +Copyright Kostiantyn Mysnyk and Out of the Box Systems (c) 2021-2024 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation diff --git a/module/alias/file_tools/License b/module/alias/file_tools/License index 6d5ef8559f..0804aed8e3 100644 --- a/module/alias/file_tools/License +++ b/module/alias/file_tools/License @@ -1,4 +1,4 @@ -Copyright Kostiantyn W and Out of the Box Systems (c) 2013-2024 +Copyright Kostiantyn Mysnyk and Out of the Box Systems (c) 2021-2024 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation diff --git a/module/alias/fundamental_data_type/License b/module/alias/fundamental_data_type/License index 6d5ef8559f..0804aed8e3 100644 --- a/module/alias/fundamental_data_type/License +++ b/module/alias/fundamental_data_type/License @@ -1,4 +1,4 @@ -Copyright Kostiantyn W and Out of the Box Systems (c) 2013-2024 +Copyright Kostiantyn Mysnyk and Out of the Box Systems (c) 2021-2024 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation diff --git a/module/alias/instance_of/License b/module/alias/instance_of/License index e3e9e057cf..c32986cee3 100644 --- a/module/alias/instance_of/License +++ b/module/alias/instance_of/License @@ -1,4 +1,4 @@ -Copyright Kostiantyn W and Out of the Box Systems (c) 2013-2024 +Copyright Kostiantyn Mysnyk and Out of the Box Systems (c) 2021-2024 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation diff --git a/module/alias/multilayer/License b/module/alias/multilayer/License index e3e9e057cf..c32986cee3 100644 --- a/module/alias/multilayer/License +++ b/module/alias/multilayer/License @@ -1,4 +1,4 @@ -Copyright Kostiantyn W and Out of the Box Systems (c) 2013-2024 +Copyright Kostiantyn Mysnyk and Out of the Box Systems (c) 2021-2024 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation diff --git a/module/alias/proc_macro_tools/License b/module/alias/proc_macro_tools/License index 6d5ef8559f..0804aed8e3 100644 --- a/module/alias/proc_macro_tools/License +++ b/module/alias/proc_macro_tools/License @@ -1,4 +1,4 @@ -Copyright Kostiantyn W and Out of the Box Systems (c) 2013-2024 +Copyright Kostiantyn Mysnyk and Out of the Box Systems (c) 2021-2024 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation diff --git a/module/alias/proper_tools/License b/module/alias/proper_tools/License index 6d5ef8559f..0804aed8e3 100644 --- a/module/alias/proper_tools/License +++ b/module/alias/proper_tools/License @@ -1,4 +1,4 @@ -Copyright Kostiantyn W and Out of the Box Systems (c) 2013-2024 +Copyright Kostiantyn Mysnyk and Out of the Box Systems (c) 2021-2024 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation diff --git a/module/alias/werror/License b/module/alias/werror/License index 6d5ef8559f..0804aed8e3 100644 --- a/module/alias/werror/License +++ b/module/alias/werror/License @@ -1,4 +1,4 @@ -Copyright Kostiantyn W and Out of the Box Systems (c) 2013-2024 +Copyright Kostiantyn Mysnyk and Out of the Box Systems (c) 2021-2024 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation diff --git a/module/alias/willbe2/License b/module/alias/willbe2/License index 6d5ef8559f..0804aed8e3 100644 --- a/module/alias/willbe2/License +++ b/module/alias/willbe2/License @@ -1,4 +1,4 @@ -Copyright Kostiantyn W and Out of the Box Systems (c) 2013-2024 +Copyright Kostiantyn Mysnyk and Out of the Box Systems (c) 2021-2024 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation diff --git a/module/alias/winterval/License b/module/alias/winterval/License index 6d5ef8559f..0804aed8e3 100644 --- a/module/alias/winterval/License +++ b/module/alias/winterval/License @@ -1,4 +1,4 @@ -Copyright Kostiantyn W and Out of the Box Systems (c) 2013-2024 +Copyright Kostiantyn Mysnyk and Out of the Box Systems (c) 2021-2024 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation diff --git a/module/alias/wproc_macro/License b/module/alias/wproc_macro/License index 6d5ef8559f..0804aed8e3 100644 --- a/module/alias/wproc_macro/License +++ b/module/alias/wproc_macro/License @@ -1,4 +1,4 @@ -Copyright Kostiantyn W and Out of the Box Systems (c) 2013-2024 +Copyright Kostiantyn Mysnyk and Out of the Box Systems (c) 2021-2024 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation diff --git a/module/alias/wstring_tools/License b/module/alias/wstring_tools/License index 6d5ef8559f..0804aed8e3 100644 --- a/module/alias/wstring_tools/License +++ b/module/alias/wstring_tools/License @@ -1,4 +1,4 @@ -Copyright Kostiantyn W and Out of the Box Systems (c) 2013-2024 +Copyright Kostiantyn Mysnyk and Out of the Box Systems (c) 2021-2024 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation diff --git a/module/alias/wtest/License b/module/alias/wtest/License index e3e9e057cf..c32986cee3 100644 --- a/module/alias/wtest/License +++ b/module/alias/wtest/License @@ -1,4 +1,4 @@ -Copyright Kostiantyn W and Out of the Box Systems (c) 2013-2024 +Copyright Kostiantyn Mysnyk and Out of the Box Systems (c) 2021-2024 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation diff --git a/module/alias/wtest_basic/License b/module/alias/wtest_basic/License index 6d5ef8559f..0804aed8e3 100644 --- a/module/alias/wtest_basic/License +++ b/module/alias/wtest_basic/License @@ -1,4 +1,4 @@ -Copyright Kostiantyn W and Out of the Box Systems (c) 2013-2024 +Copyright Kostiantyn Mysnyk and Out of the Box Systems (c) 2021-2024 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation diff --git a/module/blank/brain_tools/License b/module/blank/brain_tools/License index 6d5ef8559f..0804aed8e3 100644 --- a/module/blank/brain_tools/License +++ b/module/blank/brain_tools/License @@ -1,4 +1,4 @@ -Copyright Kostiantyn W and Out of the Box Systems (c) 2013-2024 +Copyright Kostiantyn Mysnyk and Out of the Box Systems (c) 2021-2024 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation diff --git a/module/blank/draw_lang/License b/module/blank/draw_lang/License index 6d5ef8559f..0804aed8e3 100644 --- a/module/blank/draw_lang/License +++ b/module/blank/draw_lang/License @@ -1,4 +1,4 @@ -Copyright Kostiantyn W and Out of the Box Systems (c) 2013-2024 +Copyright Kostiantyn Mysnyk and Out of the Box Systems (c) 2021-2024 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation diff --git a/module/blank/drawboard/License b/module/blank/drawboard/License index 6d5ef8559f..0804aed8e3 100644 --- a/module/blank/drawboard/License +++ b/module/blank/drawboard/License @@ -1,4 +1,4 @@ -Copyright Kostiantyn W and Out of the Box Systems (c) 2013-2024 +Copyright Kostiantyn Mysnyk and Out of the Box Systems (c) 2021-2024 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation diff --git a/module/blank/drawql/License b/module/blank/drawql/License index 6d5ef8559f..0804aed8e3 100644 --- a/module/blank/drawql/License +++ b/module/blank/drawql/License @@ -1,4 +1,4 @@ -Copyright Kostiantyn W and Out of the Box Systems (c) 2013-2024 +Copyright Kostiantyn Mysnyk and Out of the Box Systems (c) 2021-2024 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation diff --git a/module/blank/exe_tools/License b/module/blank/exe_tools/License index 6d5ef8559f..0804aed8e3 100644 --- a/module/blank/exe_tools/License +++ b/module/blank/exe_tools/License @@ -1,4 +1,4 @@ -Copyright Kostiantyn W and Out of the Box Systems (c) 2013-2024 +Copyright Kostiantyn Mysnyk and Out of the Box Systems (c) 2021-2024 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation diff --git a/module/blank/graphtools/Cargo.toml b/module/blank/graphtools/Cargo.toml new file mode 100644 index 0000000000..67a3c06564 --- /dev/null +++ b/module/blank/graphtools/Cargo.toml @@ -0,0 +1,34 @@ +[package] +name = "graphtools" +version = "0.1.0" +edition = "2021" +authors = [ + "Kostiantyn Wandalen ", +] +license = "MIT" +readme = "Readme.md" +documentation = "https://docs.rs/graphtools" +repository = "https://github.com/Wandalen/wTools/tree/master/module/blank/graphtools" +homepage = "https://github.com/Wandalen/wTools/tree/master/module/blank/graphtools" +description = """ +Tools to manipulate graphs. +""" +categories = [ "algorithms", "development-tools" ] +keywords = [ "fundamental", "general-purpose" ] + +[lints] +workspace = true + +[package.metadata.docs.rs] +features = [ "full" ] +all-features = false + +[features] +default = [ "enabled" ] +full = [ "enabled" ] +enabled = [] + +[dependencies] + +[dev-dependencies] +test_tools = { workspace = true } diff --git a/module/blank/graphtools/License b/module/blank/graphtools/License new file mode 100644 index 0000000000..0804aed8e3 --- /dev/null +++ b/module/blank/graphtools/License @@ -0,0 +1,22 @@ +Copyright Kostiantyn Mysnyk and Out of the Box Systems (c) 2021-2024 + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. diff --git a/module/blank/graphtools/Readme.md b/module/blank/graphtools/Readme.md new file mode 100644 index 0000000000..175776ccd1 --- /dev/null +++ b/module/blank/graphtools/Readme.md @@ -0,0 +1,33 @@ + + +# Module :: graphtools +[![experimental](https://raster.shields.io/static/v1?label=stability&message=experimental&color=orange&logoColor=eee)](https://github.com/emersion/stability-badges#experimental) [![rust-status](https://github.com/Wandalen/wTools/actions/workflows/Modulebrain_toolsPush.yml/badge.svg)](https://github.com/Wandalen/wTools/actions/workflows/Modulebrain_toolsPush.yml) [![docs.rs](https://img.shields.io/docsrs/graphtools?color=e3e8f0&logo=docs.rs)](https://docs.rs/graphtools) [![discord](https://img.shields.io/discord/872391416519737405?color=eee&logo=discord&logoColor=eee&label=ask)](https://discord.gg/m3YfbXpUUY) + +Tools to manipulate graphs. + + diff --git a/module/blank/graphtools/src/lib.rs b/module/blank/graphtools/src/lib.rs new file mode 100644 index 0000000000..4168554e8f --- /dev/null +++ b/module/blank/graphtools/src/lib.rs @@ -0,0 +1,11 @@ + +#![ doc( html_logo_url = "https://raw.githubusercontent.com/Wandalen/wTools/master/asset/img/logo_v3_trans_square.png" ) ] +#![ doc( html_favicon_url = "https://raw.githubusercontent.com/Wandalen/wTools/alpha/asset/img/logo_v3_trans_square_icon_small_v2.ico" ) ] +#![ doc( html_root_url = "https://docs.rs/brain_tools/latest/brain_tools/" ) ] +#![ doc = include_str!( concat!( env!( "CARGO_MANIFEST_DIR" ), "/", "Readme.md" ) ) ] + +/// Function description. +#[ cfg( feature = "enabled" ) ] +pub fn f1() +{ +} diff --git a/module/blank/graphtools/tests/inc/basic_test.rs b/module/blank/graphtools/tests/inc/basic_test.rs new file mode 100644 index 0000000000..60c9a81cfb --- /dev/null +++ b/module/blank/graphtools/tests/inc/basic_test.rs @@ -0,0 +1,7 @@ +#[ allow( unused_imports ) ] +use super::*; + +#[ test ] +fn basic() +{ +} diff --git a/module/blank/graphtools/tests/inc/mod.rs b/module/blank/graphtools/tests/inc/mod.rs new file mode 100644 index 0000000000..dde9de6f94 --- /dev/null +++ b/module/blank/graphtools/tests/inc/mod.rs @@ -0,0 +1,4 @@ +#[ allow( unused_imports ) ] +use super::*; + +mod basic_test; diff --git a/module/blank/graphtools/tests/smoke_test.rs b/module/blank/graphtools/tests/smoke_test.rs new file mode 100644 index 0000000000..663dd6fb9f --- /dev/null +++ b/module/blank/graphtools/tests/smoke_test.rs @@ -0,0 +1,12 @@ + +#[ test ] +fn local_smoke_test() +{ + ::test_tools::smoke_test_for_local_run(); +} + +#[ test ] +fn published_smoke_test() +{ + ::test_tools::smoke_test_for_published_run(); +} diff --git a/module/blank/graphtools/tests/tests.rs b/module/blank/graphtools/tests/tests.rs new file mode 100644 index 0000000000..574f34b114 --- /dev/null +++ b/module/blank/graphtools/tests/tests.rs @@ -0,0 +1,10 @@ + +include!( "../../../../module/step/meta/src/module/terminal.rs" ); + +#[ allow( unused_imports ) ] +use brain_tools as the_module; +#[ allow( unused_imports ) ] +use test_tools::exposed::*; + +#[ cfg( feature = "enabled" ) ] +mod inc; diff --git a/module/blank/image_tools/License b/module/blank/image_tools/License index 6d5ef8559f..0804aed8e3 100644 --- a/module/blank/image_tools/License +++ b/module/blank/image_tools/License @@ -1,4 +1,4 @@ -Copyright Kostiantyn W and Out of the Box Systems (c) 2013-2024 +Copyright Kostiantyn Mysnyk and Out of the Box Systems (c) 2021-2024 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation diff --git a/module/blank/math_tools/License b/module/blank/math_tools/License index 6d5ef8559f..0804aed8e3 100644 --- a/module/blank/math_tools/License +++ b/module/blank/math_tools/License @@ -1,4 +1,4 @@ -Copyright Kostiantyn W and Out of the Box Systems (c) 2013-2024 +Copyright Kostiantyn Mysnyk and Out of the Box Systems (c) 2021-2024 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation diff --git a/module/blank/mindx12/License b/module/blank/mindx12/License index 6d5ef8559f..0804aed8e3 100644 --- a/module/blank/mindx12/License +++ b/module/blank/mindx12/License @@ -1,4 +1,4 @@ -Copyright Kostiantyn W and Out of the Box Systems (c) 2013-2024 +Copyright Kostiantyn Mysnyk and Out of the Box Systems (c) 2021-2024 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation diff --git a/module/blank/mingl/License b/module/blank/mingl/License index 6d5ef8559f..0804aed8e3 100644 --- a/module/blank/mingl/License +++ b/module/blank/mingl/License @@ -1,4 +1,4 @@ -Copyright Kostiantyn W and Out of the Box Systems (c) 2013-2024 +Copyright Kostiantyn Mysnyk and Out of the Box Systems (c) 2021-2024 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation diff --git a/module/blank/minmetal/License b/module/blank/minmetal/License index 6d5ef8559f..0804aed8e3 100644 --- a/module/blank/minmetal/License +++ b/module/blank/minmetal/License @@ -1,4 +1,4 @@ -Copyright Kostiantyn W and Out of the Box Systems (c) 2013-2024 +Copyright Kostiantyn Mysnyk and Out of the Box Systems (c) 2021-2024 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation diff --git a/module/blank/minopengl/License b/module/blank/minopengl/License index 6d5ef8559f..0804aed8e3 100644 --- a/module/blank/minopengl/License +++ b/module/blank/minopengl/License @@ -1,4 +1,4 @@ -Copyright Kostiantyn W and Out of the Box Systems (c) 2013-2024 +Copyright Kostiantyn Mysnyk and Out of the Box Systems (c) 2021-2024 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation diff --git a/module/blank/minvulkan/License b/module/blank/minvulkan/License index 6d5ef8559f..0804aed8e3 100644 --- a/module/blank/minvulkan/License +++ b/module/blank/minvulkan/License @@ -1,4 +1,4 @@ -Copyright Kostiantyn W and Out of the Box Systems (c) 2013-2024 +Copyright Kostiantyn Mysnyk and Out of the Box Systems (c) 2021-2024 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation diff --git a/module/blank/minwebgl/License b/module/blank/minwebgl/License index 6d5ef8559f..0804aed8e3 100644 --- a/module/blank/minwebgl/License +++ b/module/blank/minwebgl/License @@ -1,4 +1,4 @@ -Copyright Kostiantyn W and Out of the Box Systems (c) 2013-2024 +Copyright Kostiantyn Mysnyk and Out of the Box Systems (c) 2021-2024 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation diff --git a/module/blank/minwebgpu/License b/module/blank/minwebgpu/License index 6d5ef8559f..0804aed8e3 100644 --- a/module/blank/minwebgpu/License +++ b/module/blank/minwebgpu/License @@ -1,4 +1,4 @@ -Copyright Kostiantyn W and Out of the Box Systems (c) 2013-2024 +Copyright Kostiantyn Mysnyk and Out of the Box Systems (c) 2021-2024 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation diff --git a/module/blank/minwgpu/License b/module/blank/minwgpu/License index 6d5ef8559f..0804aed8e3 100644 --- a/module/blank/minwgpu/License +++ b/module/blank/minwgpu/License @@ -1,4 +1,4 @@ -Copyright Kostiantyn W and Out of the Box Systems (c) 2013-2024 +Copyright Kostiantyn Mysnyk and Out of the Box Systems (c) 2021-2024 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation diff --git a/module/blank/paths_tools/License b/module/blank/paths_tools/License index 6d5ef8559f..0804aed8e3 100644 --- a/module/blank/paths_tools/License +++ b/module/blank/paths_tools/License @@ -1,4 +1,4 @@ -Copyright Kostiantyn W and Out of the Box Systems (c) 2013-2024 +Copyright Kostiantyn Mysnyk and Out of the Box Systems (c) 2021-2024 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation diff --git a/module/blank/rustql/License b/module/blank/rustql/License index 6d5ef8559f..0804aed8e3 100644 --- a/module/blank/rustql/License +++ b/module/blank/rustql/License @@ -1,4 +1,4 @@ -Copyright Kostiantyn W and Out of the Box Systems (c) 2013-2024 +Copyright Kostiantyn Mysnyk and Out of the Box Systems (c) 2021-2024 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation diff --git a/module/blank/second_brain/License b/module/blank/second_brain/License index 6d5ef8559f..0804aed8e3 100644 --- a/module/blank/second_brain/License +++ b/module/blank/second_brain/License @@ -1,4 +1,4 @@ -Copyright Kostiantyn W and Out of the Box Systems (c) 2013-2024 +Copyright Kostiantyn Mysnyk and Out of the Box Systems (c) 2021-2024 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation diff --git a/module/blank/w4d/License b/module/blank/w4d/License index 6d5ef8559f..0804aed8e3 100644 --- a/module/blank/w4d/License +++ b/module/blank/w4d/License @@ -1,4 +1,4 @@ -Copyright Kostiantyn W and Out of the Box Systems (c) 2013-2024 +Copyright Kostiantyn Mysnyk and Out of the Box Systems (c) 2021-2024 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation diff --git a/module/blank/wlang/License b/module/blank/wlang/License index 6d5ef8559f..0804aed8e3 100644 --- a/module/blank/wlang/License +++ b/module/blank/wlang/License @@ -1,4 +1,4 @@ -Copyright Kostiantyn W and Out of the Box Systems (c) 2013-2024 +Copyright Kostiantyn Mysnyk and Out of the Box Systems (c) 2021-2024 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation diff --git a/module/core/clone_dyn/License b/module/core/clone_dyn/License index 6d5ef8559f..0804aed8e3 100644 --- a/module/core/clone_dyn/License +++ b/module/core/clone_dyn/License @@ -1,4 +1,4 @@ -Copyright Kostiantyn W and Out of the Box Systems (c) 2013-2024 +Copyright Kostiantyn Mysnyk and Out of the Box Systems (c) 2021-2024 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation diff --git a/module/core/clone_dyn_meta/License b/module/core/clone_dyn_meta/License index 6d5ef8559f..0804aed8e3 100644 --- a/module/core/clone_dyn_meta/License +++ b/module/core/clone_dyn_meta/License @@ -1,4 +1,4 @@ -Copyright Kostiantyn W and Out of the Box Systems (c) 2013-2024 +Copyright Kostiantyn Mysnyk and Out of the Box Systems (c) 2021-2024 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation diff --git a/module/core/clone_dyn_types/License b/module/core/clone_dyn_types/License index 6d5ef8559f..0804aed8e3 100644 --- a/module/core/clone_dyn_types/License +++ b/module/core/clone_dyn_types/License @@ -1,4 +1,4 @@ -Copyright Kostiantyn W and Out of the Box Systems (c) 2013-2024 +Copyright Kostiantyn Mysnyk and Out of the Box Systems (c) 2021-2024 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation diff --git a/module/core/collection_tools/License b/module/core/collection_tools/License index 6d5ef8559f..0804aed8e3 100644 --- a/module/core/collection_tools/License +++ b/module/core/collection_tools/License @@ -1,4 +1,4 @@ -Copyright Kostiantyn W and Out of the Box Systems (c) 2013-2024 +Copyright Kostiantyn Mysnyk and Out of the Box Systems (c) 2021-2024 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation diff --git a/module/core/data_type/License b/module/core/data_type/License index 6d5ef8559f..0804aed8e3 100644 --- a/module/core/data_type/License +++ b/module/core/data_type/License @@ -1,4 +1,4 @@ -Copyright Kostiantyn W and Out of the Box Systems (c) 2013-2024 +Copyright Kostiantyn Mysnyk and Out of the Box Systems (c) 2021-2024 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation diff --git a/module/core/derive_tools/License b/module/core/derive_tools/License index 6d5ef8559f..0804aed8e3 100644 --- a/module/core/derive_tools/License +++ b/module/core/derive_tools/License @@ -1,4 +1,4 @@ -Copyright Kostiantyn W and Out of the Box Systems (c) 2013-2024 +Copyright Kostiantyn Mysnyk and Out of the Box Systems (c) 2021-2024 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation diff --git a/module/core/derive_tools_meta/License b/module/core/derive_tools_meta/License index 6d5ef8559f..0804aed8e3 100644 --- a/module/core/derive_tools_meta/License +++ b/module/core/derive_tools_meta/License @@ -1,4 +1,4 @@ -Copyright Kostiantyn W and Out of the Box Systems (c) 2013-2024 +Copyright Kostiantyn Mysnyk and Out of the Box Systems (c) 2021-2024 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation diff --git a/module/core/diagnostics_tools/License b/module/core/diagnostics_tools/License index 6d5ef8559f..0804aed8e3 100644 --- a/module/core/diagnostics_tools/License +++ b/module/core/diagnostics_tools/License @@ -1,4 +1,4 @@ -Copyright Kostiantyn W and Out of the Box Systems (c) 2013-2024 +Copyright Kostiantyn Mysnyk and Out of the Box Systems (c) 2021-2024 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation diff --git a/module/core/error_tools/License b/module/core/error_tools/License index 6d5ef8559f..0804aed8e3 100644 --- a/module/core/error_tools/License +++ b/module/core/error_tools/License @@ -1,4 +1,4 @@ -Copyright Kostiantyn W and Out of the Box Systems (c) 2013-2024 +Copyright Kostiantyn Mysnyk and Out of the Box Systems (c) 2021-2024 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation diff --git a/module/core/for_each/License b/module/core/for_each/License index 6d5ef8559f..0804aed8e3 100644 --- a/module/core/for_each/License +++ b/module/core/for_each/License @@ -1,4 +1,4 @@ -Copyright Kostiantyn W and Out of the Box Systems (c) 2013-2024 +Copyright Kostiantyn Mysnyk and Out of the Box Systems (c) 2021-2024 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation diff --git a/module/core/format_tools/License b/module/core/format_tools/License index 6d5ef8559f..0804aed8e3 100644 --- a/module/core/format_tools/License +++ b/module/core/format_tools/License @@ -1,4 +1,4 @@ -Copyright Kostiantyn W and Out of the Box Systems (c) 2013-2024 +Copyright Kostiantyn Mysnyk and Out of the Box Systems (c) 2021-2024 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation diff --git a/module/core/former/License b/module/core/former/License index e3e9e057cf..c32986cee3 100644 --- a/module/core/former/License +++ b/module/core/former/License @@ -1,4 +1,4 @@ -Copyright Kostiantyn W and Out of the Box Systems (c) 2013-2024 +Copyright Kostiantyn Mysnyk and Out of the Box Systems (c) 2021-2024 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation diff --git a/module/core/former_meta/License b/module/core/former_meta/License index e3e9e057cf..c32986cee3 100644 --- a/module/core/former_meta/License +++ b/module/core/former_meta/License @@ -1,4 +1,4 @@ -Copyright Kostiantyn W and Out of the Box Systems (c) 2013-2024 +Copyright Kostiantyn Mysnyk and Out of the Box Systems (c) 2021-2024 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation diff --git a/module/core/former_types/License b/module/core/former_types/License index e3e9e057cf..c32986cee3 100644 --- a/module/core/former_types/License +++ b/module/core/former_types/License @@ -1,4 +1,4 @@ -Copyright Kostiantyn W and Out of the Box Systems (c) 2013-2024 +Copyright Kostiantyn Mysnyk and Out of the Box Systems (c) 2021-2024 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation diff --git a/module/core/fs_tools/License b/module/core/fs_tools/License index e3e9e057cf..c32986cee3 100644 --- a/module/core/fs_tools/License +++ b/module/core/fs_tools/License @@ -1,4 +1,4 @@ -Copyright Kostiantyn W and Out of the Box Systems (c) 2013-2024 +Copyright Kostiantyn Mysnyk and Out of the Box Systems (c) 2021-2024 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation diff --git a/module/core/implements/License b/module/core/implements/License index e3e9e057cf..c32986cee3 100644 --- a/module/core/implements/License +++ b/module/core/implements/License @@ -1,4 +1,4 @@ -Copyright Kostiantyn W and Out of the Box Systems (c) 2013-2024 +Copyright Kostiantyn Mysnyk and Out of the Box Systems (c) 2021-2024 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation diff --git a/module/core/impls_index/License b/module/core/impls_index/License index 6d5ef8559f..0804aed8e3 100644 --- a/module/core/impls_index/License +++ b/module/core/impls_index/License @@ -1,4 +1,4 @@ -Copyright Kostiantyn W and Out of the Box Systems (c) 2013-2024 +Copyright Kostiantyn Mysnyk and Out of the Box Systems (c) 2021-2024 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation diff --git a/module/core/impls_index_meta/License b/module/core/impls_index_meta/License index e3e9e057cf..c32986cee3 100644 --- a/module/core/impls_index_meta/License +++ b/module/core/impls_index_meta/License @@ -1,4 +1,4 @@ -Copyright Kostiantyn W and Out of the Box Systems (c) 2013-2024 +Copyright Kostiantyn Mysnyk and Out of the Box Systems (c) 2021-2024 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation diff --git a/module/core/include_md/License b/module/core/include_md/License index 6d5ef8559f..0804aed8e3 100644 --- a/module/core/include_md/License +++ b/module/core/include_md/License @@ -1,4 +1,4 @@ -Copyright Kostiantyn W and Out of the Box Systems (c) 2013-2024 +Copyright Kostiantyn Mysnyk and Out of the Box Systems (c) 2021-2024 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation diff --git a/module/core/inspect_type/License b/module/core/inspect_type/License index 6d5ef8559f..0804aed8e3 100644 --- a/module/core/inspect_type/License +++ b/module/core/inspect_type/License @@ -1,4 +1,4 @@ -Copyright Kostiantyn W and Out of the Box Systems (c) 2013-2024 +Copyright Kostiantyn Mysnyk and Out of the Box Systems (c) 2021-2024 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation diff --git a/module/core/interval_adapter/License b/module/core/interval_adapter/License index 6d5ef8559f..0804aed8e3 100644 --- a/module/core/interval_adapter/License +++ b/module/core/interval_adapter/License @@ -1,4 +1,4 @@ -Copyright Kostiantyn W and Out of the Box Systems (c) 2013-2024 +Copyright Kostiantyn Mysnyk and Out of the Box Systems (c) 2021-2024 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation diff --git a/module/core/is_slice/License b/module/core/is_slice/License index e3e9e057cf..c32986cee3 100644 --- a/module/core/is_slice/License +++ b/module/core/is_slice/License @@ -1,4 +1,4 @@ -Copyright Kostiantyn W and Out of the Box Systems (c) 2013-2024 +Copyright Kostiantyn Mysnyk and Out of the Box Systems (c) 2021-2024 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation diff --git a/module/core/iter_tools/License b/module/core/iter_tools/License index 6d5ef8559f..0804aed8e3 100644 --- a/module/core/iter_tools/License +++ b/module/core/iter_tools/License @@ -1,4 +1,4 @@ -Copyright Kostiantyn W and Out of the Box Systems (c) 2013-2024 +Copyright Kostiantyn Mysnyk and Out of the Box Systems (c) 2021-2024 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation diff --git a/module/core/macro_tools/License b/module/core/macro_tools/License index 6d5ef8559f..0804aed8e3 100644 --- a/module/core/macro_tools/License +++ b/module/core/macro_tools/License @@ -1,4 +1,4 @@ -Copyright Kostiantyn W and Out of the Box Systems (c) 2013-2024 +Copyright Kostiantyn Mysnyk and Out of the Box Systems (c) 2021-2024 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation diff --git a/module/core/mem_tools/License b/module/core/mem_tools/License index 6d5ef8559f..0804aed8e3 100644 --- a/module/core/mem_tools/License +++ b/module/core/mem_tools/License @@ -1,4 +1,4 @@ -Copyright Kostiantyn W and Out of the Box Systems (c) 2013-2024 +Copyright Kostiantyn Mysnyk and Out of the Box Systems (c) 2021-2024 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation diff --git a/module/core/meta_tools/License b/module/core/meta_tools/License index 6d5ef8559f..0804aed8e3 100644 --- a/module/core/meta_tools/License +++ b/module/core/meta_tools/License @@ -1,4 +1,4 @@ -Copyright Kostiantyn W and Out of the Box Systems (c) 2013-2024 +Copyright Kostiantyn Mysnyk and Out of the Box Systems (c) 2021-2024 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation diff --git a/module/core/mod_interface/License b/module/core/mod_interface/License index e3e9e057cf..c32986cee3 100644 --- a/module/core/mod_interface/License +++ b/module/core/mod_interface/License @@ -1,4 +1,4 @@ -Copyright Kostiantyn W and Out of the Box Systems (c) 2013-2024 +Copyright Kostiantyn Mysnyk and Out of the Box Systems (c) 2021-2024 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation diff --git a/module/core/mod_interface_meta/License b/module/core/mod_interface_meta/License index e3e9e057cf..c32986cee3 100644 --- a/module/core/mod_interface_meta/License +++ b/module/core/mod_interface_meta/License @@ -1,4 +1,4 @@ -Copyright Kostiantyn W and Out of the Box Systems (c) 2013-2024 +Copyright Kostiantyn Mysnyk and Out of the Box Systems (c) 2021-2024 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation diff --git a/module/core/process_tools/License b/module/core/process_tools/License index 6d5ef8559f..0804aed8e3 100644 --- a/module/core/process_tools/License +++ b/module/core/process_tools/License @@ -1,4 +1,4 @@ -Copyright Kostiantyn W and Out of the Box Systems (c) 2013-2024 +Copyright Kostiantyn Mysnyk and Out of the Box Systems (c) 2021-2024 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation diff --git a/module/core/program_tools/License b/module/core/program_tools/License index 6d5ef8559f..0804aed8e3 100644 --- a/module/core/program_tools/License +++ b/module/core/program_tools/License @@ -1,4 +1,4 @@ -Copyright Kostiantyn W and Out of the Box Systems (c) 2013-2024 +Copyright Kostiantyn Mysnyk and Out of the Box Systems (c) 2021-2024 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation diff --git a/module/core/proper_path_tools/License b/module/core/proper_path_tools/License index 6d5ef8559f..0804aed8e3 100644 --- a/module/core/proper_path_tools/License +++ b/module/core/proper_path_tools/License @@ -1,4 +1,4 @@ -Copyright Kostiantyn W and Out of the Box Systems (c) 2013-2024 +Copyright Kostiantyn Mysnyk and Out of the Box Systems (c) 2021-2024 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation diff --git a/module/core/reflect_tools/License b/module/core/reflect_tools/License index 6d5ef8559f..0804aed8e3 100644 --- a/module/core/reflect_tools/License +++ b/module/core/reflect_tools/License @@ -1,4 +1,4 @@ -Copyright Kostiantyn W and Out of the Box Systems (c) 2013-2024 +Copyright Kostiantyn Mysnyk and Out of the Box Systems (c) 2021-2024 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation diff --git a/module/core/reflect_tools_meta/License b/module/core/reflect_tools_meta/License index 6d5ef8559f..0804aed8e3 100644 --- a/module/core/reflect_tools_meta/License +++ b/module/core/reflect_tools_meta/License @@ -1,4 +1,4 @@ -Copyright Kostiantyn W and Out of the Box Systems (c) 2013-2024 +Copyright Kostiantyn Mysnyk and Out of the Box Systems (c) 2021-2024 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation diff --git a/module/core/strs_tools/License b/module/core/strs_tools/License index 6d5ef8559f..0804aed8e3 100644 --- a/module/core/strs_tools/License +++ b/module/core/strs_tools/License @@ -1,4 +1,4 @@ -Copyright Kostiantyn W and Out of the Box Systems (c) 2013-2024 +Copyright Kostiantyn Mysnyk and Out of the Box Systems (c) 2021-2024 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation diff --git a/module/core/test_tools/License b/module/core/test_tools/License index 6d5ef8559f..0804aed8e3 100644 --- a/module/core/test_tools/License +++ b/module/core/test_tools/License @@ -1,4 +1,4 @@ -Copyright Kostiantyn W and Out of the Box Systems (c) 2013-2024 +Copyright Kostiantyn Mysnyk and Out of the Box Systems (c) 2021-2024 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation diff --git a/module/core/time_tools/License b/module/core/time_tools/License index 6d5ef8559f..0804aed8e3 100644 --- a/module/core/time_tools/License +++ b/module/core/time_tools/License @@ -1,4 +1,4 @@ -Copyright Kostiantyn W and Out of the Box Systems (c) 2013-2024 +Copyright Kostiantyn Mysnyk and Out of the Box Systems (c) 2021-2024 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation diff --git a/module/core/typing_tools/License b/module/core/typing_tools/License index 6d5ef8559f..0804aed8e3 100644 --- a/module/core/typing_tools/License +++ b/module/core/typing_tools/License @@ -1,4 +1,4 @@ -Copyright Kostiantyn W and Out of the Box Systems (c) 2013-2024 +Copyright Kostiantyn Mysnyk and Out of the Box Systems (c) 2021-2024 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation diff --git a/module/core/variadic_from/License b/module/core/variadic_from/License index 6d5ef8559f..0804aed8e3 100644 --- a/module/core/variadic_from/License +++ b/module/core/variadic_from/License @@ -1,4 +1,4 @@ -Copyright Kostiantyn W and Out of the Box Systems (c) 2013-2024 +Copyright Kostiantyn Mysnyk and Out of the Box Systems (c) 2021-2024 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation diff --git a/module/core/wtools/License b/module/core/wtools/License index e3e9e057cf..c32986cee3 100644 --- a/module/core/wtools/License +++ b/module/core/wtools/License @@ -1,4 +1,4 @@ -Copyright Kostiantyn W and Out of the Box Systems (c) 2013-2024 +Copyright Kostiantyn Mysnyk and Out of the Box Systems (c) 2021-2024 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation diff --git a/module/move/assistant/License b/module/move/assistant/License index 6d5ef8559f..0804aed8e3 100644 --- a/module/move/assistant/License +++ b/module/move/assistant/License @@ -1,4 +1,4 @@ -Copyright Kostiantyn W and Out of the Box Systems (c) 2013-2024 +Copyright Kostiantyn Mysnyk and Out of the Box Systems (c) 2021-2024 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation diff --git a/module/move/crates_tools/License b/module/move/crates_tools/License index 6d5ef8559f..0804aed8e3 100644 --- a/module/move/crates_tools/License +++ b/module/move/crates_tools/License @@ -1,4 +1,4 @@ -Copyright Kostiantyn W and Out of the Box Systems (c) 2013-2024 +Copyright Kostiantyn Mysnyk and Out of the Box Systems (c) 2021-2024 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation diff --git a/module/move/deterministic_rand/License b/module/move/deterministic_rand/License index 6d5ef8559f..0804aed8e3 100644 --- a/module/move/deterministic_rand/License +++ b/module/move/deterministic_rand/License @@ -1,4 +1,4 @@ -Copyright Kostiantyn W and Out of the Box Systems (c) 2013-2024 +Copyright Kostiantyn Mysnyk and Out of the Box Systems (c) 2021-2024 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation diff --git a/module/move/graphs_tools/Cargo.toml b/module/move/graphs_tools/Cargo.toml index f0eeb97831..b5d158bc21 100644 --- a/module/move/graphs_tools/Cargo.toml +++ b/module/move/graphs_tools/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "graphs_tools" -version = "0.2.0" +version = "0.3.0" edition = "2021" authors = [ "Kostiantyn Wandalen ", diff --git a/module/move/graphs_tools/License b/module/move/graphs_tools/License index 6d5ef8559f..0804aed8e3 100644 --- a/module/move/graphs_tools/License +++ b/module/move/graphs_tools/License @@ -1,4 +1,4 @@ -Copyright Kostiantyn W and Out of the Box Systems (c) 2013-2024 +Copyright Kostiantyn Mysnyk and Out of the Box Systems (c) 2021-2024 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation diff --git a/module/move/optimization_tools/License b/module/move/optimization_tools/License index 6d5ef8559f..0804aed8e3 100644 --- a/module/move/optimization_tools/License +++ b/module/move/optimization_tools/License @@ -1,4 +1,4 @@ -Copyright Kostiantyn W and Out of the Box Systems (c) 2013-2024 +Copyright Kostiantyn Mysnyk and Out of the Box Systems (c) 2021-2024 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation diff --git a/module/move/plot_interface/License b/module/move/plot_interface/License index 6d5ef8559f..0804aed8e3 100644 --- a/module/move/plot_interface/License +++ b/module/move/plot_interface/License @@ -1,4 +1,4 @@ -Copyright Kostiantyn W and Out of the Box Systems (c) 2013-2024 +Copyright Kostiantyn Mysnyk and Out of the Box Systems (c) 2021-2024 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation diff --git a/module/move/refiner/License b/module/move/refiner/License index 6d5ef8559f..0804aed8e3 100644 --- a/module/move/refiner/License +++ b/module/move/refiner/License @@ -1,4 +1,4 @@ -Copyright Kostiantyn W and Out of the Box Systems (c) 2013-2024 +Copyright Kostiantyn Mysnyk and Out of the Box Systems (c) 2021-2024 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation diff --git a/module/move/sqlx_query/License b/module/move/sqlx_query/License index 6d5ef8559f..0804aed8e3 100644 --- a/module/move/sqlx_query/License +++ b/module/move/sqlx_query/License @@ -1,4 +1,4 @@ -Copyright Kostiantyn W and Out of the Box Systems (c) 2013-2024 +Copyright Kostiantyn Mysnyk and Out of the Box Systems (c) 2021-2024 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation diff --git a/module/move/wca/License b/module/move/wca/License index 6d5ef8559f..0804aed8e3 100644 --- a/module/move/wca/License +++ b/module/move/wca/License @@ -1,4 +1,4 @@ -Copyright Kostiantyn W and Out of the Box Systems (c) 2013-2024 +Copyright Kostiantyn Mysnyk and Out of the Box Systems (c) 2021-2024 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation diff --git a/module/move/willbe/License b/module/move/willbe/License index 6d5ef8559f..0804aed8e3 100644 --- a/module/move/willbe/License +++ b/module/move/willbe/License @@ -1,4 +1,4 @@ -Copyright Kostiantyn W and Out of the Box Systems (c) 2013-2024 +Copyright Kostiantyn Mysnyk and Out of the Box Systems (c) 2021-2024 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation diff --git a/module/move/wplot/License b/module/move/wplot/License index 6d5ef8559f..0804aed8e3 100644 --- a/module/move/wplot/License +++ b/module/move/wplot/License @@ -1,4 +1,4 @@ -Copyright Kostiantyn W and Out of the Box Systems (c) 2013-2024 +Copyright Kostiantyn Mysnyk and Out of the Box Systems (c) 2021-2024 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation diff --git a/module/postponed/_video_experiment/License b/module/postponed/_video_experiment/License index 6d5ef8559f..0804aed8e3 100644 --- a/module/postponed/_video_experiment/License +++ b/module/postponed/_video_experiment/License @@ -1,4 +1,4 @@ -Copyright Kostiantyn W and Out of the Box Systems (c) 2013-2024 +Copyright Kostiantyn Mysnyk and Out of the Box Systems (c) 2021-2024 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation diff --git a/module/postponed/automata_tools/License b/module/postponed/automata_tools/License index 6d5ef8559f..0804aed8e3 100644 --- a/module/postponed/automata_tools/License +++ b/module/postponed/automata_tools/License @@ -1,4 +1,4 @@ -Copyright Kostiantyn W and Out of the Box Systems (c) 2013-2024 +Copyright Kostiantyn Mysnyk and Out of the Box Systems (c) 2021-2024 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation diff --git a/module/postponed/non_std/License b/module/postponed/non_std/License index 6d5ef8559f..0804aed8e3 100644 --- a/module/postponed/non_std/License +++ b/module/postponed/non_std/License @@ -1,4 +1,4 @@ -Copyright Kostiantyn W and Out of the Box Systems (c) 2013-2024 +Copyright Kostiantyn Mysnyk and Out of the Box Systems (c) 2021-2024 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation diff --git a/module/postponed/std_tools/License b/module/postponed/std_tools/License index 6d5ef8559f..0804aed8e3 100644 --- a/module/postponed/std_tools/License +++ b/module/postponed/std_tools/License @@ -1,4 +1,4 @@ -Copyright Kostiantyn W and Out of the Box Systems (c) 2013-2024 +Copyright Kostiantyn Mysnyk and Out of the Box Systems (c) 2021-2024 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation diff --git a/module/postponed/std_x/License b/module/postponed/std_x/License index 6d5ef8559f..0804aed8e3 100644 --- a/module/postponed/std_x/License +++ b/module/postponed/std_x/License @@ -1,4 +1,4 @@ -Copyright Kostiantyn W and Out of the Box Systems (c) 2013-2024 +Copyright Kostiantyn Mysnyk and Out of the Box Systems (c) 2021-2024 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation diff --git a/module/postponed/type_constructor/License b/module/postponed/type_constructor/License index 6d5ef8559f..0804aed8e3 100644 --- a/module/postponed/type_constructor/License +++ b/module/postponed/type_constructor/License @@ -1,4 +1,4 @@ -Copyright Kostiantyn W and Out of the Box Systems (c) 2013-2024 +Copyright Kostiantyn Mysnyk and Out of the Box Systems (c) 2021-2024 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation diff --git a/module/postponed/wautomata/License b/module/postponed/wautomata/License index 6d5ef8559f..0804aed8e3 100644 --- a/module/postponed/wautomata/License +++ b/module/postponed/wautomata/License @@ -1,4 +1,4 @@ -Copyright Kostiantyn W and Out of the Box Systems (c) 2013-2024 +Copyright Kostiantyn Mysnyk and Out of the Box Systems (c) 2021-2024 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation diff --git a/module/postponed/wpublisher/License b/module/postponed/wpublisher/License index 6d5ef8559f..0804aed8e3 100644 --- a/module/postponed/wpublisher/License +++ b/module/postponed/wpublisher/License @@ -1,4 +1,4 @@ -Copyright Kostiantyn W and Out of the Box Systems (c) 2013-2024 +Copyright Kostiantyn Mysnyk and Out of the Box Systems (c) 2021-2024 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation diff --git a/module/template/template_alias/License b/module/template/template_alias/License index 6d5ef8559f..0804aed8e3 100644 --- a/module/template/template_alias/License +++ b/module/template/template_alias/License @@ -1,4 +1,4 @@ -Copyright Kostiantyn W and Out of the Box Systems (c) 2013-2024 +Copyright Kostiantyn Mysnyk and Out of the Box Systems (c) 2021-2024 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation diff --git a/module/template/template_blank/License b/module/template/template_blank/License index 6d5ef8559f..0804aed8e3 100644 --- a/module/template/template_blank/License +++ b/module/template/template_blank/License @@ -1,4 +1,4 @@ -Copyright Kostiantyn W and Out of the Box Systems (c) 2013-2024 +Copyright Kostiantyn Mysnyk and Out of the Box Systems (c) 2021-2024 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation diff --git a/module/template/template_procedural_macro/License b/module/template/template_procedural_macro/License index e3e9e057cf..c32986cee3 100644 --- a/module/template/template_procedural_macro/License +++ b/module/template/template_procedural_macro/License @@ -1,4 +1,4 @@ -Copyright Kostiantyn W and Out of the Box Systems (c) 2013-2024 +Copyright Kostiantyn Mysnyk and Out of the Box Systems (c) 2021-2024 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation diff --git a/module/template/template_procedural_macro_meta/License b/module/template/template_procedural_macro_meta/License index e3e9e057cf..c32986cee3 100644 --- a/module/template/template_procedural_macro_meta/License +++ b/module/template/template_procedural_macro_meta/License @@ -1,4 +1,4 @@ -Copyright Kostiantyn W and Out of the Box Systems (c) 2013-2024 +Copyright Kostiantyn Mysnyk and Out of the Box Systems (c) 2021-2024 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation diff --git a/module/template/template_procedural_macro_runtime/License b/module/template/template_procedural_macro_runtime/License index e3e9e057cf..c32986cee3 100644 --- a/module/template/template_procedural_macro_runtime/License +++ b/module/template/template_procedural_macro_runtime/License @@ -1,4 +1,4 @@ -Copyright Kostiantyn W and Out of the Box Systems (c) 2013-2024 +Copyright Kostiantyn Mysnyk and Out of the Box Systems (c) 2021-2024 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation From 9ec39a8618bb65463099b34639dc1ca6cf4025e0 Mon Sep 17 00:00:00 2001 From: wandalen Date: Wed, 30 Oct 2024 10:28:29 +0200 Subject: [PATCH 32/67] add cgtools and for path tools add from_strs method for AbsolutePath --- cgtools | 1 + module/core/derive_tools/src/lib.rs | 1 + module/core/proper_path_tools/src/path.rs | 1 + .../proper_path_tools/src/path/absolute_path.rs | 16 +++++++++++++++- .../tests/inc/path_canonicalize.rs | 8 ++++---- 5 files changed, 22 insertions(+), 5 deletions(-) create mode 160000 cgtools diff --git a/cgtools b/cgtools new file mode 160000 index 0000000000..f42bdc878f --- /dev/null +++ b/cgtools @@ -0,0 +1 @@ +Subproject commit f42bdc878f9414f7fd46b212454f615ab6ebcf61 diff --git a/module/core/derive_tools/src/lib.rs b/module/core/derive_tools/src/lib.rs index be57137ee6..62468ed1dc 100644 --- a/module/core/derive_tools/src/lib.rs +++ b/module/core/derive_tools/src/lib.rs @@ -144,6 +144,7 @@ pub mod exposed #[ cfg( feature = "derive_strum" ) ] #[ doc( inline ) ] pub use ::strum::*; + // qqq : xxx : name all #[ cfg( any( feature = "derive_variadic_from", feature = "type_variadic_from" ) ) ] #[ doc( inline ) ] diff --git a/module/core/proper_path_tools/src/path.rs b/module/core/proper_path_tools/src/path.rs index b11df6f466..313fb4fcd9 100644 --- a/module/core/proper_path_tools/src/path.rs +++ b/module/core/proper_path_tools/src/path.rs @@ -299,6 +299,7 @@ mod private Ok( std::format!( "{}_{}_{}_{}", timestamp, pid, tid, count ) ) } + /// Joins a list of file system paths into a single absolute path. /// /// This function takes a list of file system paths and joins them into a single path, diff --git a/module/core/proper_path_tools/src/path/absolute_path.rs b/module/core/proper_path_tools/src/path/absolute_path.rs index ba6e37f3ba..16fbf3ec78 100644 --- a/module/core/proper_path_tools/src/path/absolute_path.rs +++ b/module/core/proper_path_tools/src/path/absolute_path.rs @@ -23,7 +23,7 @@ mod private #[ cfg( feature="no_std" ) ] extern crate std; - + #[ cfg( feature="no_std" ) ] use alloc::string::String; @@ -80,6 +80,20 @@ mod private self.0 } + // qqq : xxx : cover by minimal tests + // qqq : xxx : make iterator over Paths also working + /// Joins a list of strs into a single absolute path. + pub fn from_strs< 'a, I >( iter : I ) -> Result< Self, io::Error > + where + I : Iterator< Item = &'a str >, + { + // Join all the path segments using join_paths + let joined_path = path::join_paths( iter.map( Path::new ) ); + + // Convert the joined PathBuf into an AbsolutePath + AbsolutePath::try_from( joined_path ) + } + } impl fmt::Display for AbsolutePath diff --git a/module/core/proper_path_tools/tests/inc/path_canonicalize.rs b/module/core/proper_path_tools/tests/inc/path_canonicalize.rs index 9e0c5c10e0..ae94013a4c 100644 --- a/module/core/proper_path_tools/tests/inc/path_canonicalize.rs +++ b/module/core/proper_path_tools/tests/inc/path_canonicalize.rs @@ -7,10 +7,10 @@ use the_module::path; fn assumptions() { - assert_eq!( PathBuf::from( "c:/src/" ).is_absolute(), false ); - assert_eq!( PathBuf::from( "/c/src/" ).is_absolute(), true ); - assert_eq!( PathBuf::from( "/c:/src/" ).is_absolute(), true ); - assert_eq!( PathBuf::from( "/c/src/" ).is_absolute(), true ); + // assert_eq!( PathBuf::from( "c:/src/" ).is_absolute(), false ); // qqq : xxx : this assumption is false on linux + // assert_eq!( PathBuf::from( "/c/src/" ).is_absolute(), true ); // qqq : xxx : this assumption is false, seems + // assert_eq!( PathBuf::from( "/c:/src/" ).is_absolute(), true ); // qqq : xxx : this assumption is false, too + // assert_eq!( PathBuf::from( "/c/src/" ).is_absolute(), true ); // qqq : xxx : this assumption is false, too } From 23c31a72d9f2ca2eec13d713ad7443eb64c8a2ad Mon Sep 17 00:00:00 2001 From: wbot <69343704+wtools-bot@users.noreply.github.com> Date: Wed, 30 Oct 2024 10:29:27 +0200 Subject: [PATCH 33/67] READY : new key directory format (#1467) * feat: key directory docs & makefile changes * fix: remove old key format from makefile --------- Co-authored-by: Viktor Dudnik <37380849+0x07C0@users.noreply.github.com> --- .../move/willbe/template/deploy/Makefile.hbs | 52 ++++------- .../template/deploy/deploy/aws/variables.tf | 6 +- .../deploy/deploy/hetzner/variables.tf | 6 +- .../willbe/template/deploy/key/.gitignore | 1 + .../move/willbe/template/deploy/key/Readme.md | 86 +++++++++++++------ 5 files changed, 81 insertions(+), 70 deletions(-) diff --git a/module/move/willbe/template/deploy/Makefile.hbs b/module/move/willbe/template/deploy/Makefile.hbs index 7f79a8adad..f978b887eb 100644 --- a/module/move/willbe/template/deploy/Makefile.hbs +++ b/module/move/willbe/template/deploy/Makefile.hbs @@ -1,16 +1,5 @@ .PHONY: deploy -# Secrets that can be provided via ENV vars or files in ./key/ directory. - -# Hetzner API token -export SECRET_CSP_HETZNER ?= $(shell cat key/SECRET_CSP_HETZNER 2> /dev/null) -# Cloud Storage file encryption key -export SECRET_STATE_ARCHIVE_KEY ?= $(shell cat key/SECRET_STATE_ARCHIVE_KEY 2> /dev/null) -# AWS Access Key id -export SECRET_AWS_ACCESS_KEY_ID ?= $(shell cat key/SECRET_AWS_ACCESS_KEY_ID 2> /dev/null) -# AWS Access Key -export SECRET_AWS_ACCESS_KEY ?= $(shell cat key/SECRET_AWS_ACCESS_KEY 2> /dev/null) - # Configuration variables for deployment. Can be edited for desired behavior. # Base terraform directory @@ -24,7 +13,7 @@ export TF_VAR_REPO_NAME ?= {{gcp_artifact_repo_name}} # Pushed image name export TF_VAR_IMAGE_NAME ?= {{docker_image_name}} # Path to the service account credentials -export google_sa_creds ?= key/service_account.json +export google_sa_creds ?= key/-service_account.json # Cloud Storage bucket name export TF_VAR_BUCKET_NAME ?= {{docker_image_name}}_tfstate # Specifies where to deploy the project. Possible values: `hetzner`, `gce`, `aws` @@ -45,40 +34,29 @@ export AWS_SECRET_ACCESS_KEY ?= $(SECRET_AWS_ACCESS_KEY) # Check Hetzner and deployment related keys check-hetzner-keys: - @[ -f key/SECRET_CSP_HETZNER ] \ - || [ ! -z "${SECRET_CSP_HETZNER}" ] \ - || { echo "ERROR: File key/SECRET_CSP_HETZNER does not exist"; exit 1; } + @[ ! -z "${SECRET_CSP_HETZNER}" ] \ + || { echo "ERROR: Key SECRET_CSP_HETZNER does not exist"; exit 1; } # Check AWS and deployment related keys check-aws-keys: - @[ -f key/SECRET_AWS_ACCESS_KEY_ID ] \ - || [ ! -z "${SECRET_AWS_ACCESS_KEY_ID}" ] \ - || echo "ERROR: File key/SECRET_AWS_ACCESS_KEY_ID does not exist" - @[ -f key/SECRET_AWS_ACCESS_KEY ] \ - || [ ! -z "${SECRET_AWS_ACCESS_KEY}" ] \ - || echo "ERROR: File key/SECRET_AWS_ACCESS_KEY does not exist" - @[ -f key/SECRET_AWS_ACCESS_KEY_ID ] \ - || [ ! -z "${SECRET_AWS_ACCESS_KEY_ID}" ] \ - || exit 1 - @[ -f key/SECRET_AWS_ACCESS_KEY ] \ - || [ ! -z "${SECRET_AWS_ACCESS_KEY}" ] \ - || exit 1 + @[ ! -z "${SECRET_AWS_ACCESS_KEY_ID}" ] \ + || echo "ERROR: Key SECRET_AWS_ACCESS_KEY_ID does not exist" + @[ ! -z "${SECRET_AWS_ACCESS_KEY}" ] \ + || echo "ERROR: Key SECRET_AWS_ACCESS_KEY does not exist" + @[ ! -z "${SECRET_AWS_ACCESS_KEY_ID}" ] || exit 1 + @[ ! -z "${SECRET_AWS_ACCESS_KEY}" ] || exit 1 check-gce-keys: @echo "All required GCE keys are the same as GCP keys" # Check if required GCP keys are present check-gcp-keys: - @[ -f key/service_account.json ] \ - || echo "ERROR: File key/service_account.json does not exist" - @[ -f key/SECRET_STATE_ARCHIVE_KEY ] \ - || [ ! -z "${SECRET_STATE_ARCHIVE_KEY}" ] \ - || echo "ERROR: File key/SECRET_STATE_ARCHIVE_KEY does not exist" - @[ -f key/service_account.json ] \ - || exit 1 - @[ -f key/SECRET_STATE_ARCHIVE_KEY ] \ - || [ ! -z "${SECRET_STATE_ARCHIVE_KEY}" ] \ - || exit 1 + @[ -f key/-service_account.json ] \ + || echo "ERROR: Key file key/-service_account.json does not exist" + @[ ! -z "${SECRET_STATE_ARCHIVE_KEY}" ] \ + || echo "ERROR: Key SECRET_STATE_ARCHIVE_KEY does not exist" + @[ -f key/-service_account.json ] || exit 1 + @[ ! -z "${SECRET_STATE_ARCHIVE_KEY}" ] || exit 1 # Start local docker container start: diff --git a/module/move/willbe/template/deploy/deploy/aws/variables.tf b/module/move/willbe/template/deploy/deploy/aws/variables.tf index ed15e05028..c536019a13 100644 --- a/module/move/willbe/template/deploy/deploy/aws/variables.tf +++ b/module/move/willbe/template/deploy/deploy/aws/variables.tf @@ -20,15 +20,15 @@ variable "IMAGE_NAME" { # Google Cloud Platform credentials data "local_sensitive_file" "service_account_creds" { - filename = "${path.module}/../../key/service_account.json" + filename = "${path.module}/../../key/-service_account.json" } # Private key for SSH connection data "local_sensitive_file" "ssh_private_key" { - filename = "${path.module}/../../key/rsa_ssh_key" + filename = "${path.module}/../../key/-rsa_ssh_key" } # Public key for SSH connection data "local_sensitive_file" "ssh_public_key" { - filename = "${path.module}/../../key/rsa_ssh_key.pub" + filename = "${path.module}/../../key/-rsa_ssh_key.pub" } diff --git a/module/move/willbe/template/deploy/deploy/hetzner/variables.tf b/module/move/willbe/template/deploy/deploy/hetzner/variables.tf index 4d445400eb..a6c27db413 100644 --- a/module/move/willbe/template/deploy/deploy/hetzner/variables.tf +++ b/module/move/willbe/template/deploy/deploy/hetzner/variables.tf @@ -25,15 +25,15 @@ variable "IMAGE_NAME" { # Google Cloud Platform credentials data "local_sensitive_file" "service_account_creds" { - filename = "${path.module}/../../key/service_account.json" + filename = "${path.module}/../../key/-service_account.json" } # Private key for SSH connection data "local_sensitive_file" "ssh_private_key" { - filename = "${path.module}/../../key/rsa_ssh_key" + filename = "${path.module}/../../key/-rsa_ssh_key" } # Public key for SSH connection data "local_sensitive_file" "ssh_public_key" { - filename = "${path.module}/../../key/rsa_ssh_key.pub" + filename = "${path.module}/../../key/-rsa_ssh_key.pub" } diff --git a/module/move/willbe/template/deploy/key/.gitignore b/module/move/willbe/template/deploy/key/.gitignore index 38b7807347..96870e1f6b 100644 --- a/module/move/willbe/template/deploy/key/.gitignore +++ b/module/move/willbe/template/deploy/key/.gitignore @@ -2,3 +2,4 @@ !.gitignore !*.md !pack.sh +-* diff --git a/module/move/willbe/template/deploy/key/Readme.md b/module/move/willbe/template/deploy/key/Readme.md index 84fc765608..d46ad6df48 100644 --- a/module/move/willbe/template/deploy/key/Readme.md +++ b/module/move/willbe/template/deploy/key/Readme.md @@ -1,50 +1,82 @@ -# Deploy credentials +# Keys -A list of all keys you'd need to deploy your project on different hosts. +This document provides a concise example of an environment configuration script, used to set up environment variables for a project. +These variables configure application behavior without altering the code. -- [Deploy credentials](#deploy-credentials) - - [Files](#files) - - [Env vars](#env-vars) +- [Keys](#keys) + - [Examples](#examples) + - [`-gcp.sh`](#-gcpsh) + - [`-hetzner.sh`](#-hetznersh) + - [`-aws.sh`](#-awssh) + - [How to Run](#how-to-run) - [Retrieving keys](#retrieving-keys) - [How to get `service_account.json`](#how-to-get-service_accountjson) - [How to get `SECRET_STATE_ARCHIVE_KEY`](#how-to-get-secret_state_archive_key) - [How to get `SECRET_CSP_HETZNER`](#how-to-get-secret_csp_hetzner) - [How to get `SECRET_AWS_ACCESS_KEY_ID` and `SECRET_AWS_ACCESS_KEY`](#how-to-get-secret_aws_access_key_id-and-secret_aws_access_key) -## Files -All secrets can be provided as files in current directory: +## Examples -- [service_account.json](./service_account.json) - default credentials for the service account to use in deployment. -- [rsa_ssh_key](./rsa_ssh_key) - SSH Private key that will be used for redeployment. -- [rsa_ssh_key.pub](./rsa_ssh_key.pub) - SSH Private key that will be used for redeployment. -- [`SECRET_STATE_ARCHIVE_KEY`](./SECRET_STATE_ARCHIVE_KEY) - [📃] base64 encoded AES256 key to encrypt and decrypt .tfstate files. -- [`SECRET_CSP_HETZNER`](./SECRET_CSP_HETZNER) - [📃] Hetzner token for deploying a server. -- [`SECRET_AWS_ACCESS_KEY_ID`](./SECRET_AWS_ACCESS_KEY_ID) - [📃] Access Key ID from AWS Credentials. Created at the same time as the Access Key itself. -- [`SECRET_AWS_ACCESS_KEY`](./SECRET_AWS_ACCESS_KEY) - [📃] Access Key for AWS API. Has to be accompanied with respectful Access Key ID. +### `-gcp.sh` -## Env vars +Contents example for the file `-gcp.sh`. This is a required configuration for all deploy targets. -Some secrets can be presented as an env var: +```bash +#!/bin/bash +CSP=gce +SECRET_STATE_ARCHIVE_KEY=qK1/4m60aZvclYi4bZFeBl8GxpyWcJ2iEevHN+uMy7w= -- [`SECRET_STATE_ARCHIVE_KEY`](./SECRET_STATE_ARCHIVE_KEY) - [📃] base64 encoded AES256 key to encrypt and decrypt .tfstate files. -- [`SECRET_CSP_HETZNER`](./SECRET_CSP_HETZNER) - [📃] Hetzner token for deploying a server. -- [`SECRET_AWS_ACCESS_KEY_ID`](./SECRET_AWS_ACCESS_KEY_ID) - [📃] Access Key ID from AWS Credentials. Created at the same time as the Access Key itself. -- [`SECRET_AWS_ACCESS_KEY`](./SECRET_AWS_ACCESS_KEY) - [📃] Access Key for AWS API. Has to be accompanied with respectful Access Key ID. +FILE_PATH="$( realpath -qms "${BASH_SOURCE[0]:-$PWD}" )" +DIR_PATH="${FILE_PATH%/*}" +head -c -1 << EOF > ${DIR_PATH}/-service_account.json +{ + // Your service_account information +} +EOF +``` + +- `CSP`: (Optional) Specifies deployment to GCE. +- `SECRET_STATE_ARCHIVE_KEY`: Base64 encoded AES256 key to encrypt and decrypt .tfstate files. +- `-service_account.json`: Default credentials for the service account to use in deployment. + +### `-hetzner.sh` + +Contents example for the file `-hetzner.sh`: + +```bash +CSP=hetzner +SECRET_CSP_HETZNER=your_token_here +``` -Env vars have a higher priority then the files. +- `CSP`: Specifies deployment to Hetzner. +- `SECRET_CSP_HETZNER`: Hetzner token for deploying a server. -For ENV [📃] secrets values can be placed in files in this directory for automatic exporting to env during deployment. +### `-aws.sh` -Example of a file that will be pulled to env vars: +Contents example for the file `-aws.sh`: -File name: `SECRET_CSP_HETZNER` -File contents: +```bash +CSP=aws +SECRET_AWS_ACCESS_KEY_ID=aws_credentials_here +SECRET_AWS_ACCESS_KEY=aws_credentials_here ``` -hetzner_token_123 + +- `CSP`: Specifies deployment to AWS. +- `SECRET_AWS_ACCESS_KEY_ID`: Access Key ID from AWS Credentials. Created at the same time as the Access Key itself. +- `SECRET_AWS_ACCESS_KEY`: Access Key for AWS API. Has to be accompanied with respectful Access Key ID. + +## How to Run + +To apply these variables to your current shell session, use: + +```bash +. ./key/-gcp.sh +. ./key/-hetzner.sh ``` -Will export a variable to env like so `SECRET_CSP_HETZNER=hetzner_token_123` +This command sources the script, making the variables available in your current session and allowing deployment to Hetzner. +Ensure `-env.sh` is in the `key` directory relative to your current location. ## Retrieving keys From 1ce4ea61005d7e76c6c9a97ec7dcb118530383ae Mon Sep 17 00:00:00 2001 From: wbot <69343704+wtools-bot@users.noreply.github.com> Date: Wed, 30 Oct 2024 12:03:09 +0200 Subject: [PATCH 34/67] AUTO : Forward from refactoring_1 to alpha (#1468) publishing --- Cargo.toml | 20 ++++++++++---------- module/core/clone_dyn_types/Cargo.toml | 2 +- module/core/collection_tools/Cargo.toml | 2 +- module/core/former_types/Cargo.toml | 2 +- module/core/interval_adapter/Cargo.toml | 2 +- module/core/iter_tools/Cargo.toml | 2 +- module/core/macro_tools/Cargo.toml | 6 +++--- module/core/test_tools/Cargo.toml | 4 ++-- module/core/test_tools/src/lib.rs | 4 ++-- 9 files changed, 22 insertions(+), 22 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index fefc262f5f..ca0d63d237 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -98,7 +98,7 @@ default-features = false # path = "module/core/type_constructor_derive_pair_meta" [workspace.dependencies.interval_adapter] -version = "~0.24.0" +version = "~0.25.0" path = "module/core/interval_adapter" default-features = false features = [ "enabled" ] @@ -110,7 +110,7 @@ default-features = false features = [ "enabled" ] [workspace.dependencies.collection_tools] -version = "~0.12.0" +version = "~0.13.0" path = "module/core/collection_tools" default-features = false @@ -176,7 +176,7 @@ path = "module/core/clone_dyn_meta" features = [ "enabled" ] [workspace.dependencies.clone_dyn_types] -version = "~0.23.0" +version = "~0.24.0" path = "module/core/clone_dyn_types" default-features = false features = [ "enabled" ] @@ -201,7 +201,7 @@ default-features = false ## iter [workspace.dependencies.iter_tools] -version = "~0.21.0" +version = "~0.22.0" path = "module/core/iter_tools" default-features = false @@ -234,7 +234,7 @@ path = "module/core/former_meta" default-features = false [workspace.dependencies.former_types] -version = "~2.8.0" +version = "~2.9.0" path = "module/core/former_types" default-features = false @@ -279,7 +279,7 @@ default-features = false ## macro tools [workspace.dependencies.macro_tools] -version = "~0.40.0" +version = "~0.41.0" path = "module/core/macro_tools" default-features = false @@ -379,10 +379,10 @@ version = "~0.10.0" path = "module/core/process_tools" default-features = false -[workspace.dependencies.process_tools_published] -package = "process_tools" -version = "~0.9.0" -default-features = false +# [workspace.dependencies.process_tools_published] +# package = "process_tools" +# version = "~0.9.0" +# default-features = false ## test diff --git a/module/core/clone_dyn_types/Cargo.toml b/module/core/clone_dyn_types/Cargo.toml index b9c3e6ee14..65eec4f684 100644 --- a/module/core/clone_dyn_types/Cargo.toml +++ b/module/core/clone_dyn_types/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clone_dyn_types" -version = "0.23.0" +version = "0.24.0" edition = "2021" authors = [ "Kostiantyn Wandalen ", diff --git a/module/core/collection_tools/Cargo.toml b/module/core/collection_tools/Cargo.toml index 58c96881f9..96ad524cc9 100644 --- a/module/core/collection_tools/Cargo.toml +++ b/module/core/collection_tools/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "collection_tools" -version = "0.12.0" +version = "0.13.0" edition = "2021" authors = [ "Kostiantyn Wandalen ", diff --git a/module/core/former_types/Cargo.toml b/module/core/former_types/Cargo.toml index 6baa5017e7..9277639847 100644 --- a/module/core/former_types/Cargo.toml +++ b/module/core/former_types/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "former_types" -version = "2.8.0" +version = "2.9.0" edition = "2021" authors = [ "Kostiantyn Wandalen ", diff --git a/module/core/interval_adapter/Cargo.toml b/module/core/interval_adapter/Cargo.toml index cf0fd7fe6c..ba40cf4f31 100644 --- a/module/core/interval_adapter/Cargo.toml +++ b/module/core/interval_adapter/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "interval_adapter" -version = "0.24.0" +version = "0.25.0" edition = "2021" authors = [ "Kostiantyn Wandalen ", diff --git a/module/core/iter_tools/Cargo.toml b/module/core/iter_tools/Cargo.toml index 68cafe52bf..0ab282194a 100644 --- a/module/core/iter_tools/Cargo.toml +++ b/module/core/iter_tools/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "iter_tools" -version = "0.21.0" +version = "0.22.0" edition = "2021" authors = [ "Kostiantyn Wandalen ", diff --git a/module/core/macro_tools/Cargo.toml b/module/core/macro_tools/Cargo.toml index 1213064721..15c70c5298 100644 --- a/module/core/macro_tools/Cargo.toml +++ b/module/core/macro_tools/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "macro_tools" -version = "0.40.0" +version = "0.41.0" edition = "2021" authors = [ "Kostiantyn Wandalen ", @@ -108,5 +108,5 @@ iter_tools = { workspace = true, features = [ "iter_trait" ] } clone_dyn_types = { workspace = true, features = [] } former_types = { workspace = true, features = [ "types_component_assign" ] } -[dev-dependencies] -test_tools = { workspace = true } +# [dev-dependencies] +# test_tools = { workspace = true } diff --git a/module/core/test_tools/Cargo.toml b/module/core/test_tools/Cargo.toml index 0ac5366bb4..2df25d0312 100644 --- a/module/core/test_tools/Cargo.toml +++ b/module/core/test_tools/Cargo.toml @@ -59,7 +59,7 @@ enabled = [ "typing_tools/enabled", "data_type/enabled", "diagnostics_tools/enabled", - "process_tools_published/enabled", + "process_tools/enabled", "collection_tools/enabled", ] # nightly = [ "typing_tools/nightly" ] @@ -83,7 +83,7 @@ mem_tools = { workspace = true, features = [ "full" ] } typing_tools = { workspace = true, features = [ "full" ] } data_type = { workspace = true, features = [ "full" ] } diagnostics_tools = { workspace = true, features = [ "full" ] } -process_tools_published = { workspace = true, features = [ "full" ] } +process_tools = { workspace = true, features = [ "full" ] } collection_tools = { workspace = true, features = [ "full" ] } # former_stable = { workspace = true, features = [ "full" ] } diff --git a/module/core/test_tools/src/lib.rs b/module/core/test_tools/src/lib.rs index ba768f7641..cbac84274c 100644 --- a/module/core/test_tools/src/lib.rs +++ b/module/core/test_tools/src/lib.rs @@ -42,10 +42,10 @@ pub mod dependency #[ doc( inline ) ] #[ allow( unused_imports ) ] - pub use ::process_tools_published; + pub use ::process_tools; #[ doc( inline ) ] #[ allow( unused_imports ) ] - pub use ::process_tools_published as process_tools; + pub use ::process_tools as process_tools; } From aa0de549b200f217e4b8a57d6f60fad04172f1c0 Mon Sep 17 00:00:00 2001 From: Wandalen Date: Tue, 5 Nov 2024 09:42:37 +0200 Subject: [PATCH 35/67] NOT READY : Global Refactoring (#1469) async crates --- .github/workflows/module_async_from_push.yml | 24 ++ .github/workflows/module_async_tools_push.yml | 24 ++ .github/workflows/module_graphtools_push.yml | 24 ++ .github/workflows/module_mindx_12_push.yml | 24 ++ .github/workflows/module_mingl_push.yml | 24 ++ .github/workflows/module_minmetal_push.yml | 24 ++ .github/workflows/module_minopengl_push.yml | 24 ++ .github/workflows/module_minvulkan_push.yml | 24 ++ .github/workflows/module_minwebgl_push.yml | 24 ++ .github/workflows/module_minwebgpu_push.yml | 24 ++ .github/workflows/module_minwgpu_push.yml | 24 ++ .../module_proper_path_tools_push.yml | 2 +- .github/workflows/module_pth_push.yml | 24 ++ Cargo.toml | 124 ++++--- Readme.md | 12 +- module/alias/wtest_basic/Cargo.toml | 6 +- module/blank/proper_path_tools/Cargo.toml | 34 ++ .../{core => blank}/proper_path_tools/License | 0 module/blank/proper_path_tools/Readme.md | 33 ++ module/blank/proper_path_tools/src/lib.rs | 11 + .../proper_path_tools/tests/inc/basic_test.rs | 7 + .../blank/proper_path_tools/tests/inc/mod.rs | 4 + .../proper_path_tools/tests/smoke_test.rs | 12 + .../proper_path_tools/tests/tests.rs | 0 module/core/async_from/Cargo.toml | 38 +++ module/core/async_from/License | 22 ++ module/core/async_from/Readme.md | 91 +++++ module/core/async_from/src/lib.rs | 316 ++++++++++++++++++ .../core/async_from/tests/inc/basic_test.rs | 84 +++++ module/core/async_from/tests/inc/mod.rs | 3 + module/core/async_from/tests/tests.rs | 9 + module/core/async_tools/Cargo.toml | 39 +++ module/core/async_tools/License | 22 ++ module/core/async_tools/Readme.md | 6 + module/core/async_tools/src/lib.rs | 79 +++++ .../core/async_tools/tests/inc/basic_test.rs | 84 +++++ module/core/async_tools/tests/inc/mod.rs | 3 + module/core/async_tools/tests/tests.rs | 10 + module/core/clone_dyn/Cargo.toml | 3 +- module/core/clone_dyn/src/lib.rs | 1 - module/core/clone_dyn_meta/Cargo.toml | 2 +- module/core/clone_dyn_types/Cargo.toml | 2 +- module/core/collection_tools/Cargo.toml | 4 +- module/core/data_type/Cargo.toml | 29 +- module/core/data_type/src/lib.rs | 14 +- module/core/derive_tools/Cargo.toml | 2 +- module/core/derive_tools_meta/Cargo.toml | 2 +- module/core/diagnostics_tools/Cargo.toml | 2 +- module/core/error_tools/Cargo.toml | 2 +- module/core/for_each/Cargo.toml | 2 +- module/core/former/Cargo.toml | 4 +- module/core/former_meta/Cargo.toml | 4 +- module/core/former_types/Cargo.toml | 4 +- module/core/implements/Cargo.toml | 2 +- module/core/impls_index/Cargo.toml | 2 +- module/core/impls_index_meta/Cargo.toml | 2 +- module/core/inspect_type/Cargo.toml | 2 +- module/core/interval_adapter/Cargo.toml | 2 +- module/core/is_slice/Cargo.toml | 2 +- module/core/iter_tools/Cargo.toml | 2 +- module/core/iter_tools/src/iter.rs | 2 + module/core/macro_tools/Cargo.toml | 2 +- module/core/mem_tools/Cargo.toml | 2 +- module/core/meta_tools/Cargo.toml | 2 +- module/core/mod_interface/Cargo.toml | 2 +- module/core/mod_interface_meta/Cargo.toml | 2 +- module/core/mod_interface_meta/src/lib.rs | 2 +- module/core/process_tools/Cargo.toml | 6 +- module/core/program_tools/Cargo.toml | 4 +- module/core/proper_path_tools/Readme.md | 35 -- .../{proper_path_tools => pth}/Cargo.toml | 12 +- module/core/pth/License | 22 ++ module/core/pth/Readme.md | 51 +++ module/core/pth/src/as_path.rs | 71 ++++ .../{proper_path_tools => pth}/src/lib.rs | 18 +- .../{proper_path_tools => pth}/src/path.rs | 81 +++-- .../src/path/absolute_path.rs | 228 ++++++------- .../src/path/canonical_path.rs | 53 ++- .../src/path/current_path.rs | 41 ++- module/core/pth/src/path/joining.rs | 191 +++++++++++ .../src/path/native_path.rs | 52 ++- .../src/transitive.rs | 4 +- module/core/pth/src/try_into_cow_path.rs | 111 ++++++ module/core/pth/src/try_into_path.rs | 109 ++++++ .../tests/experiment.rs | 2 +- .../core/pth/tests/inc/absolute_path_test.rs | 5 + .../inc/absolute_path_test/basic_test.rs} | 9 - .../inc/absolute_path_test/from_paths_test.rs | 92 +++++ .../inc/absolute_path_test/try_from_test.rs | 55 +++ module/core/pth/tests/inc/as_path_test.rs | 103 ++++++ .../tests/inc/current_path.rs | 0 .../tests/inc/mod.rs | 13 +- .../tests/inc/path_canonicalize.rs | 0 .../tests/inc/path_change_ext.rs | 0 .../tests/inc/path_common.rs | 0 .../tests/inc/path_ext.rs | 0 .../tests/inc/path_exts.rs | 0 .../tests/inc/path_is_glob.rs | 0 .../tests/inc/path_join_fn_test.rs} | 58 ++-- .../pth/tests/inc/path_join_trait_test.rs | 206 ++++++++++++ .../tests/inc/path_normalize.rs | 0 .../tests/inc/path_relative.rs | 0 .../tests/inc/path_unique_folder_name.rs | 0 .../tests/inc/rebase_path.rs | 0 .../tests/inc/transitive.rs | 4 +- .../pth/tests/inc/try_into_cow_path_test.rs | 124 +++++++ .../core/pth/tests/inc/try_into_path_test.rs | 117 +++++++ .../tests/inc/without_ext.rs | 0 .../tests/smoke_test.rs | 0 module/core/pth/tests/tests.rs | 9 + module/core/strs_tools/Cargo.toml | 2 +- module/core/test_tools/Cargo.toml | 2 +- module/core/test_tools/src/lib.rs | 7 +- module/core/typing_tools/Cargo.toml | 2 +- module/core/variadic_from/Cargo.toml | 2 +- module/core/wtools/Cargo.toml | 3 - module/move/crates_tools/Cargo.toml | 2 +- module/move/unitore/Cargo.toml | 2 +- module/move/unitore/src/action/config.rs | 4 +- module/move/unitore/src/entity/config.rs | 2 +- module/move/unitore/tests/config_add.rs | 2 +- module/move/unitore/tests/config_delete.rs | 2 +- module/move/unitore/tests/frames_download.rs | 6 +- module/move/unitore/tests/query_execute.rs | 20 +- module/move/unitore/tests/table_list.rs | 4 +- module/move/unitore/tests/tables_list.rs | 2 +- module/move/wca/Cargo.toml | 2 +- module/move/willbe/Cargo.toml | 6 +- .../action/readme_modules_headers_renew.rs | 2 +- module/move/willbe/src/tool/path.rs | 4 +- module/move/willbe/src/wtools.rs | 6 +- .../type_constructor/tests/inc/mod.rs | 4 +- .../tests/inc/prelude_test.rs | 136 ++++---- step/Cargo.toml | 2 +- 134 files changed, 3058 insertions(+), 498 deletions(-) create mode 100644 .github/workflows/module_async_from_push.yml create mode 100644 .github/workflows/module_async_tools_push.yml create mode 100644 .github/workflows/module_graphtools_push.yml create mode 100644 .github/workflows/module_mindx_12_push.yml create mode 100644 .github/workflows/module_mingl_push.yml create mode 100644 .github/workflows/module_minmetal_push.yml create mode 100644 .github/workflows/module_minopengl_push.yml create mode 100644 .github/workflows/module_minvulkan_push.yml create mode 100644 .github/workflows/module_minwebgl_push.yml create mode 100644 .github/workflows/module_minwebgpu_push.yml create mode 100644 .github/workflows/module_minwgpu_push.yml create mode 100644 .github/workflows/module_pth_push.yml create mode 100644 module/blank/proper_path_tools/Cargo.toml rename module/{core => blank}/proper_path_tools/License (100%) create mode 100644 module/blank/proper_path_tools/Readme.md create mode 100644 module/blank/proper_path_tools/src/lib.rs create mode 100644 module/blank/proper_path_tools/tests/inc/basic_test.rs create mode 100644 module/blank/proper_path_tools/tests/inc/mod.rs create mode 100644 module/blank/proper_path_tools/tests/smoke_test.rs rename module/{core => blank}/proper_path_tools/tests/tests.rs (100%) create mode 100644 module/core/async_from/Cargo.toml create mode 100644 module/core/async_from/License create mode 100644 module/core/async_from/Readme.md create mode 100644 module/core/async_from/src/lib.rs create mode 100644 module/core/async_from/tests/inc/basic_test.rs create mode 100644 module/core/async_from/tests/inc/mod.rs create mode 100644 module/core/async_from/tests/tests.rs create mode 100644 module/core/async_tools/Cargo.toml create mode 100644 module/core/async_tools/License create mode 100644 module/core/async_tools/Readme.md create mode 100644 module/core/async_tools/src/lib.rs create mode 100644 module/core/async_tools/tests/inc/basic_test.rs create mode 100644 module/core/async_tools/tests/inc/mod.rs create mode 100644 module/core/async_tools/tests/tests.rs delete mode 100644 module/core/proper_path_tools/Readme.md rename module/core/{proper_path_tools => pth}/Cargo.toml (87%) create mode 100644 module/core/pth/License create mode 100644 module/core/pth/Readme.md create mode 100644 module/core/pth/src/as_path.rs rename module/core/{proper_path_tools => pth}/src/lib.rs (67%) rename module/core/{proper_path_tools => pth}/src/path.rs (95%) rename module/core/{proper_path_tools => pth}/src/path/absolute_path.rs (54%) rename module/core/{proper_path_tools => pth}/src/path/canonical_path.rs (85%) rename module/core/{proper_path_tools => pth}/src/path/current_path.rs (67%) create mode 100644 module/core/pth/src/path/joining.rs rename module/core/{proper_path_tools => pth}/src/path/native_path.rs (86%) rename module/core/{proper_path_tools => pth}/src/transitive.rs (98%) create mode 100644 module/core/pth/src/try_into_cow_path.rs create mode 100644 module/core/pth/src/try_into_path.rs rename module/core/{proper_path_tools => pth}/tests/experiment.rs (94%) create mode 100644 module/core/pth/tests/inc/absolute_path_test.rs rename module/core/{proper_path_tools/tests/inc/absolute_path.rs => pth/tests/inc/absolute_path_test/basic_test.rs} (93%) create mode 100644 module/core/pth/tests/inc/absolute_path_test/from_paths_test.rs create mode 100644 module/core/pth/tests/inc/absolute_path_test/try_from_test.rs create mode 100644 module/core/pth/tests/inc/as_path_test.rs rename module/core/{proper_path_tools => pth}/tests/inc/current_path.rs (100%) rename module/core/{proper_path_tools => pth}/tests/inc/mod.rs (57%) rename module/core/{proper_path_tools => pth}/tests/inc/path_canonicalize.rs (100%) rename module/core/{proper_path_tools => pth}/tests/inc/path_change_ext.rs (100%) rename module/core/{proper_path_tools => pth}/tests/inc/path_common.rs (100%) rename module/core/{proper_path_tools => pth}/tests/inc/path_ext.rs (100%) rename module/core/{proper_path_tools => pth}/tests/inc/path_exts.rs (100%) rename module/core/{proper_path_tools => pth}/tests/inc/path_is_glob.rs (100%) rename module/core/{proper_path_tools/tests/inc/path_join.rs => pth/tests/inc/path_join_fn_test.rs} (76%) create mode 100644 module/core/pth/tests/inc/path_join_trait_test.rs rename module/core/{proper_path_tools => pth}/tests/inc/path_normalize.rs (100%) rename module/core/{proper_path_tools => pth}/tests/inc/path_relative.rs (100%) rename module/core/{proper_path_tools => pth}/tests/inc/path_unique_folder_name.rs (100%) rename module/core/{proper_path_tools => pth}/tests/inc/rebase_path.rs (100%) rename module/core/{proper_path_tools => pth}/tests/inc/transitive.rs (96%) create mode 100644 module/core/pth/tests/inc/try_into_cow_path_test.rs create mode 100644 module/core/pth/tests/inc/try_into_path_test.rs rename module/core/{proper_path_tools => pth}/tests/inc/without_ext.rs (100%) rename module/core/{proper_path_tools => pth}/tests/smoke_test.rs (100%) create mode 100644 module/core/pth/tests/tests.rs diff --git a/.github/workflows/module_async_from_push.yml b/.github/workflows/module_async_from_push.yml new file mode 100644 index 0000000000..dd4257fe08 --- /dev/null +++ b/.github/workflows/module_async_from_push.yml @@ -0,0 +1,24 @@ +name : async_from + +on : + push : + branches : + - 'alpha' + - 'beta' + - 'master' + + +env : + CARGO_TERM_COLOR : always + +jobs : + + # async_from + + test : + uses : Wandalen/wTools/.github/workflows/standard_rust_push.yml@alpha + with : + manifest_path : 'module/core/async_from/Cargo.toml' + module_name : 'async_from' + commit_message : ${{ github.event.head_commit.message }} + commiter_username: ${{ github.event.head_commit.committer.username }} diff --git a/.github/workflows/module_async_tools_push.yml b/.github/workflows/module_async_tools_push.yml new file mode 100644 index 0000000000..0310131fd5 --- /dev/null +++ b/.github/workflows/module_async_tools_push.yml @@ -0,0 +1,24 @@ +name : async_tools + +on : + push : + branches : + - 'alpha' + - 'beta' + - 'master' + + +env : + CARGO_TERM_COLOR : always + +jobs : + + # async_tools + + test : + uses : Wandalen/wTools/.github/workflows/standard_rust_push.yml@alpha + with : + manifest_path : 'module/core/async_tools/Cargo.toml' + module_name : 'async_tools' + commit_message : ${{ github.event.head_commit.message }} + commiter_username: ${{ github.event.head_commit.committer.username }} diff --git a/.github/workflows/module_graphtools_push.yml b/.github/workflows/module_graphtools_push.yml new file mode 100644 index 0000000000..81fcfc6c0d --- /dev/null +++ b/.github/workflows/module_graphtools_push.yml @@ -0,0 +1,24 @@ +name : graphtools + +on : + push : + branches : + - 'alpha' + - 'beta' + - 'master' + + +env : + CARGO_TERM_COLOR : always + +jobs : + + # graphtools + + test : + uses : Wandalen/wTools/.github/workflows/standard_rust_push.yml@alpha + with : + manifest_path : 'module/blank/graphtools/Cargo.toml' + module_name : 'graphtools' + commit_message : ${{ github.event.head_commit.message }} + commiter_username: ${{ github.event.head_commit.committer.username }} diff --git a/.github/workflows/module_mindx_12_push.yml b/.github/workflows/module_mindx_12_push.yml new file mode 100644 index 0000000000..dc1bf11265 --- /dev/null +++ b/.github/workflows/module_mindx_12_push.yml @@ -0,0 +1,24 @@ +name : mindx12 + +on : + push : + branches : + - 'alpha' + - 'beta' + - 'master' + + +env : + CARGO_TERM_COLOR : always + +jobs : + + # mindx12 + + test : + uses : Wandalen/wTools/.github/workflows/standard_rust_push.yml@alpha + with : + manifest_path : 'module/blank/mindx12/Cargo.toml' + module_name : 'mindx12' + commit_message : ${{ github.event.head_commit.message }} + commiter_username: ${{ github.event.head_commit.committer.username }} diff --git a/.github/workflows/module_mingl_push.yml b/.github/workflows/module_mingl_push.yml new file mode 100644 index 0000000000..c6ce82da26 --- /dev/null +++ b/.github/workflows/module_mingl_push.yml @@ -0,0 +1,24 @@ +name : mingl + +on : + push : + branches : + - 'alpha' + - 'beta' + - 'master' + + +env : + CARGO_TERM_COLOR : always + +jobs : + + # mingl + + test : + uses : Wandalen/wTools/.github/workflows/standard_rust_push.yml@alpha + with : + manifest_path : 'module/blank/mingl/Cargo.toml' + module_name : 'mingl' + commit_message : ${{ github.event.head_commit.message }} + commiter_username: ${{ github.event.head_commit.committer.username }} diff --git a/.github/workflows/module_minmetal_push.yml b/.github/workflows/module_minmetal_push.yml new file mode 100644 index 0000000000..e76b7cf916 --- /dev/null +++ b/.github/workflows/module_minmetal_push.yml @@ -0,0 +1,24 @@ +name : minmetal + +on : + push : + branches : + - 'alpha' + - 'beta' + - 'master' + + +env : + CARGO_TERM_COLOR : always + +jobs : + + # minmetal + + test : + uses : Wandalen/wTools/.github/workflows/standard_rust_push.yml@alpha + with : + manifest_path : 'module/blank/minmetal/Cargo.toml' + module_name : 'minmetal' + commit_message : ${{ github.event.head_commit.message }} + commiter_username: ${{ github.event.head_commit.committer.username }} diff --git a/.github/workflows/module_minopengl_push.yml b/.github/workflows/module_minopengl_push.yml new file mode 100644 index 0000000000..5d412de534 --- /dev/null +++ b/.github/workflows/module_minopengl_push.yml @@ -0,0 +1,24 @@ +name : minopengl + +on : + push : + branches : + - 'alpha' + - 'beta' + - 'master' + + +env : + CARGO_TERM_COLOR : always + +jobs : + + # minopengl + + test : + uses : Wandalen/wTools/.github/workflows/standard_rust_push.yml@alpha + with : + manifest_path : 'module/blank/minopengl/Cargo.toml' + module_name : 'minopengl' + commit_message : ${{ github.event.head_commit.message }} + commiter_username: ${{ github.event.head_commit.committer.username }} diff --git a/.github/workflows/module_minvulkan_push.yml b/.github/workflows/module_minvulkan_push.yml new file mode 100644 index 0000000000..1350cf0693 --- /dev/null +++ b/.github/workflows/module_minvulkan_push.yml @@ -0,0 +1,24 @@ +name : minvulkan + +on : + push : + branches : + - 'alpha' + - 'beta' + - 'master' + + +env : + CARGO_TERM_COLOR : always + +jobs : + + # minvulkan + + test : + uses : Wandalen/wTools/.github/workflows/standard_rust_push.yml@alpha + with : + manifest_path : 'module/blank/minvulkan/Cargo.toml' + module_name : 'minvulkan' + commit_message : ${{ github.event.head_commit.message }} + commiter_username: ${{ github.event.head_commit.committer.username }} diff --git a/.github/workflows/module_minwebgl_push.yml b/.github/workflows/module_minwebgl_push.yml new file mode 100644 index 0000000000..4d63735d10 --- /dev/null +++ b/.github/workflows/module_minwebgl_push.yml @@ -0,0 +1,24 @@ +name : minwebgl + +on : + push : + branches : + - 'alpha' + - 'beta' + - 'master' + + +env : + CARGO_TERM_COLOR : always + +jobs : + + # minwebgl + + test : + uses : Wandalen/wTools/.github/workflows/standard_rust_push.yml@alpha + with : + manifest_path : 'module/blank/minwebgl/Cargo.toml' + module_name : 'minwebgl' + commit_message : ${{ github.event.head_commit.message }} + commiter_username: ${{ github.event.head_commit.committer.username }} diff --git a/.github/workflows/module_minwebgpu_push.yml b/.github/workflows/module_minwebgpu_push.yml new file mode 100644 index 0000000000..4e8992613d --- /dev/null +++ b/.github/workflows/module_minwebgpu_push.yml @@ -0,0 +1,24 @@ +name : minwebgpu + +on : + push : + branches : + - 'alpha' + - 'beta' + - 'master' + + +env : + CARGO_TERM_COLOR : always + +jobs : + + # minwebgpu + + test : + uses : Wandalen/wTools/.github/workflows/standard_rust_push.yml@alpha + with : + manifest_path : 'module/blank/minwebgpu/Cargo.toml' + module_name : 'minwebgpu' + commit_message : ${{ github.event.head_commit.message }} + commiter_username: ${{ github.event.head_commit.committer.username }} diff --git a/.github/workflows/module_minwgpu_push.yml b/.github/workflows/module_minwgpu_push.yml new file mode 100644 index 0000000000..382a15d19b --- /dev/null +++ b/.github/workflows/module_minwgpu_push.yml @@ -0,0 +1,24 @@ +name : minwgpu + +on : + push : + branches : + - 'alpha' + - 'beta' + - 'master' + + +env : + CARGO_TERM_COLOR : always + +jobs : + + # minwgpu + + test : + uses : Wandalen/wTools/.github/workflows/standard_rust_push.yml@alpha + with : + manifest_path : 'module/blank/minwgpu/Cargo.toml' + module_name : 'minwgpu' + commit_message : ${{ github.event.head_commit.message }} + commiter_username: ${{ github.event.head_commit.committer.username }} diff --git a/.github/workflows/module_proper_path_tools_push.yml b/.github/workflows/module_proper_path_tools_push.yml index ebfcf83964..238a9cdb3f 100644 --- a/.github/workflows/module_proper_path_tools_push.yml +++ b/.github/workflows/module_proper_path_tools_push.yml @@ -18,7 +18,7 @@ jobs : test : uses : Wandalen/wTools/.github/workflows/standard_rust_push.yml@alpha with : - manifest_path : 'module/core/proper_path_tools/Cargo.toml' + manifest_path : 'module/blank/proper_path_tools/Cargo.toml' module_name : 'proper_path_tools' commit_message : ${{ github.event.head_commit.message }} commiter_username: ${{ github.event.head_commit.committer.username }} diff --git a/.github/workflows/module_pth_push.yml b/.github/workflows/module_pth_push.yml new file mode 100644 index 0000000000..ddff538916 --- /dev/null +++ b/.github/workflows/module_pth_push.yml @@ -0,0 +1,24 @@ +name : pth + +on : + push : + branches : + - 'alpha' + - 'beta' + - 'master' + + +env : + CARGO_TERM_COLOR : always + +jobs : + + # pth + + test : + uses : Wandalen/wTools/.github/workflows/standard_rust_push.yml@alpha + with : + manifest_path : 'module/core/pth/Cargo.toml' + module_name : 'pth' + commit_message : ${{ github.event.head_commit.message }} + commiter_username: ${{ github.event.head_commit.committer.username }} diff --git a/Cargo.toml b/Cargo.toml index ca0d63d237..a940b1b1ec 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -80,7 +80,7 @@ path = "module/alias/std_x" ## data_type [workspace.dependencies.data_type] -version = "~0.10.0" +version = "~0.12.0" path = "module/core/data_type" default-features = false @@ -98,19 +98,19 @@ default-features = false # path = "module/core/type_constructor_derive_pair_meta" [workspace.dependencies.interval_adapter] -version = "~0.25.0" +version = "~0.27.0" path = "module/core/interval_adapter" default-features = false -features = [ "enabled" ] +# features = [ "enabled" ] [workspace.dependencies.winterval] version = "~0.3.0" path = "module/alias/winterval" default-features = false -features = [ "enabled" ] +# features = [ "enabled" ] [workspace.dependencies.collection_tools] -version = "~0.13.0" +version = "~0.15.0" path = "module/core/collection_tools" default-features = false @@ -118,34 +118,34 @@ default-features = false ## derive [workspace.dependencies.derive_tools] -version = "~0.29.0" +version = "~0.32.0" path = "module/core/derive_tools" default-features = false -features = [ "enabled" ] +# features = [ "enabled" ] [workspace.dependencies.derive_tools_meta] -version = "~0.28.0" +version = "~0.31.0" path = "module/core/derive_tools_meta" default-features = false -features = [ "enabled" ] +# features = [ "enabled" ] [workspace.dependencies.reflect_tools] version = "~0.3.0" path = "module/core/reflect_tools" default-features = false -features = [ "enabled" ] +# features = [ "enabled" ] [workspace.dependencies.reflect_tools_meta] version = "~0.3.0" path = "module/core/reflect_tools_meta" default-features = false -features = [ "enabled" ] +# features = [ "enabled" ] [workspace.dependencies.format_tools] version = "~0.2.0" path = "module/core/format_tools" default-features = false -features = [ "enabled" ] +# features = [ "enabled" ] # xxx : remove features, maybe # [workspace.dependencies.type_constructor] @@ -159,33 +159,33 @@ path = "module/alias/fundamental_data_type" default-features = false [workspace.dependencies.variadic_from] -version = "~0.24.0" +version = "~0.27.0" path = "module/core/variadic_from" default-features = false -features = [ "enabled" ] +# features = [ "enabled" ] [workspace.dependencies.clone_dyn] -version = "~0.25.0" +version = "~0.29.0" path = "module/core/clone_dyn" default-features = false -features = [ "enabled" ] +# features = [ "enabled" ] [workspace.dependencies.clone_dyn_meta] -version = "~0.24.0" +version = "~0.27.0" path = "module/core/clone_dyn_meta" -features = [ "enabled" ] +# features = [ "enabled" ] [workspace.dependencies.clone_dyn_types] -version = "~0.24.0" +version = "~0.26.0" path = "module/core/clone_dyn_types" default-features = false -features = [ "enabled" ] +# features = [ "enabled" ] ## mem [workspace.dependencies.mem_tools] -version = "~0.7.0" +version = "~0.8.0" path = "module/core/mem_tools" default-features = false @@ -193,7 +193,7 @@ default-features = false ## diagnostics [workspace.dependencies.diagnostics_tools] -version = "~0.9.0" +version = "~0.10.0" path = "module/core/diagnostics_tools" default-features = false @@ -201,7 +201,7 @@ default-features = false ## iter [workspace.dependencies.iter_tools] -version = "~0.22.0" +version = "~0.24.0" path = "module/core/iter_tools" default-features = false @@ -209,17 +209,17 @@ default-features = false ## meta [workspace.dependencies.meta_tools] -version = "~0.11.0" +version = "~0.12.0" path = "module/core/meta_tools" default-features = false [workspace.dependencies.for_each] -version = "~0.9.0" +version = "~0.10.0" path = "module/core/for_each" default-features = false [workspace.dependencies.former] -version = "~2.9.0" +version = "~2.11.0" path = "module/core/former" default-features = false @@ -229,31 +229,31 @@ default-features = false # default-features = false [workspace.dependencies.former_meta] -version = "~2.9.0" +version = "~2.11.0" path = "module/core/former_meta" default-features = false [workspace.dependencies.former_types] -version = "~2.9.0" +version = "~2.12.0" path = "module/core/former_types" default-features = false [workspace.dependencies.impls_index] -version = "~0.8.0" +version = "~0.9.0" path = "module/core/impls_index" default-features = false [workspace.dependencies.impls_index_meta] -version = "~0.8.0" +version = "~0.9.0" path = "module/core/impls_index_meta" [workspace.dependencies.mod_interface] -version = "~0.27.0" +version = "~0.30.0" path = "module/core/mod_interface" default-features = false [workspace.dependencies.mod_interface_meta] -version = "~0.26.0" +version = "~0.29.0" path = "module/core/mod_interface_meta" default-features = false @@ -279,7 +279,7 @@ default-features = false ## macro tools [workspace.dependencies.macro_tools] -version = "~0.41.0" +version = "~0.44.0" path = "module/core/macro_tools" default-features = false @@ -305,12 +305,12 @@ default-features = false ## typing [workspace.dependencies.typing_tools] -version = "~0.9.0" +version = "~0.10.0" path = "module/core/typing_tools" default-features = false [workspace.dependencies.implements] -version = "~0.9.0" +version = "~0.10.0" path = "module/core/implements" default-features = false @@ -320,12 +320,12 @@ path = "module/alias/instance_of" default-features = false [workspace.dependencies.inspect_type] -version = "~0.11.0" +version = "~0.12.0" path = "module/core/inspect_type" default-features = false [workspace.dependencies.is_slice] -version = "~0.10.0" +version = "~0.11.0" path = "module/core/is_slice" default-features = false @@ -333,7 +333,7 @@ default-features = false ## error [workspace.dependencies.error_tools] -version = "~0.17.0" +version = "~0.19.0" path = "module/core/error_tools" default-features = false @@ -345,7 +345,7 @@ path = "module/alias/werror" ## string tools [workspace.dependencies.strs_tools] -version = "~0.17.0" +version = "~0.18.0" path = "module/core/strs_tools" default-features = false @@ -366,16 +366,21 @@ version = "~0.1.0" path = "module/alias/file_tools" default-features = false -[workspace.dependencies.proper_path_tools] -version = "~0.11.0" -path = "module/core/proper_path_tools" +[workspace.dependencies.pth] +version = "~0.21.0" +path = "module/core/pth" default-features = false +# [workspace.dependencies.proper_path_tools] +# version = "~0.15.0" +# path = "module/core/proper_path_tools" +# default-features = false + ## process tools [workspace.dependencies.process_tools] -version = "~0.10.0" +version = "~0.12.0" path = "module/core/process_tools" default-features = false @@ -392,14 +397,29 @@ version = "~0.4.0" path = "module/alias/wtest" [workspace.dependencies.test_tools] -version = "~0.10.0" +version = "~0.11.0" path = "module/core/test_tools" +features = [ "full" ] + +[workspace.dependencies.test_tools_stable] +package = "test_tools" +version = "~0.10.0" +features = [ "full" ] [workspace.dependencies.wtest_basic] version = "~0.4.0" path = "module/alias/wtest_basic" +## async + +[workspace.dependencies.async_from] +version = "~0.2.0" +path = "module/core/async_from" + +[workspace.dependencies.async_tools] +version = "~0.1.0" +path = "module/core/async_tools" ## graphs tools @@ -422,7 +442,7 @@ default-features = false ## ca [workspace.dependencies.wca] -version = "~0.22.0" +version = "~0.23.0" path = "module/move/wca" @@ -436,7 +456,7 @@ path = "module/move/wcensor" ## willbe [workspace.dependencies.willbe] -version = "~0.19.0" +version = "~0.20.0" path = "module/move/willbe" @@ -476,7 +496,7 @@ version = "~0.5.0" path = "module/move/deterministic_rand" [workspace.dependencies.crates_tools] -version = "~0.13.0" +version = "~0.14.0" path = "module/move/crates_tools" @@ -507,3 +527,13 @@ default-features = true version = "~0.3.0" path = "module/test/c" default-features = true + +## External + +[workspace.dependencies.async-trait] +version = "0.1.83" + +[workspace.dependencies.tokio] +version = "1.41.0" +features = [] +default-features = false \ No newline at end of file diff --git a/Readme.md b/Readme.md index 36612d9970..1dd88d3db6 100644 --- a/Readme.md +++ b/Readme.md @@ -27,31 +27,33 @@ Collection of general purpose tools for solving problems. Fundamentally extend t | [clone_dyn](module/core/clone_dyn) | [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_clone_dyn_push.yml?label=&branch=master)](https://github.com/Wandalen/wTools/actions/workflows/module_clone_dyn_push.yml?query=branch%3Amaster) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_clone_dyn_push.yml?label=&branch=alpha)](https://github.com/Wandalen/wTools/actions/workflows/module_clone_dyn_push.yml?query=branch%3Aalpha) | [![docs.rs](https://raster.shields.io/static/v1?label=&message=docs&color=eee)](https://docs.rs/clone_dyn) | [![Open in Gitpod](https://raster.shields.io/static/v1?label=&message=try&color=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=module%2Fcore%2Fclone_dyn%2Fexamples%2Fclone_dyn_trivial.rs,RUN_POSTFIX=--example%20clone_dyn_trivial/https://github.com/Wandalen/wTools) | | [variadic_from](module/core/variadic_from) | [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_variadic_from_push.yml?label=&branch=master)](https://github.com/Wandalen/wTools/actions/workflows/module_variadic_from_push.yml?query=branch%3Amaster) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_variadic_from_push.yml?label=&branch=alpha)](https://github.com/Wandalen/wTools/actions/workflows/module_variadic_from_push.yml?query=branch%3Aalpha) | [![docs.rs](https://raster.shields.io/static/v1?label=&message=docs&color=eee)](https://docs.rs/variadic_from) | [![Open in Gitpod](https://raster.shields.io/static/v1?label=&message=try&color=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=module%2Fcore%2Fvariadic_from%2Fexamples%2Fvariadic_from_trivial.rs,RUN_POSTFIX=--example%20variadic_from_trivial/https://github.com/Wandalen/wTools) | | [derive_tools](module/core/derive_tools) | [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_derive_tools_push.yml?label=&branch=master)](https://github.com/Wandalen/wTools/actions/workflows/module_derive_tools_push.yml?query=branch%3Amaster) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_derive_tools_push.yml?label=&branch=alpha)](https://github.com/Wandalen/wTools/actions/workflows/module_derive_tools_push.yml?query=branch%3Aalpha) | [![docs.rs](https://raster.shields.io/static/v1?label=&message=docs&color=eee)](https://docs.rs/derive_tools) | [![Open in Gitpod](https://raster.shields.io/static/v1?label=&message=try&color=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=module%2Fcore%2Fderive_tools%2Fexamples%2Fderive_tools_trivial.rs,RUN_POSTFIX=--example%20derive_tools_trivial/https://github.com/Wandalen/wTools) | +| [mod_interface_meta](module/core/mod_interface_meta) | [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_mod_interface_meta_push.yml?label=&branch=master)](https://github.com/Wandalen/wTools/actions/workflows/module_mod_interface_meta_push.yml?query=branch%3Amaster) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_mod_interface_meta_push.yml?label=&branch=alpha)](https://github.com/Wandalen/wTools/actions/workflows/module_mod_interface_meta_push.yml?query=branch%3Aalpha) | [![docs.rs](https://raster.shields.io/static/v1?label=&message=docs&color=eee)](https://docs.rs/mod_interface_meta) | | | [former_meta](module/core/former_meta) | [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_former_meta_push.yml?label=&branch=master)](https://github.com/Wandalen/wTools/actions/workflows/module_former_meta_push.yml?query=branch%3Amaster) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_former_meta_push.yml?label=&branch=alpha)](https://github.com/Wandalen/wTools/actions/workflows/module_former_meta_push.yml?query=branch%3Aalpha) | [![docs.rs](https://raster.shields.io/static/v1?label=&message=docs&color=eee)](https://docs.rs/former_meta) | | | [impls_index_meta](module/core/impls_index_meta) | [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_impls_index_meta_push.yml?label=&branch=master)](https://github.com/Wandalen/wTools/actions/workflows/module_impls_index_meta_push.yml?query=branch%3Amaster) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_impls_index_meta_push.yml?label=&branch=alpha)](https://github.com/Wandalen/wTools/actions/workflows/module_impls_index_meta_push.yml?query=branch%3Aalpha) | [![docs.rs](https://raster.shields.io/static/v1?label=&message=docs&color=eee)](https://docs.rs/impls_index_meta) | | -| [mod_interface_meta](module/core/mod_interface_meta) | [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_mod_interface_meta_push.yml?label=&branch=master)](https://github.com/Wandalen/wTools/actions/workflows/module_mod_interface_meta_push.yml?query=branch%3Amaster) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_mod_interface_meta_push.yml?label=&branch=alpha)](https://github.com/Wandalen/wTools/actions/workflows/module_mod_interface_meta_push.yml?query=branch%3Aalpha) | [![docs.rs](https://raster.shields.io/static/v1?label=&message=docs&color=eee)](https://docs.rs/mod_interface_meta) | | +| [mod_interface](module/core/mod_interface) | [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_mod_interface_push.yml?label=&branch=master)](https://github.com/Wandalen/wTools/actions/workflows/module_mod_interface_push.yml?query=branch%3Amaster) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_mod_interface_push.yml?label=&branch=alpha)](https://github.com/Wandalen/wTools/actions/workflows/module_mod_interface_push.yml?query=branch%3Aalpha) | [![docs.rs](https://raster.shields.io/static/v1?label=&message=docs&color=eee)](https://docs.rs/mod_interface) | | +| [error_tools](module/core/error_tools) | [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_error_tools_push.yml?label=&branch=master)](https://github.com/Wandalen/wTools/actions/workflows/module_error_tools_push.yml?query=branch%3Amaster) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_error_tools_push.yml?label=&branch=alpha)](https://github.com/Wandalen/wTools/actions/workflows/module_error_tools_push.yml?query=branch%3Aalpha) | [![docs.rs](https://raster.shields.io/static/v1?label=&message=docs&color=eee)](https://docs.rs/error_tools) | [![Open in Gitpod](https://raster.shields.io/static/v1?label=&message=try&color=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=module%2Fcore%2Ferror_tools%2Fexamples%2Ferror_tools_trivial.rs,RUN_POSTFIX=--example%20error_tools_trivial/https://github.com/Wandalen/wTools) | | [for_each](module/core/for_each) | [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_for_each_push.yml?label=&branch=master)](https://github.com/Wandalen/wTools/actions/workflows/module_for_each_push.yml?query=branch%3Amaster) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_for_each_push.yml?label=&branch=alpha)](https://github.com/Wandalen/wTools/actions/workflows/module_for_each_push.yml?query=branch%3Aalpha) | [![docs.rs](https://raster.shields.io/static/v1?label=&message=docs&color=eee)](https://docs.rs/for_each) | [![Open in Gitpod](https://raster.shields.io/static/v1?label=&message=try&color=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=module%2Fcore%2Ffor_each%2Fexamples%2Ffor_each_trivial.rs,RUN_POSTFIX=--example%20for_each_trivial/https://github.com/Wandalen/wTools) | | [former](module/core/former) | [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_former_push.yml?label=&branch=master)](https://github.com/Wandalen/wTools/actions/workflows/module_former_push.yml?query=branch%3Amaster) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_former_push.yml?label=&branch=alpha)](https://github.com/Wandalen/wTools/actions/workflows/module_former_push.yml?query=branch%3Aalpha) | [![docs.rs](https://raster.shields.io/static/v1?label=&message=docs&color=eee)](https://docs.rs/former) | [![Open in Gitpod](https://raster.shields.io/static/v1?label=&message=try&color=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=module%2Fcore%2Fformer%2Fexamples%2Fformer_trivial.rs,RUN_POSTFIX=--example%20former_trivial/https://github.com/Wandalen/wTools) | | [implements](module/core/implements) | [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_implements_push.yml?label=&branch=master)](https://github.com/Wandalen/wTools/actions/workflows/module_implements_push.yml?query=branch%3Amaster) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_implements_push.yml?label=&branch=alpha)](https://github.com/Wandalen/wTools/actions/workflows/module_implements_push.yml?query=branch%3Aalpha) | [![docs.rs](https://raster.shields.io/static/v1?label=&message=docs&color=eee)](https://docs.rs/implements) | [![Open in Gitpod](https://raster.shields.io/static/v1?label=&message=try&color=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=module%2Fcore%2Fimplements%2Fexamples%2Fimplements_trivial.rs,RUN_POSTFIX=--example%20implements_trivial/https://github.com/Wandalen/wTools) | | [impls_index](module/core/impls_index) | [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_impls_index_push.yml?label=&branch=master)](https://github.com/Wandalen/wTools/actions/workflows/module_impls_index_push.yml?query=branch%3Amaster) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_impls_index_push.yml?label=&branch=alpha)](https://github.com/Wandalen/wTools/actions/workflows/module_impls_index_push.yml?query=branch%3Aalpha) | [![docs.rs](https://raster.shields.io/static/v1?label=&message=docs&color=eee)](https://docs.rs/impls_index) | [![Open in Gitpod](https://raster.shields.io/static/v1?label=&message=try&color=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=module%2Fcore%2Fimpls_index%2Fexamples%2Fimpls_index_trivial.rs,RUN_POSTFIX=--example%20impls_index_trivial/https://github.com/Wandalen/wTools) | | [inspect_type](module/core/inspect_type) | [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_inspect_type_push.yml?label=&branch=master)](https://github.com/Wandalen/wTools/actions/workflows/module_inspect_type_push.yml?query=branch%3Amaster) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_inspect_type_push.yml?label=&branch=alpha)](https://github.com/Wandalen/wTools/actions/workflows/module_inspect_type_push.yml?query=branch%3Aalpha) | [![docs.rs](https://raster.shields.io/static/v1?label=&message=docs&color=eee)](https://docs.rs/inspect_type) | [![Open in Gitpod](https://raster.shields.io/static/v1?label=&message=try&color=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=module%2Fcore%2Finspect_type%2Fexamples%2Finspect_type_trivial.rs,RUN_POSTFIX=--example%20inspect_type_trivial/https://github.com/Wandalen/wTools) | | [is_slice](module/core/is_slice) | [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_is_slice_push.yml?label=&branch=master)](https://github.com/Wandalen/wTools/actions/workflows/module_is_slice_push.yml?query=branch%3Amaster) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_is_slice_push.yml?label=&branch=alpha)](https://github.com/Wandalen/wTools/actions/workflows/module_is_slice_push.yml?query=branch%3Aalpha) | [![docs.rs](https://raster.shields.io/static/v1?label=&message=docs&color=eee)](https://docs.rs/is_slice) | [![Open in Gitpod](https://raster.shields.io/static/v1?label=&message=try&color=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=module%2Fcore%2Fis_slice%2Fexamples%2Fis_slice_trivial.rs,RUN_POSTFIX=--example%20is_slice_trivial/https://github.com/Wandalen/wTools) | -| [mod_interface](module/core/mod_interface) | [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_mod_interface_push.yml?label=&branch=master)](https://github.com/Wandalen/wTools/actions/workflows/module_mod_interface_push.yml?query=branch%3Amaster) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_mod_interface_push.yml?label=&branch=alpha)](https://github.com/Wandalen/wTools/actions/workflows/module_mod_interface_push.yml?query=branch%3Aalpha) | [![docs.rs](https://raster.shields.io/static/v1?label=&message=docs&color=eee)](https://docs.rs/mod_interface) | | +| [pth](module/core/pth) | [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_pth_push.yml?label=&branch=master)](https://github.com/Wandalen/wTools/actions/workflows/module_pth_push.yml?query=branch%3Amaster) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_pth_push.yml?label=&branch=alpha)](https://github.com/Wandalen/wTools/actions/workflows/module_pth_push.yml?query=branch%3Aalpha) | [![docs.rs](https://raster.shields.io/static/v1?label=&message=docs&color=eee)](https://docs.rs/pth) | | | [reflect_tools_meta](module/core/reflect_tools_meta) | [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_reflect_tools_meta_push.yml?label=&branch=master)](https://github.com/Wandalen/wTools/actions/workflows/module_reflect_tools_meta_push.yml?query=branch%3Amaster) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_reflect_tools_meta_push.yml?label=&branch=alpha)](https://github.com/Wandalen/wTools/actions/workflows/module_reflect_tools_meta_push.yml?query=branch%3Aalpha) | [![docs.rs](https://raster.shields.io/static/v1?label=&message=docs&color=eee)](https://docs.rs/reflect_tools_meta) | | | [data_type](module/core/data_type) | [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_data_type_push.yml?label=&branch=master)](https://github.com/Wandalen/wTools/actions/workflows/module_data_type_push.yml?query=branch%3Amaster) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_data_type_push.yml?label=&branch=alpha)](https://github.com/Wandalen/wTools/actions/workflows/module_data_type_push.yml?query=branch%3Aalpha) | [![docs.rs](https://raster.shields.io/static/v1?label=&message=docs&color=eee)](https://docs.rs/data_type) | [![Open in Gitpod](https://raster.shields.io/static/v1?label=&message=try&color=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=module%2Fcore%2Fdata_type%2Fexamples%2Fdata_type_trivial.rs,RUN_POSTFIX=--example%20data_type_trivial/https://github.com/Wandalen/wTools) | | [diagnostics_tools](module/core/diagnostics_tools) | [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_diagnostics_tools_push.yml?label=&branch=master)](https://github.com/Wandalen/wTools/actions/workflows/module_diagnostics_tools_push.yml?query=branch%3Amaster) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_diagnostics_tools_push.yml?label=&branch=alpha)](https://github.com/Wandalen/wTools/actions/workflows/module_diagnostics_tools_push.yml?query=branch%3Aalpha) | [![docs.rs](https://raster.shields.io/static/v1?label=&message=docs&color=eee)](https://docs.rs/diagnostics_tools) | [![Open in Gitpod](https://raster.shields.io/static/v1?label=&message=try&color=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=module%2Fcore%2Fdiagnostics_tools%2Fexamples%2Fdiagnostics_tools_trivial.rs,RUN_POSTFIX=--example%20diagnostics_tools_trivial/https://github.com/Wandalen/wTools) | -| [error_tools](module/core/error_tools) | [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_error_tools_push.yml?label=&branch=master)](https://github.com/Wandalen/wTools/actions/workflows/module_error_tools_push.yml?query=branch%3Amaster) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_error_tools_push.yml?label=&branch=alpha)](https://github.com/Wandalen/wTools/actions/workflows/module_error_tools_push.yml?query=branch%3Aalpha) | [![docs.rs](https://raster.shields.io/static/v1?label=&message=docs&color=eee)](https://docs.rs/error_tools) | [![Open in Gitpod](https://raster.shields.io/static/v1?label=&message=try&color=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=module%2Fcore%2Ferror_tools%2Fexamples%2Ferror_tools_trivial.rs,RUN_POSTFIX=--example%20error_tools_trivial/https://github.com/Wandalen/wTools) | | [mem_tools](module/core/mem_tools) | [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_mem_tools_push.yml?label=&branch=master)](https://github.com/Wandalen/wTools/actions/workflows/module_mem_tools_push.yml?query=branch%3Amaster) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_mem_tools_push.yml?label=&branch=alpha)](https://github.com/Wandalen/wTools/actions/workflows/module_mem_tools_push.yml?query=branch%3Aalpha) | [![docs.rs](https://raster.shields.io/static/v1?label=&message=docs&color=eee)](https://docs.rs/mem_tools) | [![Open in Gitpod](https://raster.shields.io/static/v1?label=&message=try&color=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=module%2Fcore%2Fmem_tools%2Fexamples%2Fmem_tools_trivial.rs,RUN_POSTFIX=--example%20mem_tools_trivial/https://github.com/Wandalen/wTools) | | [meta_tools](module/core/meta_tools) | [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_meta_tools_push.yml?label=&branch=master)](https://github.com/Wandalen/wTools/actions/workflows/module_meta_tools_push.yml?query=branch%3Amaster) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_meta_tools_push.yml?label=&branch=alpha)](https://github.com/Wandalen/wTools/actions/workflows/module_meta_tools_push.yml?query=branch%3Aalpha) | [![docs.rs](https://raster.shields.io/static/v1?label=&message=docs&color=eee)](https://docs.rs/meta_tools) | [![Open in Gitpod](https://raster.shields.io/static/v1?label=&message=try&color=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=module%2Fcore%2Fmeta_tools%2Fexamples%2Fmeta_tools_trivial.rs,RUN_POSTFIX=--example%20meta_tools_trivial/https://github.com/Wandalen/wTools) | -| [proper_path_tools](module/core/proper_path_tools) | [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_proper_path_tools_push.yml?label=&branch=master)](https://github.com/Wandalen/wTools/actions/workflows/module_proper_path_tools_push.yml?query=branch%3Amaster) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_proper_path_tools_push.yml?label=&branch=alpha)](https://github.com/Wandalen/wTools/actions/workflows/module_proper_path_tools_push.yml?query=branch%3Aalpha) | [![docs.rs](https://raster.shields.io/static/v1?label=&message=docs&color=eee)](https://docs.rs/proper_path_tools) | | +| [process_tools](module/core/process_tools) | [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_process_tools_push.yml?label=&branch=master)](https://github.com/Wandalen/wTools/actions/workflows/module_process_tools_push.yml?query=branch%3Amaster) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_process_tools_push.yml?label=&branch=alpha)](https://github.com/Wandalen/wTools/actions/workflows/module_process_tools_push.yml?query=branch%3Aalpha) | [![docs.rs](https://raster.shields.io/static/v1?label=&message=docs&color=eee)](https://docs.rs/process_tools) | | | [reflect_tools](module/core/reflect_tools) | [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_reflect_tools_push.yml?label=&branch=master)](https://github.com/Wandalen/wTools/actions/workflows/module_reflect_tools_push.yml?query=branch%3Amaster) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_reflect_tools_push.yml?label=&branch=alpha)](https://github.com/Wandalen/wTools/actions/workflows/module_reflect_tools_push.yml?query=branch%3Aalpha) | [![docs.rs](https://raster.shields.io/static/v1?label=&message=docs&color=eee)](https://docs.rs/reflect_tools) | [![Open in Gitpod](https://raster.shields.io/static/v1?label=&message=try&color=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=module%2Fcore%2Freflect_tools%2Fexamples%2Freflect_tools_trivial.rs,RUN_POSTFIX=--example%20reflect_tools_trivial/https://github.com/Wandalen/wTools) | | [strs_tools](module/core/strs_tools) | [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_strs_tools_push.yml?label=&branch=master)](https://github.com/Wandalen/wTools/actions/workflows/module_strs_tools_push.yml?query=branch%3Amaster) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_strs_tools_push.yml?label=&branch=alpha)](https://github.com/Wandalen/wTools/actions/workflows/module_strs_tools_push.yml?query=branch%3Aalpha) | [![docs.rs](https://raster.shields.io/static/v1?label=&message=docs&color=eee)](https://docs.rs/strs_tools) | [![Open in Gitpod](https://raster.shields.io/static/v1?label=&message=try&color=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=module%2Fcore%2Fstrs_tools%2Fexamples%2Fstrs_tools_trivial.rs,RUN_POSTFIX=--example%20strs_tools_trivial/https://github.com/Wandalen/wTools) | | [time_tools](module/core/time_tools) | [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_time_tools_push.yml?label=&branch=master)](https://github.com/Wandalen/wTools/actions/workflows/module_time_tools_push.yml?query=branch%3Amaster) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_time_tools_push.yml?label=&branch=alpha)](https://github.com/Wandalen/wTools/actions/workflows/module_time_tools_push.yml?query=branch%3Aalpha) | [![docs.rs](https://raster.shields.io/static/v1?label=&message=docs&color=eee)](https://docs.rs/time_tools) | [![Open in Gitpod](https://raster.shields.io/static/v1?label=&message=try&color=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=module%2Fcore%2Ftime_tools%2Fexamples%2Ftime_tools_trivial.rs,RUN_POSTFIX=--example%20time_tools_trivial/https://github.com/Wandalen/wTools) | | [typing_tools](module/core/typing_tools) | [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_typing_tools_push.yml?label=&branch=master)](https://github.com/Wandalen/wTools/actions/workflows/module_typing_tools_push.yml?query=branch%3Amaster) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_typing_tools_push.yml?label=&branch=alpha)](https://github.com/Wandalen/wTools/actions/workflows/module_typing_tools_push.yml?query=branch%3Aalpha) | [![docs.rs](https://raster.shields.io/static/v1?label=&message=docs&color=eee)](https://docs.rs/typing_tools) | [![Open in Gitpod](https://raster.shields.io/static/v1?label=&message=try&color=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=module%2Fcore%2Ftyping_tools%2Fexamples%2Ftyping_tools_trivial.rs,RUN_POSTFIX=--example%20typing_tools_trivial/https://github.com/Wandalen/wTools) | +| [async_from](module/core/async_from) | [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_async_from_push.yml?label=&branch=master)](https://github.com/Wandalen/wTools/actions/workflows/module_async_from_push.yml?query=branch%3Amaster) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_async_from_push.yml?label=&branch=alpha)](https://github.com/Wandalen/wTools/actions/workflows/module_async_from_push.yml?query=branch%3Aalpha) | [![docs.rs](https://raster.shields.io/static/v1?label=&message=docs&color=eee)](https://docs.rs/async_from) | | +| [async_tools](module/core/async_tools) | [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_async_tools_push.yml?label=&branch=master)](https://github.com/Wandalen/wTools/actions/workflows/module_async_tools_push.yml?query=branch%3Amaster) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_async_tools_push.yml?label=&branch=alpha)](https://github.com/Wandalen/wTools/actions/workflows/module_async_tools_push.yml?query=branch%3Aalpha) | [![docs.rs](https://raster.shields.io/static/v1?label=&message=docs&color=eee)](https://docs.rs/async_tools) | | | [format_tools](module/core/format_tools) | [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_format_tools_push.yml?label=&branch=master)](https://github.com/Wandalen/wTools/actions/workflows/module_format_tools_push.yml?query=branch%3Amaster) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_format_tools_push.yml?label=&branch=alpha)](https://github.com/Wandalen/wTools/actions/workflows/module_format_tools_push.yml?query=branch%3Aalpha) | [![docs.rs](https://raster.shields.io/static/v1?label=&message=docs&color=eee)](https://docs.rs/format_tools) | [![Open in Gitpod](https://raster.shields.io/static/v1?label=&message=try&color=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=module%2Fcore%2Fformat_tools%2Fexamples%2Fformat_tools_trivial.rs,RUN_POSTFIX=--example%20format_tools_trivial/https://github.com/Wandalen/wTools) | | [fs_tools](module/core/fs_tools) | [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_fs_tools_push.yml?label=&branch=master)](https://github.com/Wandalen/wTools/actions/workflows/module_fs_tools_push.yml?query=branch%3Amaster) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_fs_tools_push.yml?label=&branch=alpha)](https://github.com/Wandalen/wTools/actions/workflows/module_fs_tools_push.yml?query=branch%3Aalpha) | [![docs.rs](https://raster.shields.io/static/v1?label=&message=docs&color=eee)](https://docs.rs/fs_tools) | | | [include_md](module/core/include_md) | [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_include_md_push.yml?label=&branch=master)](https://github.com/Wandalen/wTools/actions/workflows/module_include_md_push.yml?query=branch%3Amaster) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_include_md_push.yml?label=&branch=alpha)](https://github.com/Wandalen/wTools/actions/workflows/module_include_md_push.yml?query=branch%3Aalpha) | [![docs.rs](https://raster.shields.io/static/v1?label=&message=docs&color=eee)](https://docs.rs/include_md) | | -| [process_tools](module/core/process_tools) | [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_process_tools_push.yml?label=&branch=master)](https://github.com/Wandalen/wTools/actions/workflows/module_process_tools_push.yml?query=branch%3Amaster) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_process_tools_push.yml?label=&branch=alpha)](https://github.com/Wandalen/wTools/actions/workflows/module_process_tools_push.yml?query=branch%3Aalpha) | [![docs.rs](https://raster.shields.io/static/v1?label=&message=docs&color=eee)](https://docs.rs/process_tools) | | | [program_tools](module/core/program_tools) | [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_program_tools_push.yml?label=&branch=master)](https://github.com/Wandalen/wTools/actions/workflows/module_program_tools_push.yml?query=branch%3Amaster) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_program_tools_push.yml?label=&branch=alpha)](https://github.com/Wandalen/wTools/actions/workflows/module_program_tools_push.yml?query=branch%3Aalpha) | [![docs.rs](https://raster.shields.io/static/v1?label=&message=docs&color=eee)](https://docs.rs/program_tools) | | | [test_tools](module/core/test_tools) | [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_test_tools_push.yml?label=&branch=master)](https://github.com/Wandalen/wTools/actions/workflows/module_test_tools_push.yml?query=branch%3Amaster) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_test_tools_push.yml?label=&branch=alpha)](https://github.com/Wandalen/wTools/actions/workflows/module_test_tools_push.yml?query=branch%3Aalpha) | [![docs.rs](https://raster.shields.io/static/v1?label=&message=docs&color=eee)](https://docs.rs/test_tools) | [![Open in Gitpod](https://raster.shields.io/static/v1?label=&message=try&color=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=module%2Fcore%2Ftest_tools%2Fexamples%2Ftest_tools_trivial.rs,RUN_POSTFIX=--example%20test_tools_trivial/https://github.com/Wandalen/wTools) | | [wtools](module/core/wtools) | [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_wtools_push.yml?label=&branch=master)](https://github.com/Wandalen/wTools/actions/workflows/module_wtools_push.yml?query=branch%3Amaster) | [![rust-status](https://img.shields.io/github/actions/workflow/status/Wandalen/wTools/module_wtools_push.yml?label=&branch=alpha)](https://github.com/Wandalen/wTools/actions/workflows/module_wtools_push.yml?query=branch%3Aalpha) | [![docs.rs](https://raster.shields.io/static/v1?label=&message=docs&color=eee)](https://docs.rs/wtools) | [![Open in Gitpod](https://raster.shields.io/static/v1?label=&message=try&color=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=module%2Fcore%2Fwtools%2Fexamples%2Fwtools_trivial.rs,RUN_POSTFIX=--example%20wtools_trivial/https://github.com/Wandalen/wTools) | diff --git a/module/alias/wtest_basic/Cargo.toml b/module/alias/wtest_basic/Cargo.toml index 3d5e3d1218..6e6ceb65fd 100644 --- a/module/alias/wtest_basic/Cargo.toml +++ b/module/alias/wtest_basic/Cargo.toml @@ -58,7 +58,7 @@ enabled = [ "test_tools/enabled" ] [dependencies] -test_tools = { workspace = true, features = [ "full" ] } +test_tools = { workspace = true, default-features = true } # ## external # @@ -76,5 +76,5 @@ test_tools = { workspace = true, features = [ "full" ] } # data_type = { workspace = true, features = [ "full" ] } # diagnostics_tools = { workspace = true, features = [ "full" ] } -# [dev-dependencies] -# test_tools = { workspace = true } +[dev-dependencies] +test_tools = { workspace = true } diff --git a/module/blank/proper_path_tools/Cargo.toml b/module/blank/proper_path_tools/Cargo.toml new file mode 100644 index 0000000000..4fe862c57e --- /dev/null +++ b/module/blank/proper_path_tools/Cargo.toml @@ -0,0 +1,34 @@ +[package] +name = "proper_path_tools" +version = "0.1.0" +edition = "2021" +authors = [ + "Kostiantyn Wandalen ", +] +license = "MIT" +readme = "Readme.md" +documentation = "https://docs.rs/proper_path_tools" +repository = "https://github.com/Wandalen/wTools/tree/master/module/core/proper_path_tools" +homepage = "https://github.com/Wandalen/wTools/tree/master/module/core/proper_path_tools" +description = """ +Tools for second brain. +""" +categories = [ "algorithms", "development-tools" ] +keywords = [ "fundamental", "general-purpose" ] + +[lints] +workspace = true + +[package.metadata.docs.rs] +features = [ "full" ] +all-features = false + +[features] +default = [ "enabled" ] +full = [ "enabled" ] +enabled = [] + +[dependencies] + +[dev-dependencies] +test_tools = { workspace = true } diff --git a/module/core/proper_path_tools/License b/module/blank/proper_path_tools/License similarity index 100% rename from module/core/proper_path_tools/License rename to module/blank/proper_path_tools/License diff --git a/module/blank/proper_path_tools/Readme.md b/module/blank/proper_path_tools/Readme.md new file mode 100644 index 0000000000..7fd9f99168 --- /dev/null +++ b/module/blank/proper_path_tools/Readme.md @@ -0,0 +1,33 @@ + + +# Module :: proper_path_tools +[![experimental](https://raster.shields.io/static/v1?label=stability&message=experimental&color=orange&logoColor=eee)](https://github.com/emersion/stability-badges#experimental) [![rust-status](https://github.com/Wandalen/wTools/actions/workflows/Moduleproper_path_toolsPush.yml/badge.svg)](https://github.com/Wandalen/wTools/actions/workflows/Moduleproper_path_toolsPush.yml) [![docs.rs](https://img.shields.io/docsrs/proper_path_tools?color=e3e8f0&logo=docs.rs)](https://docs.rs/proper_path_tools) [![discord](https://img.shields.io/discord/872391416519737405?color=eee&logo=discord&logoColor=eee&label=ask)](https://discord.gg/m3YfbXpUUY) + +Tools for second brain. + + diff --git a/module/blank/proper_path_tools/src/lib.rs b/module/blank/proper_path_tools/src/lib.rs new file mode 100644 index 0000000000..b96a03ed21 --- /dev/null +++ b/module/blank/proper_path_tools/src/lib.rs @@ -0,0 +1,11 @@ + +#![ doc( html_logo_url = "https://raw.githubusercontent.com/Wandalen/wTools/master/asset/img/logo_v3_trans_square.png" ) ] +#![ doc( html_favicon_url = "https://raw.githubusercontent.com/Wandalen/wTools/alpha/asset/img/logo_v3_trans_square_icon_small_v2.ico" ) ] +#![ doc( html_root_url = "https://docs.rs/proper_path_tools/latest/proper_path_tools/" ) ] +#![ doc = include_str!( concat!( env!( "CARGO_MANIFEST_DIR" ), "/", "Readme.md" ) ) ] + +/// Function description. +#[ cfg( feature = "enabled" ) ] +pub fn f1() +{ +} diff --git a/module/blank/proper_path_tools/tests/inc/basic_test.rs b/module/blank/proper_path_tools/tests/inc/basic_test.rs new file mode 100644 index 0000000000..60c9a81cfb --- /dev/null +++ b/module/blank/proper_path_tools/tests/inc/basic_test.rs @@ -0,0 +1,7 @@ +#[ allow( unused_imports ) ] +use super::*; + +#[ test ] +fn basic() +{ +} diff --git a/module/blank/proper_path_tools/tests/inc/mod.rs b/module/blank/proper_path_tools/tests/inc/mod.rs new file mode 100644 index 0000000000..dde9de6f94 --- /dev/null +++ b/module/blank/proper_path_tools/tests/inc/mod.rs @@ -0,0 +1,4 @@ +#[ allow( unused_imports ) ] +use super::*; + +mod basic_test; diff --git a/module/blank/proper_path_tools/tests/smoke_test.rs b/module/blank/proper_path_tools/tests/smoke_test.rs new file mode 100644 index 0000000000..663dd6fb9f --- /dev/null +++ b/module/blank/proper_path_tools/tests/smoke_test.rs @@ -0,0 +1,12 @@ + +#[ test ] +fn local_smoke_test() +{ + ::test_tools::smoke_test_for_local_run(); +} + +#[ test ] +fn published_smoke_test() +{ + ::test_tools::smoke_test_for_published_run(); +} diff --git a/module/core/proper_path_tools/tests/tests.rs b/module/blank/proper_path_tools/tests/tests.rs similarity index 100% rename from module/core/proper_path_tools/tests/tests.rs rename to module/blank/proper_path_tools/tests/tests.rs diff --git a/module/core/async_from/Cargo.toml b/module/core/async_from/Cargo.toml new file mode 100644 index 0000000000..b6be30c5c7 --- /dev/null +++ b/module/core/async_from/Cargo.toml @@ -0,0 +1,38 @@ +[package] +name = "async_from" +version = "0.2.0" +edition = "2021" +authors = [ + "Kostiantyn Wandalen ", +] +license = "MIT" +readme = "Readme.md" +documentation = "https://docs.rs/async_from" +repository = "https://github.com/Wandalen/wTools/tree/master/module/core/async_from" +homepage = "https://github.com/Wandalen/wTools/tree/master/module/core/async_from" +description = """ +Async version of From, Into, TryFrom, TryInto. +""" +categories = [ "algorithms", "development-tools" ] +keywords = [ "fundamental", "general-purpose" ] + +[lints] +workspace = true + +[package.metadata.docs.rs] +features = [ "full" ] +all-features = false + +[features] +default = [ "enabled", "async_from", "async_try_from" ] +full = [ "default" ] +enabled = [] +async_from = [] +async_try_from = [] + +[dependencies] +async-trait = { workspace = true } + +[dev-dependencies] +# test_tools = { workspace = true } +tokio = { workspace = true, features = [ "rt-multi-thread", "time", "macros" ] } diff --git a/module/core/async_from/License b/module/core/async_from/License new file mode 100644 index 0000000000..0804aed8e3 --- /dev/null +++ b/module/core/async_from/License @@ -0,0 +1,22 @@ +Copyright Kostiantyn Mysnyk and Out of the Box Systems (c) 2021-2024 + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. diff --git a/module/core/async_from/Readme.md b/module/core/async_from/Readme.md new file mode 100644 index 0000000000..374fd4595c --- /dev/null +++ b/module/core/async_from/Readme.md @@ -0,0 +1,91 @@ + + +# Module :: async_from +[![experimental](https://raster.shields.io/static/v1?label=stability&message=experimental&color=orange&logoColor=eee)](https://github.com/emersion/stability-badges#experimental) [![rust-status](https://github.com/Wandalen/wTools/actions/workflows/Moduleasync_fromPush.yml/badge.svg)](https://github.com/Wandalen/wTools/actions/workflows/Moduleasync_fromPush.yml) [![docs.rs](https://img.shields.io/docsrs/async_from?color=e3e8f0&logo=docs.rs)](https://docs.rs/async_from) [![discord](https://img.shields.io/discord/872391416519737405?color=eee&logo=discord&logoColor=eee&label=ask)](https://discord.gg/m3YfbXpUUY) + +Async version of From, Into, TryFrom, TryInto. + +The `async_from` crate provides asynchronous versions of the well-known `From`, `Into`, `TryFrom`, and `TryInto` traits. These traits are essential for handling conversions in Rust, and their asynchronous counterparts, allowing for conversions that involve asynchronous operations. + +## Why Asynchronous Conversion Traits? + +In Rust, the `From`, `Into`, `TryFrom`, and `TryInto` traits provide a standardized way to handle type conversions. The `async_from` module extends this functionality to asynchronous contexts with `AsyncFrom`, `AsyncInto`, `AsyncTryFrom`, and `AsyncTryInto` traits, offering several key benefits: + +- **Simplicity**: Allow straightforward conversions without boilerplate, even in asynchronous contexts. +- **Consistency**: Provide a uniform interface for conversions across different types, aiding in writing predictable and maintainable code. +- **Error Handling**: Enable safe and explicit handling of conversion failures, essential for robust error management in commercial applications. +- **Asynchronous Contexts**: Facilitate conversions involving asynchronous operations, such as network requests or database queries, which are common in modern applications. + +The `async_from` provides developers with the tools needed to handle complex conversions in an async context efficiently, which is particularly important for commercial applications requiring reliable and efficient handling of asynchronous operations. + +### `AsyncFrom` and `AsyncInto` + +Trait for asynchronous conversions from a type T. + +These traits are designed for infallible asynchronous conversions. They allow you to convert types asynchronously, returning the result directly. + +```rust +use async_from::{ async_trait, AsyncFrom, AsyncInto }; + +struct MyNumber( u32 ); + +#[ async_trait ] +impl AsyncFrom< String > for MyNumber +{ + async fn async_from( value : String ) -> Self + { + let num = value.parse::< u32 >().unwrap_or( 0 ); + MyNumber( num ) + } +} + +#[ tokio::main ] +async fn main() +{ + let num = MyNumber::async_from( "42".to_string() ).await; + println!( "Converted: {}", num.0 ); + let num : MyNumber = "42".to_string().async_into().await; + println!( "Converted: {}", num.0 ); +} +``` + +### `AsyncTryFrom` and `AsyncTryInto` + +Trait for asynchronous fallible conversions from a type T. + +These traits are for fallible asynchronous conversions, where the conversion might fail. They return a `Result` wrapped in a `Future`, allowing you to handle errors gracefully. + +```rust +use async_from::{ async_trait, AsyncTryFrom, AsyncTryInto }; +use std::num::ParseIntError; + +struct MyNumber( u32 ); + +#[ async_trait ] +impl AsyncTryFrom< String > for MyNumber +{ + type Error = ParseIntError; + + async fn async_try_from( value : String ) -> Result< Self, Self::Error > + { + let num = value.parse::< u32 >()?; + Ok( MyNumber( num ) ) + } +} + +#[ tokio::main ] +async fn main() +{ + match MyNumber::async_try_from( "42".to_string() ).await + { + Ok( my_num ) => println!( "Converted successfully: {}", my_num.0 ), + Err( e ) => println!( "Conversion failed: {:?}", e ), + } + let result : Result< MyNumber, _ > = "42".to_string().async_try_into().await; + match result + { + Ok( my_num ) => println!( "Converted successfully using AsyncTryInto: {}", my_num.0 ), + Err( e ) => println!( "Conversion failed using AsyncTryInto: {:?}", e ), + } +} +``` diff --git a/module/core/async_from/src/lib.rs b/module/core/async_from/src/lib.rs new file mode 100644 index 0000000000..b2419ae521 --- /dev/null +++ b/module/core/async_from/src/lib.rs @@ -0,0 +1,316 @@ + +#![ doc( html_logo_url = "https://raw.githubusercontent.com/Wandalen/wTools/master/asset/img/logo_v3_trans_square.png" ) ] +#![ doc( html_favicon_url = "https://raw.githubusercontent.com/Wandalen/wTools/alpha/asset/img/logo_v3_trans_square_icon_small_v2.ico" ) ] +#![ doc( html_root_url = "https://docs.rs/async_from/latest/async_from/" ) ] +#![ doc = include_str!( concat!( env!( "CARGO_MANIFEST_DIR" ), "/", "Readme.md" ) ) ] + +/// Namespace with dependencies. +#[ cfg( feature = "enabled" ) ] +pub mod dependency +{ + pub use ::async_trait; +} + +/// Internal namespace. +#[ cfg( feature = "enabled" ) ] +mod private +{ + + pub use async_trait::async_trait; + use std::fmt::Debug; + + /// Trait for asynchronous conversions from a type `T`. + /// + /// This trait allows for conversions that occur asynchronously, returning a `Future`. + /// + /// # Example + /// + /// ```rust + /// use async_from::{ async_trait, AsyncFrom }; + /// + /// struct MyNumber( u32 ); + /// + /// #[ async_trait ] + /// impl AsyncFrom< String > for MyNumber + /// { + /// async fn async_from( value : String ) -> Self + /// { + /// let num = value.parse::< u32 >().unwrap_or( 0 ); + /// MyNumber( num ) + /// } + /// } + /// + /// #[ tokio::main ] + /// async fn main() + /// { + /// let num = MyNumber::async_from( "42".to_string() ).await; + /// println!( "Converted: {}", num.0 ); + /// } + /// ``` + #[ cfg( feature = "async_from" ) ] + #[ async_trait ] + pub trait AsyncFrom< T > : Sized + { + /// Asynchronously converts a value of type `T` into `Self`. + /// + /// # Arguments + /// + /// * `value` - The value to be converted. + /// + /// # Returns + /// + /// * `Self` - The converted value. + async fn async_from( value : T ) -> Self; + } + + /// Trait for asynchronous conversions into a type `T`. + /// + /// This trait provides a method to convert `Self` into `T` asynchronously. + /// + /// # Example + /// + /// ```rust + /// use async_from::{ async_trait, AsyncFrom, AsyncInto }; + /// + /// struct MyNumber( u32 ); + /// + /// #[ async_trait ] + /// impl AsyncFrom< String > for MyNumber + /// { + /// async fn async_from( value : String ) -> Self + /// { + /// let num = value.parse::< u32 >().unwrap_or( 0 ); + /// MyNumber( num ) + /// } + /// } + /// + /// #[ tokio::main ] + /// async fn main() + /// { + /// let num : MyNumber = "42".to_string().async_into().await; + /// println!( "Converted: {}", num.0 ); + /// } + /// ``` + #[ async_trait ] + #[ cfg( feature = "async_from" ) ] + pub trait AsyncInto< T > : Sized + { + /// Asynchronously converts `Self` into a value of type `T`. + /// + /// # Returns + /// + /// * `T` - The converted value. + async fn async_into( self ) -> T; + } + + /// Blanket implementation of `AsyncInto` for any type that implements `AsyncFrom`. + /// + /// This implementation allows any type `T` that implements `AsyncFrom` to also implement `AsyncInto`. + #[ async_trait ] + #[ cfg( feature = "async_from" ) ] + impl< T, U > AsyncInto< U > for T + where + U : AsyncFrom< T > + Send, + T : Send, + { + /// Asynchronously converts `Self` into a value of type `U` using `AsyncFrom`. + /// + /// # Returns + /// + /// * `U` - The converted value. + async fn async_into( self ) -> U + { + U::async_from( self ).await + } + } + + /// Trait for asynchronous fallible conversions from a type `T`. + /// + /// This trait allows for conversions that may fail, returning a `Result` wrapped in a `Future`. + /// + /// # Example + /// + /// ```rust + /// use async_from::{ async_trait, AsyncTryFrom }; + /// use std::num::ParseIntError; + /// + /// struct MyNumber( u32 ); + /// + /// #[ async_trait ] + /// impl AsyncTryFrom< String > for MyNumber + /// { + /// type Error = ParseIntError; + /// + /// async fn async_try_from( value : String ) -> Result< Self, Self::Error > + /// { + /// let num = value.parse::< u32 >()?; + /// Ok( MyNumber( num ) ) + /// } + /// } + /// + /// #[ tokio::main ] + /// async fn main() + /// { + /// match MyNumber::async_try_from( "42".to_string() ).await + /// { + /// Ok( my_num ) => println!( "Converted successfully: {}", my_num.0 ), + /// Err( e ) => println!( "Conversion failed: {:?}", e ), + /// } + /// } + /// ``` + #[ async_trait ] + #[ cfg( feature = "async_try_from" ) ] + pub trait AsyncTryFrom< T > : Sized + { + /// The error type returned if the conversion fails. + type Error : Debug; + + /// Asynchronously attempts to convert a value of type `T` into `Self`. + /// + /// # Arguments + /// + /// * `value` - The value to be converted. + /// + /// # Returns + /// + /// * `Result` - On success, returns the converted value. On failure, returns an error. + async fn async_try_from( value : T ) -> Result< Self, Self::Error >; + } + + /// Trait for asynchronous fallible conversions into a type `T`. + /// + /// This trait provides a method to convert `Self` into `T`, potentially returning an error. + /// + /// # Example + /// + /// ```rust + /// use async_from::{ async_trait, AsyncTryFrom, AsyncTryInto }; + /// use std::num::ParseIntError; + /// + /// struct MyNumber( u32 ); + /// + /// #[ async_trait ] + /// impl AsyncTryFrom< String > for MyNumber + /// { + /// type Error = ParseIntError; + /// + /// async fn async_try_from( value : String ) -> Result< Self, Self::Error > + /// { + /// let num = value.parse::< u32 >()?; + /// Ok( MyNumber( num ) ) + /// } + /// } + /// + /// #[ tokio::main ] + /// async fn main() + /// { + /// let result : Result< MyNumber, _ > = "42".to_string().async_try_into().await; + /// match result + /// { + /// Ok( my_num ) => println!( "Converted successfully using AsyncTryInto: {}", my_num.0 ), + /// Err( e ) => println!( "Conversion failed using AsyncTryInto: {:?}", e ), + /// } + /// } + /// ``` + #[ async_trait ] + #[ cfg( feature = "async_try_from" ) ] + pub trait AsyncTryInto< T > : Sized + { + /// The error type returned if the conversion fails. + type Error : Debug; + + /// Asynchronously attempts to convert `Self` into a value of type `T`. + /// + /// # Returns + /// + /// * `Result` - On success, returns the converted value. On failure, returns an error. + async fn async_try_into( self ) -> Result< T, Self::Error >; + } + + /// Blanket implementation of `AsyncTryInto` for any type that implements `AsyncTryFrom`. + /// + /// This implementation allows any type `T` that implements `AsyncTryFrom` to also implement `AsyncTryInto`. + #[ async_trait ] + #[ cfg( feature = "async_try_from" ) ] + impl< T, U > AsyncTryInto< U > for T + where + U : AsyncTryFrom< T > + Send, + T : Send, + { + type Error = U::Error; + + /// Asynchronously converts `Self` into a value of type `U` using `AsyncTryFrom`. + /// + /// # Returns + /// + /// * `Result` - On success, returns the converted value. On failure, returns an error. + async fn async_try_into( self ) -> Result< U, Self::Error > + { + U::async_try_from( self ).await + } + } + +} + +#[ cfg( feature = "enabled" ) ] +#[ doc( inline ) ] +#[ allow( unused_imports ) ] +pub use own::*; + +/// Own namespace of the module. +#[ cfg( feature = "enabled" ) ] +#[ allow( unused_imports ) ] +pub mod own +{ + use super::*; + #[ doc( inline ) ] + pub use orphan::*; +} + +/// Orphan namespace of the module. +#[ cfg( feature = "enabled" ) ] +#[ allow( unused_imports ) ] +pub mod orphan +{ + use super::*; + #[ doc( inline ) ] + pub use exposed::*; + +} + +/// Exposed namespace of the module. +#[ cfg( feature = "enabled" ) ] +#[ allow( unused_imports ) ] +pub mod exposed +{ + use super::*; + + #[ doc( inline ) ] + pub use prelude::*; + + #[ doc( inline ) ] + pub use ::async_trait::async_trait; + + #[ cfg( feature = "async_from" ) ] + pub use private:: + { + AsyncFrom, + AsyncInto, + }; + + #[ cfg( feature = "async_try_from" ) ] + pub use private:: + { + AsyncTryFrom, + AsyncTryInto, + }; + +} + +/// Prelude to use essentials: `use my_module::prelude::*`. +#[ cfg( feature = "enabled" ) ] +#[ allow( unused_imports ) ] +pub mod prelude +{ + use super::*; +} diff --git a/module/core/async_from/tests/inc/basic_test.rs b/module/core/async_from/tests/inc/basic_test.rs new file mode 100644 index 0000000000..dce63a4c1e --- /dev/null +++ b/module/core/async_from/tests/inc/basic_test.rs @@ -0,0 +1,84 @@ +use super::*; + +#[ tokio::test ] +async fn async_try_from_test() +{ + + // Example implementation of AsyncTryFrom for a custom type + struct MyNumber( u32 ); + + // xxx : qqq : broken + // #[ the_module::async_trait ] + // impl< 'a > the_module::AsyncTryFrom< &'a str > for MyNumber + // { + // type Error = std::num::ParseIntError; + // + // async fn async_try_from( value : &'a str ) -> Result< Self, Self::Error > + // { + // // Simulate asynchronous work + // tokio::time::sleep( tokio::time::Duration::from_millis( 1 ) ).await; + // let num = value.parse::< u32 >()?; + // Ok( MyNumber( num ) ) + // } + // } + + #[ the_module::async_trait ] + impl the_module::AsyncTryFrom< String > for MyNumber + { + type Error = std::num::ParseIntError; + + async fn async_try_from( value : String ) -> Result< Self, Self::Error > + { + // Simulate asynchronous work + tokio::time::sleep( tokio::time::Duration::from_millis( 10 ) ).await; + let num = value.parse::< u32 >()?; + Ok( MyNumber( num ) ) + } + } + + use the_module::{ AsyncTryFrom, AsyncTryInto }; + + // Using AsyncTryFrom directly + match MyNumber::async_try_from( "42".to_string() ).await + { + Ok( my_num ) => println!( "Converted successfully: {}", my_num.0 ), + Err( e ) => println!( "Conversion failed: {:?}", e ), + } + + // Using AsyncTryInto, which is automatically implemented + let result : Result< MyNumber, _ > = "42".to_string().async_try_into().await; + match result + { + Ok( my_num ) => println!( "Converted successfully using AsyncTryInto: {}", my_num.0 ), + Err( e ) => println!( "Conversion failed using AsyncTryInto: {:?}", e ), + } +} + +#[ tokio::test ] +async fn async_from_test() +{ + // Example implementation of AsyncFrom for a custom type + struct MyNumber( u32 ); + + #[ the_module::async_trait ] + impl the_module::AsyncFrom< String > for MyNumber + { + async fn async_from( value : String ) -> Self + { + // Simulate asynchronous work + tokio::time::sleep( tokio::time::Duration::from_millis( 10 ) ).await; + let num = value.parse::< u32 >().unwrap_or( 0 ); + MyNumber( num ) + } + } + + use the_module::{ AsyncFrom, AsyncInto }; + + // Using AsyncFrom directly + let my_num : MyNumber = MyNumber::async_from( "42".to_string() ).await; + println!( "Converted successfully using AsyncFrom: {}", my_num.0 ); + + // Using AsyncInto, which is automatically implemented + let my_num : MyNumber = "42".to_string().async_into().await; + println!( "Converted successfully using AsyncInto: {}", my_num.0 ); +} diff --git a/module/core/async_from/tests/inc/mod.rs b/module/core/async_from/tests/inc/mod.rs new file mode 100644 index 0000000000..329271ad56 --- /dev/null +++ b/module/core/async_from/tests/inc/mod.rs @@ -0,0 +1,3 @@ +use super::*; + +mod basic_test; diff --git a/module/core/async_from/tests/tests.rs b/module/core/async_from/tests/tests.rs new file mode 100644 index 0000000000..299521de4e --- /dev/null +++ b/module/core/async_from/tests/tests.rs @@ -0,0 +1,9 @@ +#![ allow( unused_imports ) ] + +include!( "../../../../module/step/meta/src/module/terminal.rs" ); + +use async_from as the_module; +// use test_tools::exposed::*; + +#[ cfg( feature = "enabled" ) ] +mod inc; diff --git a/module/core/async_tools/Cargo.toml b/module/core/async_tools/Cargo.toml new file mode 100644 index 0000000000..0f6c4f835b --- /dev/null +++ b/module/core/async_tools/Cargo.toml @@ -0,0 +1,39 @@ +[package] +name = "async_tools" +version = "0.1.0" +edition = "2021" +authors = [ + "Kostiantyn Wandalen ", +] +license = "MIT" +readme = "Readme.md" +documentation = "https://docs.rs/async_tools" +repository = "https://github.com/Wandalen/wTools/tree/master/module/core/async_tools" +homepage = "https://github.com/Wandalen/wTools/tree/master/module/core/async_tools" +description = """ +Toolkit for asynchronous programming. +""" +categories = [ "algorithms", "development-tools" ] +keywords = [ "fundamental", "general-purpose" ] + +[lints] +workspace = true + +[package.metadata.docs.rs] +features = [ "full" ] +all-features = false + +[features] +default = [ "enabled", "async_from", "async_try_from" ] +full = [ "default" ] +enabled = [] +async_from = [ "async_from/async_from" ] +async_try_from = [ "async_from/async_try_from" ] + +[dependencies] +async-trait = { workspace = true } +async_from = { workspace = true } + +[dev-dependencies] +# test_tools = { workspace = true } +tokio = { workspace = true, default-features = false, features = [ "rt-multi-thread", "time", "macros" ] } diff --git a/module/core/async_tools/License b/module/core/async_tools/License new file mode 100644 index 0000000000..0804aed8e3 --- /dev/null +++ b/module/core/async_tools/License @@ -0,0 +1,22 @@ +Copyright Kostiantyn Mysnyk and Out of the Box Systems (c) 2021-2024 + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. diff --git a/module/core/async_tools/Readme.md b/module/core/async_tools/Readme.md new file mode 100644 index 0000000000..0b469a2688 --- /dev/null +++ b/module/core/async_tools/Readme.md @@ -0,0 +1,6 @@ + + +# Module :: async_tools +[![experimental](https://raster.shields.io/static/v1?label=stability&message=experimental&color=orange&logoColor=eee)](https://github.com/emersion/stability-badges#experimental) [![rust-status](https://github.com/Wandalen/wTools/actions/workflows/Moduleasync_fromPush.yml/badge.svg)](https://github.com/Wandalen/wTools/actions/workflows/Moduleasync_fromPush.yml) [![docs.rs](https://img.shields.io/docsrs/async_tools?color=e3e8f0&logo=docs.rs)](https://docs.rs/async_tools) [![discord](https://img.shields.io/discord/872391416519737405?color=eee&logo=discord&logoColor=eee&label=ask)](https://discord.gg/m3YfbXpUUY) + +Toolkit for asynchronous programming. diff --git a/module/core/async_tools/src/lib.rs b/module/core/async_tools/src/lib.rs new file mode 100644 index 0000000000..ab0bcbf7e8 --- /dev/null +++ b/module/core/async_tools/src/lib.rs @@ -0,0 +1,79 @@ + +#![ doc( html_logo_url = "https://raw.githubusercontent.com/Wandalen/wTools/master/asset/img/logo_v3_trans_square.png" ) ] +#![ doc( html_favicon_url = "https://raw.githubusercontent.com/Wandalen/wTools/alpha/asset/img/logo_v3_trans_square_icon_small_v2.ico" ) ] +#![ doc( html_root_url = "https://docs.rs/async_tools/latest/async_tools/" ) ] +#![ doc = include_str!( concat!( env!( "CARGO_MANIFEST_DIR" ), "/", "Readme.md" ) ) ] + +/// Namespace with dependencies. +#[ cfg( feature = "enabled" ) ] +pub mod dependency +{ + pub use ::async_trait; + pub use ::async_from; +} + +/// Internal namespace. +#[ cfg( feature = "enabled" ) ] +mod private +{ +} + +#[ cfg( feature = "enabled" ) ] +#[ doc( inline ) ] +#[ allow( unused_imports ) ] +pub use own::*; + +/// Own namespace of the module. +#[ cfg( feature = "enabled" ) ] +#[ allow( unused_imports ) ] +pub mod own +{ + use super::*; + #[ doc( inline ) ] + pub use orphan::*; + + #[ doc( inline ) ] + pub use ::async_from::orphan::*; + +} + +/// Orphan namespace of the module. +#[ cfg( feature = "enabled" ) ] +#[ allow( unused_imports ) ] +pub mod orphan +{ + use super::*; + #[ doc( inline ) ] + pub use exposed::*; + +} + +/// Exposed namespace of the module. +#[ cfg( feature = "enabled" ) ] +#[ allow( unused_imports ) ] +pub mod exposed +{ + use super::*; + + #[ doc( inline ) ] + pub use prelude::*; + + #[ doc( inline ) ] + pub use ::async_trait::async_trait; + + #[ doc( inline ) ] + pub use ::async_from::exposed::*; + +} + +/// Prelude to use essentials: `use my_module::prelude::*`. +#[ cfg( feature = "enabled" ) ] +#[ allow( unused_imports ) ] +pub mod prelude +{ + use super::*; + + #[ doc( inline ) ] + pub use ::async_from::prelude::*; + +} diff --git a/module/core/async_tools/tests/inc/basic_test.rs b/module/core/async_tools/tests/inc/basic_test.rs new file mode 100644 index 0000000000..c652899926 --- /dev/null +++ b/module/core/async_tools/tests/inc/basic_test.rs @@ -0,0 +1,84 @@ +use super::*; + +#[ tokio::test ] +async fn async_try_from_test() +{ + + // Example implementation of AsyncTryFrom for a custom type + struct MyNumber( u32 ); + + // xxx : qqq : broken + // #[ the_module::async_trait ] + // impl< 'a > the_module::AsyncTryFrom< &'a str > for MyNumber + // { + // type Error = std::num::ParseIntError; + // + // async fn async_try_from( value : &'a str ) -> Result< Self, Self::Error > + // { + // // Simulate asynchronous work + // tokio::time::sleep( tokio::time::Duration::from_millis( 1 ) ).await; + // let num = value.parse::< u32 >()?; + // Ok( MyNumber( num ) ) + // } + // } + + #[ the_module::async_trait ] + impl the_module::AsyncTryFrom< String > for MyNumber + { + type Error = std::num::ParseIntError; + + async fn async_try_from( value : String ) -> Result< Self, Self::Error > + { + // Simulate asynchronous work + tokio::time::sleep( tokio::time::Duration::from_millis( 10 ) ).await; + let num = value.parse::< u32 >()?; + Ok( MyNumber( num ) ) + } + } + + use the_module::{ AsyncTryFrom, AsyncTryInto }; + + // Using AsyncTryFrom directly + match MyNumber::async_try_from( "42".to_string() ).await + { + Ok( my_num ) => println!( "Converted successfully: {}", my_num.0 ), + Err( e ) => println!( "Conversion failed: {:?}", e ), + } + + // Using AsyncTryInto, which is automatically implemented + let result : Result< MyNumber, _ > = "42".to_string().async_try_into().await; + match result + { + Ok( my_num ) => println!( "Converted successfully using AsyncTryInto: {}", my_num.0 ), + Err( e ) => println!( "Conversion failed using AsyncTryInto: {:?}", e ), + } +} + +#[ tokio::test ] +async fn async_from_test() +{ + // Example implementation of AsyncFrom for a custom type + struct MyNumber( u32 ); + + #[ the_module::async_trait ] + impl the_module::AsyncFrom< String > for MyNumber + { + async fn async_tools( value : String ) -> Self + { + // Simulate asynchronous work + tokio::time::sleep( tokio::time::Duration::from_millis( 10 ) ).await; + let num = value.parse::< u32 >().unwrap_or( 0 ); + MyNumber( num ) + } + } + + use the_module::{ AsyncFrom, AsyncInto }; + + // Using AsyncFrom directly + let my_num : MyNumber = MyNumber::async_tools( "42".to_string() ).await; + println!( "Converted successfully using AsyncFrom: {}", my_num.0 ); + + // Using AsyncInto, which is automatically implemented + let my_num : MyNumber = "42".to_string().async_into().await; + println!( "Converted successfully using AsyncInto: {}", my_num.0 ); +} diff --git a/module/core/async_tools/tests/inc/mod.rs b/module/core/async_tools/tests/inc/mod.rs new file mode 100644 index 0000000000..329271ad56 --- /dev/null +++ b/module/core/async_tools/tests/inc/mod.rs @@ -0,0 +1,3 @@ +use super::*; + +mod basic_test; diff --git a/module/core/async_tools/tests/tests.rs b/module/core/async_tools/tests/tests.rs new file mode 100644 index 0000000000..42f32553db --- /dev/null +++ b/module/core/async_tools/tests/tests.rs @@ -0,0 +1,10 @@ +#![ allow( unused_imports ) ] + +include!( "../../../../module/step/meta/src/module/terminal.rs" ); + +use async_tools as the_module; +// use test_tools::exposed::*; + +#[ cfg( feature = "enabled" ) ] +#[ path = "../../../../module/core/async_from/tests/inc/mod.rs" ] +mod inc; diff --git a/module/core/clone_dyn/Cargo.toml b/module/core/clone_dyn/Cargo.toml index b27b73a6a0..400ba0e1fb 100644 --- a/module/core/clone_dyn/Cargo.toml +++ b/module/core/clone_dyn/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clone_dyn" -version = "0.25.0" +version = "0.29.0" edition = "2021" authors = [ "Kostiantyn Wandalen ", @@ -36,6 +36,7 @@ derive_clone_dyn = [ "dep:clone_dyn_meta", "clone_dyn_meta/enabled", "clone_dyn_ [dependencies] clone_dyn_meta = { workspace = true, optional = true } clone_dyn_types = { workspace = true, optional = true } +# clone_dyn_types = { version = "0.27.0", optional = true } [dev-dependencies] test_tools = { workspace = true } diff --git a/module/core/clone_dyn/src/lib.rs b/module/core/clone_dyn/src/lib.rs index 224432e031..18e0150163 100644 --- a/module/core/clone_dyn/src/lib.rs +++ b/module/core/clone_dyn/src/lib.rs @@ -5,7 +5,6 @@ #![ doc = include_str!( concat!( env!( "CARGO_MANIFEST_DIR" ), "/", "Readme.md" ) ) ] /// Namespace with dependencies. - #[ cfg( feature = "enabled" ) ] pub mod dependency { diff --git a/module/core/clone_dyn_meta/Cargo.toml b/module/core/clone_dyn_meta/Cargo.toml index 2359fb19c7..d77ad96088 100644 --- a/module/core/clone_dyn_meta/Cargo.toml +++ b/module/core/clone_dyn_meta/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clone_dyn_meta" -version = "0.24.0" +version = "0.27.0" edition = "2021" authors = [ "Kostiantyn Wandalen ", diff --git a/module/core/clone_dyn_types/Cargo.toml b/module/core/clone_dyn_types/Cargo.toml index 65eec4f684..4a145d0c13 100644 --- a/module/core/clone_dyn_types/Cargo.toml +++ b/module/core/clone_dyn_types/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "clone_dyn_types" -version = "0.24.0" +version = "0.26.0" edition = "2021" authors = [ "Kostiantyn Wandalen ", diff --git a/module/core/collection_tools/Cargo.toml b/module/core/collection_tools/Cargo.toml index 96ad524cc9..86dcfa51b3 100644 --- a/module/core/collection_tools/Cargo.toml +++ b/module/core/collection_tools/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "collection_tools" -version = "0.13.0" +version = "0.15.0" edition = "2021" authors = [ "Kostiantyn Wandalen ", @@ -28,7 +28,7 @@ all-features = false [features] no_std = [ - "test_tools/no_std", + # "test_tools/no_std", ] use_alloc = [ diff --git a/module/core/data_type/Cargo.toml b/module/core/data_type/Cargo.toml index f42fae69bd..c5f7155d97 100644 --- a/module/core/data_type/Cargo.toml +++ b/module/core/data_type/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "data_type" -version = "0.10.0" +version = "0.12.0" edition = "2021" authors = [ "Kostiantyn Wandalen ", @@ -32,7 +32,6 @@ default = [ # "use_std", "enabled", "dt_either", - "dt_prelude", "dt_interval", "dt_collection", # "dt_make", @@ -43,7 +42,6 @@ full = [ # "use_std", "enabled", "dt_either", - "dt_prelude", "dt_interval", "dt_collection", # "dt_make", @@ -54,43 +52,18 @@ no_std = [] use_alloc = [ "no_std" ] enabled = [] -# dt_prelude = [ "collection_tools/reexports" ] -dt_prelude = [] # rid of maybe? dt_interval = [ "interval_adapter/enabled" ] dt_collection = [ "collection_tools/enabled" ] dt_either = [ "either" ] # qqq : for Anton : integrate all features of collection_tools into data_type and reuse tests -# dt_type_constructor = [ "type_constructor/enabled" ] -# dt_make = [ "type_constructor/make" ] -# dt_vectorized_from = [ "type_constructor/vectorized_from" ] - -# = entries - -# [lib] -# name = "data_type" -# path = "src/dt/data_type_lib.rs" - -# [[test]] -# name = "data_type_test" -# path = "tests/dt/data_type_tests.rs" -# -# [[test]] -# name = "data_type_smoke_test" -# path = "tests/_integration_test/smoke_test.rs" -# -# [[example]] -# name = "data_type_trivial" -# path = "examples/data_type_trivial/src/main.rs" - [dependencies] ## external either = { version = "~1.6", optional = true } ## internal -# type_constructor = { workspace = true } interval_adapter = { workspace = true } collection_tools = { workspace = true } diff --git a/module/core/data_type/src/lib.rs b/module/core/data_type/src/lib.rs index 5f16a02ecb..7cdff4fae2 100644 --- a/module/core/data_type/src/lib.rs +++ b/module/core/data_type/src/lib.rs @@ -123,13 +123,13 @@ pub mod prelude pub use crate::dependency::collection_tools::prelude::*; // #[ cfg( any( not( feature = "no_std" ), feature = "use_alloc" ) ) ] - #[ cfg( feature = "dt_prelude" ) ] - #[ doc( inline ) ] - #[ allow( unused_imports ) ] - pub use core:: - { - fmt, - }; + // #[ cfg( feature = "dt_prelude" ) ] + // #[ doc( inline ) ] + // #[ allow( unused_imports ) ] + // pub use core:: + // { + // fmt, + // }; } diff --git a/module/core/derive_tools/Cargo.toml b/module/core/derive_tools/Cargo.toml index 0dd6c2d30f..a2d1e31fb4 100644 --- a/module/core/derive_tools/Cargo.toml +++ b/module/core/derive_tools/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "derive_tools" -version = "0.29.0" +version = "0.32.0" edition = "2021" authors = [ "Kostiantyn Wandalen ", diff --git a/module/core/derive_tools_meta/Cargo.toml b/module/core/derive_tools_meta/Cargo.toml index a5210dcfa6..ce3e0ce395 100644 --- a/module/core/derive_tools_meta/Cargo.toml +++ b/module/core/derive_tools_meta/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "derive_tools_meta" -version = "0.28.0" +version = "0.31.0" edition = "2021" authors = [ "Kostiantyn Wandalen ", diff --git a/module/core/diagnostics_tools/Cargo.toml b/module/core/diagnostics_tools/Cargo.toml index f8f93610c9..6b48adc190 100644 --- a/module/core/diagnostics_tools/Cargo.toml +++ b/module/core/diagnostics_tools/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "diagnostics_tools" -version = "0.9.0" +version = "0.10.0" edition = "2021" authors = [ "Kostiantyn Wandalen ", diff --git a/module/core/error_tools/Cargo.toml b/module/core/error_tools/Cargo.toml index 62f8ec7e88..de02c81004 100644 --- a/module/core/error_tools/Cargo.toml +++ b/module/core/error_tools/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "error_tools" -version = "0.17.0" +version = "0.19.0" edition = "2021" authors = [ "Kostiantyn Wandalen ", diff --git a/module/core/for_each/Cargo.toml b/module/core/for_each/Cargo.toml index c6387ef2cc..2e43d14153 100644 --- a/module/core/for_each/Cargo.toml +++ b/module/core/for_each/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "for_each" -version = "0.9.0" +version = "0.10.0" edition = "2021" authors = [ "Kostiantyn Wandalen ", diff --git a/module/core/former/Cargo.toml b/module/core/former/Cargo.toml index 6ccdadb0b4..df2a419b2b 100644 --- a/module/core/former/Cargo.toml +++ b/module/core/former/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "former" -version = "2.9.0" +version = "2.11.0" edition = "2021" authors = [ "Kostiantyn Wandalen ", @@ -65,5 +65,5 @@ former_types = { workspace = true } [dev-dependencies] -test_tools = { workspace = true, features = [ "full" ] } +test_tools = { workspace = true } collection_tools = { workspace = true, features = [ "collection_constructors" ] } diff --git a/module/core/former_meta/Cargo.toml b/module/core/former_meta/Cargo.toml index 1612da9af5..38845bc3f1 100644 --- a/module/core/former_meta/Cargo.toml +++ b/module/core/former_meta/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "former_meta" -version = "2.9.0" +version = "2.11.0" edition = "2021" authors = [ "Kostiantyn Wandalen ", @@ -57,4 +57,4 @@ iter_tools = { workspace = true } convert_case = { version = "0.6.0", default-features = false, optional = true, features = [] } [dev-dependencies] -test_tools = { workspace = true, features = [ "full" ] } +test_tools = { workspace = true } diff --git a/module/core/former_types/Cargo.toml b/module/core/former_types/Cargo.toml index 9277639847..1049a6c8bd 100644 --- a/module/core/former_types/Cargo.toml +++ b/module/core/former_types/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "former_types" -version = "2.9.0" +version = "2.12.0" edition = "2021" authors = [ "Kostiantyn Wandalen ", @@ -50,4 +50,4 @@ collection_tools = { workspace = true, features = [ "collection_constructors" ] [dev-dependencies] -test_tools = { workspace = true, features = [ "full" ] } +test_tools = { workspace = true } diff --git a/module/core/implements/Cargo.toml b/module/core/implements/Cargo.toml index 283004045e..8c468c0b60 100644 --- a/module/core/implements/Cargo.toml +++ b/module/core/implements/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "implements" -version = "0.9.0" +version = "0.10.0" edition = "2021" authors = [ "Kostiantyn Wandalen ", diff --git a/module/core/impls_index/Cargo.toml b/module/core/impls_index/Cargo.toml index 65c5781edd..1845411740 100644 --- a/module/core/impls_index/Cargo.toml +++ b/module/core/impls_index/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "impls_index" -version = "0.8.0" +version = "0.9.0" edition = "2021" authors = [ "Kostiantyn Wandalen ", diff --git a/module/core/impls_index_meta/Cargo.toml b/module/core/impls_index_meta/Cargo.toml index e680f0cc47..036c5a03d0 100644 --- a/module/core/impls_index_meta/Cargo.toml +++ b/module/core/impls_index_meta/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "impls_index_meta" -version = "0.8.0" +version = "0.9.0" edition = "2021" authors = [ "Kostiantyn Wandalen ", diff --git a/module/core/inspect_type/Cargo.toml b/module/core/inspect_type/Cargo.toml index 5bb17bb058..260320b11b 100644 --- a/module/core/inspect_type/Cargo.toml +++ b/module/core/inspect_type/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "inspect_type" -version = "0.11.0" +version = "0.12.0" edition = "2021" authors = [ "Kostiantyn Wandalen ", diff --git a/module/core/interval_adapter/Cargo.toml b/module/core/interval_adapter/Cargo.toml index ba40cf4f31..787fac61c8 100644 --- a/module/core/interval_adapter/Cargo.toml +++ b/module/core/interval_adapter/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "interval_adapter" -version = "0.25.0" +version = "0.27.0" edition = "2021" authors = [ "Kostiantyn Wandalen ", diff --git a/module/core/is_slice/Cargo.toml b/module/core/is_slice/Cargo.toml index 17dc95d0f2..2854cf37cd 100644 --- a/module/core/is_slice/Cargo.toml +++ b/module/core/is_slice/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "is_slice" -version = "0.10.0" +version = "0.11.0" edition = "2021" authors = [ "Kostiantyn Wandalen ", diff --git a/module/core/iter_tools/Cargo.toml b/module/core/iter_tools/Cargo.toml index 0ab282194a..7e4dcad983 100644 --- a/module/core/iter_tools/Cargo.toml +++ b/module/core/iter_tools/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "iter_tools" -version = "0.22.0" +version = "0.24.0" edition = "2021" authors = [ "Kostiantyn Wandalen ", diff --git a/module/core/iter_tools/src/iter.rs b/module/core/iter_tools/src/iter.rs index 86fbda3f33..727e18409f 100644 --- a/module/core/iter_tools/src/iter.rs +++ b/module/core/iter_tools/src/iter.rs @@ -5,6 +5,8 @@ mod private #[ allow( unused_imports ) ] use crate::*; // use ::itertools::process_results; + + #[ cfg( feature = "iter_trait" ) ] use clone_dyn_types::CloneDyn; /// Trait that encapsulates an iterator with specific characteristics and implemetning `CloneDyn`. diff --git a/module/core/macro_tools/Cargo.toml b/module/core/macro_tools/Cargo.toml index 15c70c5298..99877d2e6f 100644 --- a/module/core/macro_tools/Cargo.toml +++ b/module/core/macro_tools/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "macro_tools" -version = "0.41.0" +version = "0.44.0" edition = "2021" authors = [ "Kostiantyn Wandalen ", diff --git a/module/core/mem_tools/Cargo.toml b/module/core/mem_tools/Cargo.toml index 79a60f163b..cec41724d4 100644 --- a/module/core/mem_tools/Cargo.toml +++ b/module/core/mem_tools/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "mem_tools" -version = "0.7.0" +version = "0.8.0" edition = "2021" authors = [ "Kostiantyn Wandalen ", diff --git a/module/core/meta_tools/Cargo.toml b/module/core/meta_tools/Cargo.toml index c34c79a376..0fc0b3c61d 100644 --- a/module/core/meta_tools/Cargo.toml +++ b/module/core/meta_tools/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "meta_tools" -version = "0.11.0" +version = "0.12.0" edition = "2021" authors = [ "Kostiantyn Wandalen ", diff --git a/module/core/mod_interface/Cargo.toml b/module/core/mod_interface/Cargo.toml index 66a9f9bdc6..8f3e3f76f4 100644 --- a/module/core/mod_interface/Cargo.toml +++ b/module/core/mod_interface/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "mod_interface" -version = "0.27.0" +version = "0.30.0" edition = "2021" authors = [ "Kostiantyn Wandalen ", diff --git a/module/core/mod_interface_meta/Cargo.toml b/module/core/mod_interface_meta/Cargo.toml index 7c5cf7ad26..ab3e6c709c 100644 --- a/module/core/mod_interface_meta/Cargo.toml +++ b/module/core/mod_interface_meta/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "mod_interface_meta" -version = "0.26.0" +version = "0.29.0" edition = "2021" authors = [ "Kostiantyn Wandalen ", diff --git a/module/core/mod_interface_meta/src/lib.rs b/module/core/mod_interface_meta/src/lib.rs index f215f7f2ac..70dc68878e 100644 --- a/module/core/mod_interface_meta/src/lib.rs +++ b/module/core/mod_interface_meta/src/lib.rs @@ -43,7 +43,7 @@ // }; // } -// xxx : make use proper_path_tools::own::path working +// xxx : make use pth::own::path working // xxx : put modular files into a namespace `file` maybe // #[ cfg( feature = "enabled" ) ] diff --git a/module/core/process_tools/Cargo.toml b/module/core/process_tools/Cargo.toml index 6066e48942..e358031ef1 100644 --- a/module/core/process_tools/Cargo.toml +++ b/module/core/process_tools/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "process_tools" -version = "0.10.0" +version = "0.12.0" edition = "2021" authors = [ "Kostiantyn Wandalen ", @@ -31,7 +31,7 @@ full = [ "default" ] enabled = [ "mod_interface/enabled", "former/enabled", - "proper_path_tools/enabled", + "pth/enabled", "error_tools/enabled", "iter_tools/enabled", ] @@ -42,7 +42,7 @@ process_environment_is_cicd = [] [dependencies] mod_interface = { workspace = true } former = { workspace = true, features = [ "derive_former" ] } -proper_path_tools = { workspace = true } +pth = { workspace = true } error_tools = { workspace = true, features = [ "error_untyped" ] } iter_tools = { workspace = true } diff --git a/module/core/program_tools/Cargo.toml b/module/core/program_tools/Cargo.toml index 1ba2675334..a5e28c9202 100644 --- a/module/core/program_tools/Cargo.toml +++ b/module/core/program_tools/Cargo.toml @@ -36,7 +36,7 @@ full = [ enabled = [ "mod_interface/enabled", "former/enabled", - "proper_path_tools/enabled", + "pth/enabled", "error_tools/enabled", "iter_tools/enabled", ] @@ -44,7 +44,7 @@ enabled = [ [dependencies] mod_interface = { workspace = true } former = { workspace = true, features = [ "derive_former" ] } -proper_path_tools = { workspace = true } +pth = { workspace = true } error_tools = { workspace = true, features = [ "error_untyped" ] } # qqq : xxx : rid of error_untyped iter_tools = { workspace = true } diff --git a/module/core/proper_path_tools/Readme.md b/module/core/proper_path_tools/Readme.md deleted file mode 100644 index d142018019..0000000000 --- a/module/core/proper_path_tools/Readme.md +++ /dev/null @@ -1,35 +0,0 @@ - - -# Module :: proper_path_tools - - [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) [![rust-status](https://github.com/Wandalen/wTools/actions/workflows/module_proper_path_tools_push.yml/badge.svg)](https://github.com/Wandalen/wTools/actions/workflows/module_proper_path_tools_push.yml) [![docs.rs](https://img.shields.io/docsrs/proper_path_tools?color=e3e8f0&logo=docs.rs)](https://docs.rs/proper_path_tools) [![discord](https://img.shields.io/discord/872391416519737405?color=eee&logo=discord&logoColor=eee&label=ask)](https://discord.gg/m3YfbXpUUY) - - -Collection of algorithms and structures to handle paths properly. - -All functions in the crate don't touch file system, but only process paths. - - diff --git a/module/core/proper_path_tools/Cargo.toml b/module/core/pth/Cargo.toml similarity index 87% rename from module/core/proper_path_tools/Cargo.toml rename to module/core/pth/Cargo.toml index abcf335fb3..79a2141441 100644 --- a/module/core/proper_path_tools/Cargo.toml +++ b/module/core/pth/Cargo.toml @@ -1,15 +1,15 @@ [package] -name = "proper_path_tools" -version = "0.11.0" +name = "pth" +version = "0.21.0" edition = "2021" authors = [ "Kostiantyn Wandalen ", ] license = "MIT" readme = "Readme.md" -documentation = "https://docs.rs/proper_path_tools" -repository = "https://github.com/Wandalen/wTools/tree/master/module/core/proper_path_tools" -homepage = "https://github.com/Wandalen/wTools/tree/master/module/core/proper_path_tools" +documentation = "https://docs.rs/pth" +repository = "https://github.com/Wandalen/wTools/tree/master/module/core/pth" +homepage = "https://github.com/Wandalen/wTools/tree/master/module/core/pth" description = """ Collection of algorithms and structures to handle paths properly. """ @@ -35,6 +35,8 @@ full = [ "path_utf8", ] no_std = [] +# qqq : xxx : negate no_std +# use_std = [] use_alloc = [ "no_std" ] enabled = [ "mod_interface/enabled" ] diff --git a/module/core/pth/License b/module/core/pth/License new file mode 100644 index 0000000000..0804aed8e3 --- /dev/null +++ b/module/core/pth/License @@ -0,0 +1,22 @@ +Copyright Kostiantyn Mysnyk and Out of the Box Systems (c) 2021-2024 + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. diff --git a/module/core/pth/Readme.md b/module/core/pth/Readme.md new file mode 100644 index 0000000000..a6f4c2f04d --- /dev/null +++ b/module/core/pth/Readme.md @@ -0,0 +1,51 @@ + + +# Module :: pth + + [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) [![rust-status](https://github.com/Wandalen/wTools/actions/workflows/module_pth_push.yml/badge.svg)](https://github.com/Wandalen/wTools/actions/workflows/module_pth_push.yml) [![docs.rs](https://img.shields.io/docsrs/pth?color=e3e8f0&logo=docs.rs)](https://docs.rs/pth) [![discord](https://img.shields.io/discord/872391416519737405?color=eee&logo=discord&logoColor=eee&label=ask)](https://discord.gg/m3YfbXpUUY) + + +Collection of algorithms and structures to handle paths properly. + +All functions in the crate don't touch file system, but only process paths. + +### Type `AbsolutePath` + +The AbsolutePath type ensures that paths are absolute, which helps reduce issues and maintenance costs associated with relative paths. Relative paths can be problematic as they introduce additional variables and complexities, making code analysis, integration, refactoring, and testing more difficult. By using absolute paths, software architecture can be improved, similar to how avoiding global variables can enhance code quality. It is recommended to use relative paths only at the outskirts of an application. + +### Trait `AsPath` + +This trait is used to avoid redundant allocation of memory by providing a reference to a Path. It is implemented only for types that can either be referenced or are references to Path itself. Unlike `TryIntoPath`, it does not allocate memory on the heap. However, `TryIntoPath` is implemented for a wider range of types because it is not restricted from allocating memory. Unlike `AsRef< Path >`, `AsPath` is implemented for a wider number of types, including those that are not directly convertible to a Path using `AsRef`. This is because `AsPath` is designed to provide a more flexible interface for path-like types, accommodating various representations that can logically be treated as paths. + +### Trait `TryIntoPath` + +This trait is used to convert any path-like type into an owned PathBuf. Unlike `TryIntoCowPath`, it always returns an owned PathBuf, so there is no need to differentiate between borrowed and owned paths at runtime. Unlike `AsPath`, it is implemented for a wider range of path-like types, similar to `TryIntoCowPath`. + +### Trait `TryIntoCowPath` + +This trait is designed to avoid redundant memory allocation. Unlike TryIntoPath, it does not allocate memory on the heap if it’s not necessary. Unlike `AsPath`, it is implemented for a wider number of path-like types, similar to TryIntoPath. The drawback is the necessity to differentiate borrowed and owned paths at runtime. + + diff --git a/module/core/pth/src/as_path.rs b/module/core/pth/src/as_path.rs new file mode 100644 index 0000000000..b94a4cf4d4 --- /dev/null +++ b/module/core/pth/src/as_path.rs @@ -0,0 +1,71 @@ +/// Internal namespace. +mod private +{ + #[ allow( unused_imports ) ] + use crate::*; + #[ cfg( feature = "no_std" ) ] + extern crate std; + + use std::path::Path; + + /// A trait for converting various types into a reference to a `Path`. + /// + /// This trait is used to avoid redundant allocation of memory by providing a reference to a `Path`. + /// It is implemented only for types that can either be referenced or are references to `Path` itself. + /// Unlike `TryIntoPath`, it does not allocate memory on the heap. However, `TryIntoPath` is implemented for a wider range of types because it is not restricted from allocating memory. + /// Unlike `AsRef`, `AsPath` is implemented for a wider number of types, including those that are not directly convertible to a `Path` using `AsRef`. + /// This is because `AsPath` is designed to provide a more flexible interface for path-like types, accommodating various representations that can logically be treated as paths. + pub trait AsPath + { + /// Converts the implementing type into a reference to a `Path`. + /// + /// # Returns + /// + /// A reference to a `Path`. + fn as_path( &self ) -> &Path; + } + + /// Implementation of `AsPath` for `str`. + impl AsPath for str + { + fn as_path( &self ) -> &Path + { + Path::new( self ) + } + } + + /// Implementation of `AsPath` for `Path`. + impl AsPath for Path + { + fn as_path( &self ) -> &Path + { + self + } + } + + /// Implementation of `AsPath` for `Utf8Path`. + #[cfg( feature = "path_utf8" )] + impl AsPath for Utf8Path + { + fn as_path( &self ) -> &Path + { + self.as_std_path() + } + } + + /// Blanket implementation of `AsPath` for all types that implement `AsRef`. + impl< T > AsPath for T + where + T : AsRef< Path >, + { + fn as_path( &self ) -> &Path + { + self.as_ref() + } + } +} + +crate::mod_interface! +{ + orphan use AsPath; +} \ No newline at end of file diff --git a/module/core/proper_path_tools/src/lib.rs b/module/core/pth/src/lib.rs similarity index 67% rename from module/core/proper_path_tools/src/lib.rs rename to module/core/pth/src/lib.rs index 297c2aeff8..223020ac00 100644 --- a/module/core/proper_path_tools/src/lib.rs +++ b/module/core/pth/src/lib.rs @@ -1,7 +1,7 @@ #![ cfg_attr( feature = "no_std", no_std ) ] #![ doc( html_logo_url = "https://raw.githubusercontent.com/Wandalen/wTools/master/asset/img/logo_v3_trans_square.png" ) ] #![ doc( html_favicon_url = "https://raw.githubusercontent.com/Wandalen/wTools/alpha/asset/img/logo_v3_trans_square_icon_small_v2.ico" ) ] -#![ doc( html_root_url = "https://docs.rs/proper_path_tools/latest/proper_path_tools/" ) ] +#![ doc( html_root_url = "https://docs.rs/pth/latest/pth/" ) ] #![ doc = include_str!( concat!( env!( "CARGO_MANIFEST_DIR" ), "/", "Readme.md" ) ) ] #[ cfg( feature = "enabled" ) ] @@ -20,12 +20,26 @@ mod_interface! /// Basic functionality. layer path; + /// AsPath trait. + layer as_path; + /// TryIntoPath trait. + layer try_into_path; + /// TryIntoPath trait. + layer try_into_cow_path; + /// Transitive TryFrom and TryInto. layer transitive; #[ cfg( feature = "path_utf8" ) ] own use ::camino::{ Utf8Path, Utf8PathBuf }; + + // #[ cfg( not( feature = "no_std" ) ) ] + // own use ::std::path::{ PathBuf, Path, Component }; + + #[ cfg( not( feature = "no_std" ) ) ] + own use ::std::path::*; + #[ cfg( not( feature = "no_std" ) ) ] - own use ::std::path::{ PathBuf, Path }; + own use ::std::borrow::Cow; } diff --git a/module/core/proper_path_tools/src/path.rs b/module/core/pth/src/path.rs similarity index 95% rename from module/core/proper_path_tools/src/path.rs rename to module/core/pth/src/path.rs index 313fb4fcd9..7907a3268a 100644 --- a/module/core/proper_path_tools/src/path.rs +++ b/module/core/pth/src/path.rs @@ -2,6 +2,8 @@ mod private { + use crate::*; + #[ cfg( feature = "no_std" ) ] extern crate std; @@ -25,7 +27,7 @@ mod private /// # Examples: /// /// ``` - /// use proper_path_tools::path; + /// use pth::path; /// /// assert_eq!( path::is_glob( "file.txt" ), false ); // No glob patterns /// assert_eq!( path::is_glob( "*.txt" ), true ); // Contains unescaped glob character * @@ -110,7 +112,7 @@ mod private /// /// ``` /// use std::path::{ Path, PathBuf }; - /// use proper_path_tools::path as path; + /// use pth::path as path; /// /// let path = Path::new( "/a/b/./c/../d" ); /// let normalized_path = path::normalize( path ); @@ -260,7 +262,7 @@ mod private /// # Examples /// /// ``` - /// use proper_path_tools::path::unique_folder_name; + /// use pth::path::unique_folder_name; /// let folder_name = unique_folder_name().unwrap(); /// println!( "Generated folder name: {}", folder_name ); /// ``` @@ -309,23 +311,22 @@ mod private /// /// ``` /// use std::path::PathBuf; - /// use proper_path_tools::path; + /// use pth::path; /// /// let paths = vec![ PathBuf::from( "a/b/c" ), PathBuf::from( "/d/e" ), PathBuf::from( "f/g" ) ]; - /// let joined = path::join_paths( paths.iter().map( | p | p.as_path() ) ); + /// let joined = path::iter_join( paths.iter().map( | p | p.as_path() ) ); /// assert_eq!( joined, std::path::PathBuf::from( "/d/e/f/g" ) ); /// /// let paths = vec![ PathBuf::from( "" ), PathBuf::from( "a/b" ), PathBuf::from( "" ), PathBuf::from( "c" ), PathBuf::from( "" ) ]; - /// let joined = path::join_paths( paths.iter().map( | p | p.as_path() ) ); + /// let joined = path::iter_join( paths.iter().map( | p | p.as_path() ) ); /// assert_eq!( joined, std::path::PathBuf::from( PathBuf::from( "/a/b/c" ) ) ); /// /// ``` // qqq : make macro paths_join!( ... ) - pub fn join_paths< 'a, I >( paths : I ) -> std::path::PathBuf + pub fn iter_join< 'a ,I, P >( paths : I ) -> PathBuf where - // AsPath : AsRef< std::path::Path >, - // I : Iterator< Item = AsPath >, - I : Iterator< Item = &'a std::path::Path >, + I : Iterator< Item = P >, + P : TryIntoCowPath< 'a >, { #[ cfg( feature = "no_std" ) ] extern crate alloc; @@ -338,8 +339,12 @@ mod private for path in paths { - let mut path = path.to_string_lossy().replace( '\\', "/" ); - path = path.replace( ':', "" ); + // let mut path = path.to_string_lossy().replace( '\\', "/" ); + // qqq : xxx : avoid unwrap + let path = path.try_into_cow_path().unwrap().to_string_lossy().replace( '\\', "/" ); + // qqq : xxx : avoid converting to String, keep it Path + + // path = path.replace( ':', "" ); // qqq : this is a bug let mut added_slah = false; @@ -450,7 +455,7 @@ mod private /// # Examples /// /// ``` - /// use proper_path_tools::path::exts; + /// use pth::path::exts; /// /// let path = "/path/to/file.tar.gz"; /// let extensions = exts( path ); @@ -458,7 +463,7 @@ mod private /// ``` /// /// ``` - /// use proper_path_tools::path::exts; + /// use pth::path::exts; /// /// let empty_path = ""; /// let extensions = exts( empty_path ); @@ -513,7 +518,7 @@ mod private /// /// ``` /// use std::path::PathBuf; - /// use proper_path_tools::path::without_ext; + /// use pth::path::without_ext; /// /// let path = "/path/to/file.txt"; /// let modified_path = without_ext(path); @@ -522,7 +527,7 @@ mod private /// /// ``` /// use std::path::PathBuf; - /// use proper_path_tools::path::without_ext; + /// use pth::path::without_ext; /// /// let empty_path = ""; /// let modified_path = without_ext(empty_path); @@ -592,7 +597,7 @@ mod private /// /// ``` /// use std::path::PathBuf; - /// use proper_path_tools::path::change_ext; + /// use pth::path::change_ext; /// /// let path = "/path/to/file.txt"; /// let modified_path = change_ext( path, "json" ); @@ -601,7 +606,7 @@ mod private /// /// ``` /// use std::path::PathBuf; - /// use proper_path_tools::path::change_ext; + /// use pth::path::change_ext; /// /// let empty_path = ""; /// let modified_path = change_ext( empty_path, "txt" ); @@ -643,7 +648,7 @@ mod private /// # Examples /// /// ``` - /// use proper_path_tools::path::path_common; + /// use pth::path::path_common; /// /// let paths = vec![ "/a/b/c", "/a/b/d", "/a/b/e" ]; /// let common_path = path_common( paths.into_iter() ); @@ -835,7 +840,7 @@ mod private /// /// let file_path = "/home/user/documents/file.txt"; /// let new_path = "/mnt/storage"; - /// let rebased_path = proper_path_tools::path::rebase( file_path, new_path, None ).unwrap(); + /// let rebased_path = pth::path::rebase( file_path, new_path, None ).unwrap(); /// assert_eq!( rebased_path, PathBuf::from( "/mnt/storage/home/user/documents/file.txt" ) ); /// ``` /// @@ -847,7 +852,7 @@ mod private /// let file_path = "/home/user/documents/file.txt"; /// let new_path = "/mnt/storage"; /// let old_path = "/home/user"; - /// let rebased_path = proper_path_tools::path::rebase( file_path, new_path, Some( old_path ) ).unwrap(); + /// let rebased_path = pth::path::rebase( file_path, new_path, Some( old_path ) ).unwrap(); /// assert_eq!( rebased_path, PathBuf::from( "/mnt/storage/documents/file.txt" ) ); /// ``` /// @@ -901,7 +906,7 @@ mod private /// /// let from = "/a/b"; /// let to = "/a/c/d"; - /// let relative_path = proper_path_tools::path::path_relative( from, to ); + /// let relative_path = pth::path::path_relative( from, to ); /// assert_eq!( relative_path, PathBuf::from( "../c/d" ) ); /// ``` pub fn path_relative< T : AsRef< std::path::Path > >( from : T, to : T ) -> std::path::PathBuf @@ -1009,7 +1014,7 @@ mod private /// # Examples /// /// ``` - /// use proper_path_tools::path::ext; + /// use pth::path::ext; /// /// let path = "/path/to/file.txt"; /// let extension = ext( path ); @@ -1017,7 +1022,7 @@ mod private /// ``` /// /// ``` - /// use proper_path_tools::path::ext; + /// use pth::path::ext; /// /// let empty_path = ""; /// let extension = ext( empty_path ); @@ -1048,17 +1053,20 @@ mod private crate::mod_interface! { - orphan use ext; - orphan use exts; - orphan use change_ext; - orphan use path_relative; - orphan use rebase; - orphan use path_common; - orphan use join_paths; - orphan use without_ext; - orphan use is_glob; - orphan use normalize; - orphan use canonicalize; + orphan use + { + ext, + exts, + change_ext, + path_relative, + rebase, + path_common, + iter_join, + without_ext, + is_glob, + normalize, + canonicalize, + }; #[ cfg( feature = "path_unique_folder_name" ) ] orphan use unique_folder_name; @@ -1072,4 +1080,7 @@ crate::mod_interface! /// Describe native path. Use to pass path to the platfrom. layer native_path; + /// Convenient joining. + layer joining; + } diff --git a/module/core/proper_path_tools/src/path/absolute_path.rs b/module/core/pth/src/path/absolute_path.rs similarity index 54% rename from module/core/proper_path_tools/src/path/absolute_path.rs rename to module/core/pth/src/path/absolute_path.rs index 16fbf3ec78..13a9686207 100644 --- a/module/core/proper_path_tools/src/path/absolute_path.rs +++ b/module/core/pth/src/path/absolute_path.rs @@ -1,55 +1,49 @@ /// Internal namespace. mod private { - use crate::*; - use std:: { - // borrow::Cow, path::{ Path, PathBuf }, io, }; - use core:: { fmt, - ops:: - { - Deref, - DerefMut, - }, + ops::{ Deref, DerefMut }, }; - - #[ cfg( feature="no_std" ) ] + #[ cfg( feature = "no_std" ) ] extern crate std; - - #[ cfg( feature="no_std" ) ] + #[ cfg( feature = "no_std" ) ] use alloc::string::String; - #[ cfg( feature = "derive_serde" ) ] use serde::{ Serialize, Deserialize }; - #[ cfg( feature = "path_utf8" ) ] - use camino::{ Utf8Path, Utf8PathBuf }; + // #[ cfg( feature = "path_utf8" ) ] + // use camino::{ Utf8Path, Utf8PathBuf }; - /// Absolute path. + /// A new type representing an absolute path. + /// + /// The `AbsolutePath` type ensures that paths are absolute, which helps reduce issues and maintenance costs associated with relative paths. + /// Relative paths can be problematic as they introduce additional variables and complexities, making code analysis, integration, refactoring, and testing more difficult. + /// By using absolute paths, software architecture can be improved, similar to how avoiding global variables can enhance code quality. + /// It is recommended to use relative paths only at the outskirts of an application. #[ cfg_attr( feature = "derive_serde", derive( Serialize, Deserialize ) ) ] #[ derive( Debug, Default, Clone, Ord, PartialOrd, Eq, PartialEq, Hash ) ] pub struct AbsolutePath( PathBuf ); impl AbsolutePath { - - /// Returns the Path without its final component, if there is one. - /// Returns None if the path terminates in a root or prefix, or if it's the empty string. + /// Returns the parent directory as an `AbsolutePath`, if it exists. + /// + /// Returns `None` if the path terminates in a root or prefix, or if it's the empty string. #[ inline ] pub fn parent( &self ) -> Option< AbsolutePath > { self.0.parent().map( PathBuf::from ).map( AbsolutePath ) } - /// Creates an owned `AbsolutePath` with path adjoined to self. + /// Creates an owned `AbsolutePath` by joining a given path to `self`. #[ inline ] pub fn join< P >( &self, path : P ) -> AbsolutePath where @@ -58,13 +52,7 @@ mod private Self::try_from( self.0.join( path ) ).unwrap() } - // /// Converts a `AbsolutePath` to a `Cow` - // pub fn to_string_lossy( &self ) -> Cow< '_, str > - // { - // self.0.to_string_lossy() - // } - - /// Determines whether base is a prefix of self. + /// Checks if the path starts with a given base path. /// /// Only considers whole path components to match. #[ inline ] @@ -73,27 +61,52 @@ mod private self.0.starts_with( base ) } - /// Returns inner type which is PathBuf. - #[ inline( always ) ] + /// Returns the inner `PathBuf`. + #[inline(always)] pub fn inner( self ) -> PathBuf { self.0 } - // qqq : xxx : cover by minimal tests - // qqq : xxx : make iterator over Paths also working - /// Joins a list of strs into a single absolute path. - pub fn from_strs< 'a, I >( iter : I ) -> Result< Self, io::Error > + /// Creates an `AbsolutePath` from an iterator over items that implement `TryIntoCowPath`. + /// + /// This function joins all path segments into a single path and attempts to convert it + /// into an `AbsolutePath`. The resulting path must be absolute. + /// + /// # Arguments + /// + /// * `iter` - An iterator over path segments. + /// + /// # Returns + /// + /// * `Ok(AbsolutePath)` if the joined path is absolute. + /// * `Err(io::Error)` if the joined path is not absolute. + pub fn from_iter< 'a, I, P >( iter : I ) -> Result< Self, io::Error > where - I : Iterator< Item = &'a str >, + I : Iterator< Item = P >, + P : TryIntoCowPath< 'a >, { - // Join all the path segments using join_paths - let joined_path = path::join_paths( iter.map( Path::new ) ); - - // Convert the joined PathBuf into an AbsolutePath + let joined_path = iter_join( iter ); AbsolutePath::try_from( joined_path ) } + /// Joins path components into a `PathBuf`. + /// + /// This function leverages the `PathJoined` trait to join multiple path components into a single `PathBuf`. + /// + /// # Arguments + /// + /// * `paths` - A tuple of path components implementing the `PathJoined` trait. + /// + /// # Returns + /// + /// * `Ok(PathBuf)` - The joined path as a `PathBuf`. + /// * `Err(io::Error)` - An error if any component fails to convert. + pub fn from_paths< Paths : PathJoined >( paths : Paths ) -> Result< Self, io::Error > + { + Self::try_from( paths.iter_join()? ) + } + } impl fmt::Display for AbsolutePath @@ -108,8 +121,6 @@ mod private #[ inline ] fn is_absolute( path : &Path ) -> bool { - // None - not absolute - // with `.` or `..` at the first component - not absolute !path.components().next().is_some_and( | c | c.as_os_str() == "." || c.as_os_str() == ".." ) } @@ -135,7 +146,6 @@ mod private } } - // xxx : qqq : use Into< Path > impl TryFrom< &Path > for AbsolutePath { type Error = std::io::Error; @@ -143,13 +153,11 @@ mod private #[ inline ] fn try_from( src : &Path ) -> Result< Self, Self::Error > { - // < Self as TryFrom< &str > >::try_from( src.to_string_lossy() ) let path = path::canonicalize( src )?; - // xxx if !is_absolute( &path ) { - return Err( io::Error::new( io::ErrorKind::InvalidData, "Path expected to be absolute, but it's not {path}" ) ) + return Err( io::Error::new( io::ErrorKind::Other, format!( "Path expected to be absolute, but it's not {path:?}" ) ) ); } Ok( Self( path ) ) @@ -167,19 +175,29 @@ mod private } } -// impl TryFrom< &str > for AbsolutePath -// { -// type Error = std::io::Error; -// // type Error = PathError; -// -// #[ inline( always ) ] -// fn try_from( src : &str ) -> Result< Self, Self::Error > -// { -// Self::try_from( AbsolutePath::try_from( src )? ) -// } -// } - - #[ cfg( feature = "path_utf8" ) ] + impl< 'a > TryFrom< &'a String > for AbsolutePath + { + type Error = std::io::Error; + + #[ inline ] + fn try_from( src : &'a String ) -> Result< Self, Self::Error > + { + < Self as TryFrom< &Path > >::try_from( src.as_ref() ) + } + } + + impl< 'a > TryFrom< String > for AbsolutePath + { + type Error = std::io::Error; + + #[ inline ] + fn try_from( src : String ) -> Result< Self, Self::Error > + { + < Self as TryFrom< &Path > >::try_from( src.as_ref() ) + } + } + + #[cfg( feature = "path_utf8" )] impl TryFrom< Utf8PathBuf > for AbsolutePath { type Error = std::io::Error; @@ -191,7 +209,7 @@ mod private } } - #[ cfg( feature = "path_utf8" ) ] + #[cfg( feature = "path_utf8" )] impl TryFrom< &Utf8PathBuf > for AbsolutePath { type Error = std::io::Error; @@ -203,7 +221,7 @@ mod private } } - #[ cfg( feature = "path_utf8" ) ] + #[cfg( feature = "path_utf8" )] impl TryFrom< &Utf8Path > for AbsolutePath { type Error = std::io::Error; @@ -227,21 +245,18 @@ mod private impl< 'a > TryFrom< &'a AbsolutePath > for &'a str { type Error = std::io::Error; + #[ inline ] fn try_from( src : &'a AbsolutePath ) -> Result< &'a str, Self::Error > { - src - .to_str() - .ok_or_else - ( - move || io::Error::new( io::ErrorKind::Other, format!( "Can't convert &PathBuf into &str {src}" ) ) - ) + src.to_str().ok_or_else( || io::Error::new( io::ErrorKind::Other, format!( "Can't convert &PathBuf into &str {src}" ) ) ) } } impl TryFrom< &AbsolutePath > for String { type Error = std::io::Error; + #[ inline ] fn try_from( src : &AbsolutePath ) -> Result< String, Self::Error > { @@ -250,34 +265,23 @@ mod private } } -// impl TryFrom< Utf8PathBuf > for AbsolutePath -// { -// type Error = std::io::Error; -// -// fn try_from( src : Utf8PathBuf ) -> Result< Self, Self::Error > -// { -// AbsolutePath::try_from( src.as_std_path() ) -// } -// } - -// impl TryFrom< &Utf8Path > for AbsolutePath -// { -// type Error = std::io::Error; -// -// fn try_from( src : &Utf8Path ) -> Result< Self, Self::Error > -// { -// AbsolutePath::try_from( src.as_std_path() ) -// } -// } - - // // xxx : use derives - // impl AsRef< Path > for AbsolutePath - // { - // fn as_ref( &self ) -> &Path - // { - // self.0.as_ref() - // } - // } + impl TryIntoPath for AbsolutePath + { + #[ inline ] + fn try_into_path( self ) -> Result< PathBuf, io::Error > + { + Ok( self.0 ) + } + } + + impl< 'a > TryIntoCowPath< 'a > for AbsolutePath + { + #[ inline ] + fn try_into_cow_path( self ) -> Result< Cow<'a, Path>, io::Error > + { + Ok( Cow::Owned( self.0 ) ) + } + } impl AsRef< Path > for AbsolutePath { @@ -300,6 +304,7 @@ mod private impl Deref for AbsolutePath { type Target = Path; + #[ inline ] fn deref( &self ) -> &Self::Target { @@ -315,42 +320,9 @@ mod private &mut self.0 } } - -// /// Convertable into absolute path entity should implement the trait. -// pub trait TryIntoAbsolutePath -// { -// /// Error returned if conversion is failed. -// type Error; -// /// Method to convert the type into absolute path. -// fn into_absolute_path( self ) -> Result< AbsolutePath, Self::Error >; -// } -// -// // impl TryIntoAbsolutePath for AbsolutePath -// // { -// // type Error = std::io::Error; -// // #[ inline ] -// // fn into_absolute_path( self ) -> Result< AbsolutePath, Self::Error > -// // { -// // Ok( self ) -// // } -// // } -// -// impl< TryIntoAbsolutePathType > TryIntoAbsolutePath for TryIntoAbsolutePathType -// where -// TryIntoAbsolutePathType : TryInto< AbsolutePath >, -// { -// type Error = < Self as TryInto< AbsolutePath > >::Error; -// #[ inline ] -// fn into_absolute_path( self ) -> Result< AbsolutePath, Self::Error > -// { -// self.try_into() -// } -// } - } crate::mod_interface! { exposed use AbsolutePath; - // exposed use TryIntoAbsolutePath; -} +} \ No newline at end of file diff --git a/module/core/proper_path_tools/src/path/canonical_path.rs b/module/core/pth/src/path/canonical_path.rs similarity index 85% rename from module/core/proper_path_tools/src/path/canonical_path.rs rename to module/core/pth/src/path/canonical_path.rs index d6c84168da..b7a871af4d 100644 --- a/module/core/proper_path_tools/src/path/canonical_path.rs +++ b/module/core/pth/src/path/canonical_path.rs @@ -21,6 +21,7 @@ mod private }, }; + // qqq : xxx : redo #[ cfg( feature="no_std" ) ] extern crate std; @@ -30,8 +31,8 @@ mod private #[ cfg( feature = "derive_serde" ) ] use serde::{ Serialize, Deserialize }; - #[ cfg( feature = "path_utf8" ) ] - use camino::{ Utf8Path, Utf8PathBuf }; + // #[ cfg( feature = "path_utf8" ) ] + // use camino::{ Utf8Path, Utf8PathBuf }; /// Caninical path. #[ cfg_attr( feature = "derive_serde", derive( Serialize, Deserialize ) ) ] @@ -114,6 +115,28 @@ mod private } } + impl< 'a > TryFrom< &'a String > for CanonicalPath + { + type Error = std::io::Error; + + #[ inline ] + fn try_from( src : &'a String ) -> Result< Self, Self::Error > + { + < Self as TryFrom< &Path > >::try_from( src.as_ref() ) + } + } + + impl< 'a > TryFrom< String > for CanonicalPath + { + type Error = std::io::Error; + + #[ inline ] + fn try_from( src : String ) -> Result< Self, Self::Error > + { + < Self as TryFrom< &Path > >::try_from( src.as_ref() ) + } + } + impl TryFrom< PathBuf > for CanonicalPath { type Error = std::io::Error; @@ -216,6 +239,32 @@ mod private } } + impl TryIntoPath for CanonicalPath + { + #[ inline ] + fn try_into_path( self ) -> Result< PathBuf, io::Error > + { + Ok( self.0 ) + } + } + + impl< 'a > TryIntoCowPath< 'a > for CanonicalPath + { + #[ inline ] + fn try_into_cow_path( self ) -> Result< Cow<'a, Path>, io::Error > + { + Ok( Cow::Owned( self.0 ) ) + } + } + + // impl AsPath for CanonicalPath + // { + // fn as_path( &self ) -> &Path + // { + // self.0.as_path() + // } + // } + // impl TryFrom< Utf8PathBuf > for CanonicalPath // { // type Error = std::io::Error; diff --git a/module/core/proper_path_tools/src/path/current_path.rs b/module/core/pth/src/path/current_path.rs similarity index 67% rename from module/core/proper_path_tools/src/path/current_path.rs rename to module/core/pth/src/path/current_path.rs index a35c927c0f..cc5fe4aaaa 100644 --- a/module/core/proper_path_tools/src/path/current_path.rs +++ b/module/core/pth/src/path/current_path.rs @@ -4,7 +4,11 @@ mod private use crate::*; #[ cfg( not( feature = "no_std" ) ) ] - use std::env; + use std:: + { + env, + io, + }; /// Symbolize current path. #[ derive( Clone, Copy, Debug, Default, PartialEq, Eq ) ] @@ -48,7 +52,7 @@ mod private env::current_dir() } } - + #[ cfg( not( feature = "no_std" ) ) ] impl TryFrom< CurrentPath > for AbsolutePath { @@ -62,6 +66,39 @@ mod private } } + impl TryIntoPath for &CurrentPath + { + fn try_into_path( self ) -> Result< PathBuf, io::Error > + { + env::current_dir() + } + } + + impl TryIntoPath for CurrentPath + { + fn try_into_path( self ) -> Result< PathBuf, io::Error > + { + env::current_dir() + } + } + + impl< 'a > TryIntoCowPath< 'a > for CurrentPath + { + fn try_into_cow_path( self ) -> Result< Cow<'a, Path>, io::Error > + { + let current_dir = env::current_dir()?; + Ok( Cow::Owned( current_dir ) ) + } + } + + impl< 'a > TryIntoCowPath< 'a > for &CurrentPath + { + fn try_into_cow_path( self ) -> Result< Cow<'a, Path>, io::Error > + { + TryIntoCowPath::try_into_cow_path( *self ) + } + } + } crate::mod_interface! diff --git a/module/core/pth/src/path/joining.rs b/module/core/pth/src/path/joining.rs new file mode 100644 index 0000000000..6e8c0a1ddf --- /dev/null +++ b/module/core/pth/src/path/joining.rs @@ -0,0 +1,191 @@ +mod private +{ + use crate::*; + use std::{ io, path::PathBuf }; + + /// Joins path components into a `PathBuf`. + /// + /// This function leverages the `PathJoined` trait to join multiple path components into a single `PathBuf`. + /// + /// # Arguments + /// + /// * `paths` - A tuple of path components implementing the `PathJoined` trait. + /// + /// # Returns + /// + /// * `Ok(PathBuf)` - The joined path as a `PathBuf`. + /// * `Err(io::Error)` - An error if any component fails to convert. + pub fn join< Paths : PathJoined >( paths : Paths ) -> Result< PathBuf, io::Error > + { + paths.iter_join() + } + + /// A trait for joining path components into a `PathBuf`. + /// + /// This trait provides a method to join multiple path components into a single `PathBuf`. + /// It is implemented for tuples of varying lengths, allowing for flexible combinations of path components. + /// Each component must implement the `TryIntoCowPath` trait, enabling conversion into a `Cow`. + pub trait PathJoined + { + /// Joins the path components into a single `PathBuf`. + /// + /// # Returns + /// + /// * `Ok(PathBuf)` - The joined path as a `PathBuf`. + /// * `Err(io::Error)` - An error if any component fails to convert. + fn iter_join( self ) -> Result< PathBuf, io::Error >; + } + + // // Implementation for an Iterator over items implementing TryIntoCowPath + // impl< 'a, I, T > PathJoined for I + // where + // I : Iterator< Item = T >, + // T : TryIntoCowPath< 'a >, + // { + // fn iter_join( self ) -> Result< PathBuf, io::Error > + // { + // let mut result = PathBuf::new(); + // for item in self + // { + // result.push( item.try_into_cow_path()?.as_ref() ); + // } + // Ok( result ) + // } + // } + + // Implementation for a tuple of length 1 + impl< 'a, T1 > PathJoined for ( T1, ) + where + T1 : TryIntoCowPath< 'a >, + { + #[ inline ] + fn iter_join( self ) -> Result< PathBuf, io::Error > + { + let ( p1, ) = self; + let mut result = PathBuf::new(); + result.push( p1.try_into_cow_path()?.as_ref() ); + Ok( result ) + } + } + + // Implementation for a tuple of length 2 + impl< 'a, T1, T2 > PathJoined for ( T1, T2 ) + where + T1 : TryIntoCowPath< 'a >, + T2 : TryIntoCowPath< 'a >, + { + #[ inline ] + fn iter_join( self ) -> Result< PathBuf, io::Error > + { + let ( p1, p2 ) = self; + let mut result = PathBuf::new(); + result.push( p1.try_into_cow_path()?.as_ref() ); + result.push( p2.try_into_cow_path()?.as_ref() ); + Ok( result ) + } + } + + // Implementation for a tuple of length 3 + impl< 'a, T1, T2, T3 > PathJoined for ( T1, T2, T3 ) + where + T1 : TryIntoCowPath< 'a >, + T2 : TryIntoCowPath< 'a >, + T3 : TryIntoCowPath< 'a >, + { + #[ inline ] + fn iter_join( self ) -> Result< PathBuf, io::Error > + { + let ( p1, p2, p3 ) = self; + let mut result = PathBuf::new(); + result.push( p1.try_into_cow_path()?.as_ref() ); + result.push( p2.try_into_cow_path()?.as_ref() ); + result.push( p3.try_into_cow_path()?.as_ref() ); + Ok( result ) + } + } + + // Implementation for a tuple of length 4 + impl< 'a, T1, T2, T3, T4 > PathJoined for ( T1, T2, T3, T4 ) + where + T1 : TryIntoCowPath< 'a >, + T2 : TryIntoCowPath< 'a >, + T3 : TryIntoCowPath< 'a >, + T4 : TryIntoCowPath< 'a >, + { + #[ inline ] + fn iter_join( self ) -> Result< PathBuf, io::Error > + { + let ( p1, p2, p3, p4 ) = self; + let mut result = PathBuf::new(); + result.push( p1.try_into_cow_path()?.as_ref() ); + result.push( p2.try_into_cow_path()?.as_ref() ); + result.push( p3.try_into_cow_path()?.as_ref() ); + result.push( p4.try_into_cow_path()?.as_ref() ); + Ok( result ) + } + } + + // Implementation for a tuple of length 5 + impl< 'a, T1, T2, T3, T4, T5 > PathJoined for ( T1, T2, T3, T4, T5 ) + where + T1 : TryIntoCowPath< 'a >, + T2 : TryIntoCowPath< 'a >, + T3 : TryIntoCowPath< 'a >, + T4 : TryIntoCowPath< 'a >, + T5 : TryIntoCowPath< 'a >, + { + #[ inline ] + fn iter_join( self ) -> Result< PathBuf, io::Error > + { + let ( p1, p2, p3, p4, p5 ) = self; + let mut result = PathBuf::new(); + result.push( p1.try_into_cow_path()?.as_ref() ); + result.push( p2.try_into_cow_path()?.as_ref() ); + result.push( p3.try_into_cow_path()?.as_ref() ); + result.push( p4.try_into_cow_path()?.as_ref() ); + result.push( p5.try_into_cow_path()?.as_ref() ); + Ok( result ) + } + } + + // Implementation for slices + impl< 'a, T > PathJoined for &'a [ T ] + where + T : TryIntoCowPath< 'a > + Clone, + { + #[ inline ] + fn iter_join( self ) -> Result< PathBuf, io::Error > + { + let mut result = PathBuf::new(); + for item in self + { + result.push( item.clone().try_into_cow_path()?.as_ref() ); + } + Ok( result ) + } + } + + // Implementation for arrays + impl< 'a, T, const N : usize > PathJoined for [ T; N ] + where + T : TryIntoCowPath< 'a > + Clone, + { + #[ inline ] + fn iter_join( self ) -> Result< PathBuf, io::Error > + { + let mut result = PathBuf::new(); + for item in &self + { + result.push( item.clone().try_into_cow_path()?.as_ref() ); + } + Ok( result ) + } + } + +} + +crate::mod_interface! +{ + orphan use join; + exposed use PathJoined; +} diff --git a/module/core/proper_path_tools/src/path/native_path.rs b/module/core/pth/src/path/native_path.rs similarity index 86% rename from module/core/proper_path_tools/src/path/native_path.rs rename to module/core/pth/src/path/native_path.rs index d41b449acc..09dfaaed62 100644 --- a/module/core/proper_path_tools/src/path/native_path.rs +++ b/module/core/pth/src/path/native_path.rs @@ -30,8 +30,8 @@ mod private #[ cfg( feature = "derive_serde" ) ] use serde::{ Serialize, Deserialize }; - #[ cfg( feature = "path_utf8" ) ] - use camino::{ Utf8Path, Utf8PathBuf }; + // #[ cfg( feature = "path_utf8" ) ] + // use camino::{ Utf8Path, Utf8PathBuf }; /// Caninical path. #[ cfg_attr( feature = "derive_serde", derive( Serialize, Deserialize ) ) ] @@ -114,6 +114,28 @@ mod private } } + impl< 'a > TryFrom< &'a String > for NativePath + { + type Error = std::io::Error; + + #[ inline ] + fn try_from( src : &'a String ) -> Result< Self, Self::Error > + { + < Self as TryFrom< &Path > >::try_from( src.as_ref() ) + } + } + + impl< 'a > TryFrom< String > for NativePath + { + type Error = std::io::Error; + + #[ inline ] + fn try_from( src : String ) -> Result< Self, Self::Error > + { + < Self as TryFrom< &Path > >::try_from( src.as_ref() ) + } + } + impl TryFrom< PathBuf > for NativePath { type Error = std::io::Error; @@ -231,6 +253,32 @@ mod private } } + impl TryIntoPath for NativePath + { + #[ inline ] + fn try_into_path( self ) -> Result< PathBuf, io::Error > + { + Ok( self.0 ) + } + } + + impl< 'a > TryIntoCowPath< 'a > for NativePath + { + #[ inline ] + fn try_into_cow_path( self ) -> Result< Cow<'a, Path>, io::Error > + { + Ok( Cow::Owned( self.0 ) ) + } + } + + // impl AsPath for NativePath + // { + // fn as_path( &self ) -> &Path + // { + // self.0.as_path() + // } + // } + // impl TryFrom< Utf8PathBuf > for NativePath // { // type Error = std::io::Error; diff --git a/module/core/proper_path_tools/src/transitive.rs b/module/core/pth/src/transitive.rs similarity index 98% rename from module/core/proper_path_tools/src/transitive.rs rename to module/core/pth/src/transitive.rs index 9de7eef34b..93bbcd3e10 100644 --- a/module/core/proper_path_tools/src/transitive.rs +++ b/module/core/pth/src/transitive.rs @@ -49,7 +49,7 @@ mod private /// # Example /// /// ```rust - /// use proper_path_tools::TransitiveTryFrom; + /// use pth::TransitiveTryFrom; /// use std::convert::TryFrom; /// /// struct InitialType; @@ -132,7 +132,7 @@ mod private /// # Example /// /// ```rust - /// use proper_path_tools::TransitiveTryInto; + /// use pth::TransitiveTryInto; /// use std::convert::TryInto; /// /// struct InitialType; diff --git a/module/core/pth/src/try_into_cow_path.rs b/module/core/pth/src/try_into_cow_path.rs new file mode 100644 index 0000000000..b9f04524ce --- /dev/null +++ b/module/core/pth/src/try_into_cow_path.rs @@ -0,0 +1,111 @@ +/// Internal namespace. +mod private +{ + use crate::*; + use std:: + { + borrow::Cow, + io, + path::{ Component, Path, PathBuf }, + }; + // use camino::{ Utf8Path, Utf8PathBuf }; + + /// A trait for converting various types into a `Cow`. + /// + /// This trait is designed to avoid redundant memory allocation. + /// Unlike `TryIntoPath`, it does not allocate memory on the heap if it's not necessary. + /// Unlike `AsPath`, it is implemented for a wider number of path-like types, similar to `TryIntoPath`. + /// The drawback is the necessity to differentiate borrowed and owned paths at runtime. + pub trait TryIntoCowPath<'a> + { + /// Converts the implementing type into a `Cow`. + /// + /// # Returns + /// + /// * `Ok(Cow)` - A `Cow` that may be either borrowed or owned, depending on the input type. + /// * `Err(io::Error)` - An error if the conversion fails. + fn try_into_cow_path( self ) -> Result< Cow<'a, Path>, io::Error >; + } + + /// Implementation of `TryIntoCowPath` for `String`. + impl<'a> TryIntoCowPath<'a> for &'a str + { + fn try_into_cow_path( self ) -> Result< Cow<'a, Path>, io::Error > + { + Ok( Cow::Borrowed( self.as_path() ) ) + } + } + + /// Implementation of `TryIntoCowPath` for `String`. + impl<'a> TryIntoCowPath<'a> for String + { + fn try_into_cow_path( self ) -> Result< Cow<'a, Path>, io::Error > + { + Ok( Cow::Owned( PathBuf::from( self ) ) ) + } + } + + /// Implementation of `TryIntoCowPath` for `PathBuf`. + impl<'a> TryIntoCowPath<'a> for PathBuf + { + fn try_into_cow_path( self ) -> Result< Cow<'a, Path>, io::Error > + { + Ok( Cow::Owned( self ) ) + } + } + + /// Implementation of `TryIntoCowPath` for a reference to `Path`. + impl<'a> TryIntoCowPath<'a> for &'a Path + { + fn try_into_cow_path( self ) -> Result< Cow<'a, Path>, io::Error > + { + Ok( Cow::Borrowed( self ) ) + } + } + + /// Implementation of `TryIntoCowPath` for a reference to `Utf8Path`. + #[cfg( feature = "path_utf8" )] + impl< 'a > TryIntoCowPath< 'a > for &'a Utf8Path + { + fn try_into_cow_path( self ) -> Result< Cow<'a, Path>, io::Error > + { + Ok( Cow::Borrowed( self.as_std_path() ) ) + } + } + + /// Implementation of `TryIntoCowPath` for `Utf8PathBuf`. + #[cfg( feature = "path_utf8" )] + impl<'a> TryIntoCowPath<'a> for Utf8PathBuf + { + fn try_into_cow_path( self ) -> Result< Cow<'a, Path>, io::Error > + { + Ok( Cow::Owned( self.as_std_path().to_path_buf() ) ) + } + } + + /// Implementation of `TryIntoCowPath` for `std::path::Component`. + impl<'a> TryIntoCowPath<'a> for Component<'a> + { + fn try_into_cow_path( self ) -> Result< Cow<'a, Path>, io::Error > + { + Ok( Cow::Owned( PathBuf::from( self.as_os_str() ) ) ) + } + } + + /// Blanket implementation of `TryIntoCowPath` for references to types implementing `AsPath`. + impl<'a, T> TryIntoCowPath< 'a > for &'a T + where + T : AsPath, + { + fn try_into_cow_path( self ) -> Result< Cow<'a, Path>, io::Error > + { + Ok( Cow::Borrowed( self.as_path() ) ) + } + } + +} + +crate::mod_interface! +{ + orphan use TryIntoCowPath; +} \ No newline at end of file diff --git a/module/core/pth/src/try_into_path.rs b/module/core/pth/src/try_into_path.rs new file mode 100644 index 0000000000..29f508ec1b --- /dev/null +++ b/module/core/pth/src/try_into_path.rs @@ -0,0 +1,109 @@ +/// Internal namespace. +mod private +{ + #[ allow( unused_imports ) ] + use crate::*; + use std:: + { + io, + path::{ Component, Path, PathBuf }, + }; + // use camino::{ Utf8Path, Utf8PathBuf }; + + /// A trait for converting various types into a `PathBuf`. + /// + /// This trait is used to convert any path-like type into an owned `PathBuf`. + /// Unlike `TryIntoCowPath`, it always returns an owned `PathBuf`, so there is no need to differentiate between borrowed and owned paths at runtime. + /// Unlike `AsPath`, it is implemented for a wider range of path-like types, similar to `TryIntoCowPath`. + pub trait TryIntoPath + { + /// Converts the implementing type into a `PathBuf`. + /// + /// # Returns + /// + /// * `Ok(PathBuf)` - The owned path buffer. + /// * `Err(io::Error)` - An error if the conversion fails. + fn try_into_path( self ) -> Result< PathBuf, io::Error >; + } + + /// Implementation of `TryIntoPath` for `&str`. + impl TryIntoPath for &str + { + fn try_into_path( self ) -> Result< PathBuf, io::Error > + { + Ok( PathBuf::from( self ) ) + } + } + + /// Implementation of `TryIntoPath` for `String`. + impl TryIntoPath for String + { + fn try_into_path( self ) -> Result< PathBuf, io::Error > + { + Ok( PathBuf::from( self ) ) + } + } + + /// Implementation of `TryIntoPath` for a reference to `Path`. + impl TryIntoPath for &Path + { + fn try_into_path( self ) -> Result< PathBuf, io::Error > + { + Ok( self.to_path_buf() ) + } + } + + /// Implementation of `TryIntoPath` for `PathBuf`. + impl TryIntoPath for PathBuf + { + fn try_into_path( self ) -> Result< PathBuf, io::Error > + { + Ok( self ) + } + } + + /// Implementation of `TryIntoPath` for a reference to `Utf8Path`. + #[cfg( feature = "path_utf8" )] + impl TryIntoPath for &Utf8Path + { + fn try_into_path( self ) -> Result< PathBuf, io::Error > + { + Ok( self.as_std_path().to_path_buf() ) + } + } + + /// Implementation of `TryIntoPath` for `Utf8PathBuf`. + #[cfg( feature = "path_utf8" )] + impl TryIntoPath for Utf8PathBuf + { + fn try_into_path( self ) -> Result< PathBuf, io::Error > + { + Ok( self.as_std_path().to_path_buf() ) + } + } + + /// Implementation of `TryIntoPath` for `std::path::Component`. + impl TryIntoPath for Component<'_> + { + fn try_into_path( self ) -> Result< PathBuf, io::Error > + { + Ok( self.as_os_str().into() ) + } + } + + /// Blanket implementation of `TryIntoPath` for references to types implementing `AsRef`. + impl< T > TryIntoPath for &T + where + T : AsRef< Path >, + { + fn try_into_path( self ) -> Result< PathBuf, io::Error > + { + Ok( self.as_ref().to_path_buf() ) + } + } +} + +crate::mod_interface! +{ + orphan use TryIntoPath; +} \ No newline at end of file diff --git a/module/core/proper_path_tools/tests/experiment.rs b/module/core/pth/tests/experiment.rs similarity index 94% rename from module/core/proper_path_tools/tests/experiment.rs rename to module/core/pth/tests/experiment.rs index 29e2cd3eba..60e36f8879 100644 --- a/module/core/proper_path_tools/tests/experiment.rs +++ b/module/core/pth/tests/experiment.rs @@ -2,7 +2,7 @@ include!( "../../../../module/step/meta/src/module/terminal.rs" ); #[ allow( unused_imports ) ] -use proper_path_tools as the_module; +use pth as the_module; #[ allow( unused_imports ) ] use test_tools::exposed::*; diff --git a/module/core/pth/tests/inc/absolute_path_test.rs b/module/core/pth/tests/inc/absolute_path_test.rs new file mode 100644 index 0000000000..6d15a1fb2b --- /dev/null +++ b/module/core/pth/tests/inc/absolute_path_test.rs @@ -0,0 +1,5 @@ +use super::*; + +mod basic_test; +mod from_paths_test; +mod try_from_test; diff --git a/module/core/proper_path_tools/tests/inc/absolute_path.rs b/module/core/pth/tests/inc/absolute_path_test/basic_test.rs similarity index 93% rename from module/core/proper_path_tools/tests/inc/absolute_path.rs rename to module/core/pth/tests/inc/absolute_path_test/basic_test.rs index 94e4ff2ab2..3f8a254ba0 100644 --- a/module/core/proper_path_tools/tests/inc/absolute_path.rs +++ b/module/core/pth/tests/inc/absolute_path_test/basic_test.rs @@ -1,7 +1,5 @@ -#[ allow( unused_imports ) ] use super::*; -#[ cfg( not( feature="no_std" ) ) ] use the_module:: { AbsolutePath, @@ -9,12 +7,6 @@ use the_module:: PathBuf, }; -#[ cfg( feature="no_std" ) ] -use crate::the_module::AbsolutePath; - -// #[ cfg( feature = "path_utf8" ) ] -// use the_module::Utf8PathBuf; - #[ test ] fn basic() { @@ -75,7 +67,6 @@ fn test_join() assert_eq!( joined_path.to_string_lossy(), "/path/to/some/file.txt" ); } - #[test] fn test_relative_path_try_from_str() { diff --git a/module/core/pth/tests/inc/absolute_path_test/from_paths_test.rs b/module/core/pth/tests/inc/absolute_path_test/from_paths_test.rs new file mode 100644 index 0000000000..3e5bd05dd4 --- /dev/null +++ b/module/core/pth/tests/inc/absolute_path_test/from_paths_test.rs @@ -0,0 +1,92 @@ +use super::*; + +// xxx : make it working + +#[ test ] +fn test_from_paths_single_absolute_segment() +{ + use the_module::AbsolutePath; + use std::convert::TryFrom; + + let segments = vec![ "/single" ]; + let got = AbsolutePath::from_iter( segments.iter().map( |s| *s ) ).unwrap(); + let exp = AbsolutePath::try_from( "/single" ).unwrap(); + + assert_eq!( got, exp ); +} + +#[ test ] +fn test_from_paths_multiple_segments() +{ + use the_module::AbsolutePath; + use std::convert::TryFrom; + + let segments = vec![ "/path", "to", "file" ]; + let got = AbsolutePath::from_iter( segments.iter().map( |s| *s ) ).unwrap(); + let exp = AbsolutePath::try_from( "/path/to/file" ).unwrap(); + + assert_eq!( got, exp ); +} + +#[ test ] +fn test_from_paths_empty_segments() +{ + use the_module::AbsolutePath; + + let segments : Vec< &str > = vec![]; + let result = AbsolutePath::from_iter( segments.iter().map( | s | *s ) ); + + assert!( result.is_err(), "Expected an error for empty segments" ); +} + +#[ test ] +fn test_from_paths_with_dot_segments() +{ + use the_module::AbsolutePath; + use std::convert::TryFrom; + + let segments = vec![ "/path", ".", "to", "file" ]; + let got = AbsolutePath::from_iter( segments.iter().map( |s| *s ) ).unwrap(); + let exp = AbsolutePath::try_from( "/path/to/file" ).unwrap(); + + assert_eq!( got, exp ); +} + +#[ test ] +fn test_from_paths_with_dotdot_segments() +{ + use the_module::AbsolutePath; + use std::convert::TryFrom; + + let segments = vec![ "/path", "to", "..", "file" ]; + let got = AbsolutePath::from_iter( segments.iter().map( |s| *s ) ).unwrap(); + let exp = AbsolutePath::try_from( "/path/file" ).unwrap(); + + assert_eq!( got, exp ); +} + +#[ test ] +fn test_from_paths_with_trailing_slash() +{ + use the_module::AbsolutePath; + use std::convert::TryFrom; + + let segments = vec![ "/path", "to", "file/" ]; + let got = AbsolutePath::from_iter( segments.iter().map( |s| *s ) ).unwrap(); + let exp = AbsolutePath::try_from( "/path/to/file/" ).unwrap(); + + assert_eq!( got, exp ); +} + +#[ test ] +fn test_from_paths_with_mixed_slashes() +{ + use the_module::AbsolutePath; + use std::convert::TryFrom; + + let segments = vec![ "/path\\to", "file" ]; + let got = AbsolutePath::from_iter( segments.iter().map( |s| *s ) ).unwrap(); + let exp = AbsolutePath::try_from( "/path/to/file" ).unwrap(); + + assert_eq!( got, exp ); +} diff --git a/module/core/pth/tests/inc/absolute_path_test/try_from_test.rs b/module/core/pth/tests/inc/absolute_path_test/try_from_test.rs new file mode 100644 index 0000000000..ee1aa2b3a1 --- /dev/null +++ b/module/core/pth/tests/inc/absolute_path_test/try_from_test.rs @@ -0,0 +1,55 @@ +use super::*; +use std::convert::TryFrom; + +#[ test ] +fn try_from_absolute_path_test() +{ + use std::path::{ Path, PathBuf }; + use the_module::AbsolutePath; + + // Create an AbsolutePath instance + let absolute_path = AbsolutePath::try_from( "/absolute/path" ).unwrap(); + + // Test conversion to &str + let path_str : &str = TryFrom::try_from( &absolute_path ).unwrap(); + println!( "&str from AbsolutePath: {:?}", path_str ); + assert_eq!( path_str, "/absolute/path" ); + + // Test conversion to String + let path_string : String = TryFrom::try_from( &absolute_path ).unwrap(); + println!( "String from AbsolutePath: {:?}", path_string ); + assert_eq!( path_string, "/absolute/path" ); + + // Test conversion to PathBuf + let path_buf : PathBuf = TryFrom::try_from( absolute_path.clone() ).unwrap(); + println!( "PathBuf from AbsolutePath: {:?}", path_buf ); + assert_eq!( path_buf, PathBuf::from( "/absolute/path" ) ); + + // Test conversion to &Path + let path_ref : &Path = absolute_path.as_ref(); + println!( "&Path from AbsolutePath: {:?}", path_ref ); + assert_eq!( path_ref, Path::new( "/absolute/path" ) ); + + // Test conversion from &String + let string_path : String = String::from( "/absolute/path" ); + let absolute_path_from_string : AbsolutePath = TryFrom::try_from( &string_path ).unwrap(); + println!( "AbsolutePath from &String: {:?}", absolute_path_from_string ); + assert_eq!( absolute_path_from_string, absolute_path ); + + // Test conversion from String + let absolute_path_from_owned_string : AbsolutePath = TryFrom::try_from( string_path.clone() ).unwrap(); + println!( "AbsolutePath from String: {:?}", absolute_path_from_owned_string ); + assert_eq!( absolute_path_from_owned_string, absolute_path ); + + // Test conversion from &Path + let path_ref : &Path = Path::new( "/absolute/path" ); + let absolute_path_from_path_ref : AbsolutePath = TryFrom::try_from( path_ref ).unwrap(); + println!( "AbsolutePath from &Path: {:?}", absolute_path_from_path_ref ); + assert_eq!( absolute_path_from_path_ref, absolute_path ); + + // Test conversion from PathBuf + let path_buf_instance : PathBuf = PathBuf::from( "/absolute/path" ); + let absolute_path_from_path_buf : AbsolutePath = TryFrom::try_from( path_buf_instance.clone() ).unwrap(); + println!( "AbsolutePath from PathBuf: {:?}", absolute_path_from_path_buf ); + assert_eq!( absolute_path_from_path_buf, absolute_path ); +} \ No newline at end of file diff --git a/module/core/pth/tests/inc/as_path_test.rs b/module/core/pth/tests/inc/as_path_test.rs new file mode 100644 index 0000000000..340a6540ca --- /dev/null +++ b/module/core/pth/tests/inc/as_path_test.rs @@ -0,0 +1,103 @@ +use super::*; + +#[ test ] +fn as_path_test() +{ + use std::path::{ Component, Path, PathBuf }; + #[ cfg( feature = "path_utf8" ) ] + use the_module::{ Utf8Path, Utf8PathBuf }; + use the_module::{ AsPath, AbsolutePath, CanonicalPath, NativePath, CurrentPath }; + + // Test with &str + let path_str : &str = "/some/path"; + let path : &Path = AsPath::as_path( path_str ); + println!( "Path from &str: {:?}", path ); + + // Test with &String + let string_path : String = String::from( "/another/path" ); + let path : &Path = AsPath::as_path( &string_path ); + println!( "Path from &String: {:?}", path ); + + // Test with String + let path : &Path = AsPath::as_path( &string_path ); + println!( "Path from String: {:?}", path ); + + // Test with &Path + let path_ref : &Path = Path::new( "/yet/another/path" ); + let path : &Path = AsPath::as_path( path_ref ); + println!( "Path from &Path: {:?}", path ); + + // Test with &PathBuf + let path_buf : PathBuf = PathBuf::from( "/yet/another/path" ); + let path : &Path = AsPath::as_path( &path_buf ); + println!( "Path from &PathBuf: {:?}", path ); + + // Test with PathBuf + let path : &Path = AsPath::as_path( &path_buf ); + println!( "Path from PathBuf: {:?}", path ); + + // Test with &AbsolutePath + let absolute_path : AbsolutePath = AbsolutePath::try_from( "/absolute/path" ).unwrap(); + let path : &Path = AsPath::as_path( &absolute_path ); + println!( "Path from &AbsolutePath: {:?}", path ); + + // Test with AbsolutePath + let path : &Path = AsPath::as_path( &absolute_path ); + println!( "Path from AbsolutePath: {:?}", path ); + + // Test with &CanonicalPath + let canonical_path = CanonicalPath::try_from( "/canonical/path" ).unwrap(); + let path : &Path = AsPath::as_path( &canonical_path ); + println!( "Path from &CanonicalPath: {:?}", path ); + + // Test with CanonicalPath + let path : &Path = AsPath::as_path( &canonical_path ); + println!( "Path from CanonicalPath: {:?}", path ); + + // Test with &NativePath + let native_path = NativePath::try_from( PathBuf::from( "/native/path" ) ).unwrap(); + let path : &Path = AsPath::as_path( &native_path ); + println!( "Path from &NativePath: {:?}", path ); + + // Test with NativePath + let path : &Path = AsPath::as_path( &native_path ); + println!( "Path from NativePath: {:?}", path ); + + // Test with &Component + let root_component : Component< '_ > = Component::RootDir; + let path : &Path = AsPath::as_path( &root_component ); + println!( "Path from &Component: {:?}", path ); + + // Test with Component + let path : &Path = AsPath::as_path( &root_component ); + println!( "Path from Component: {:?}", path ); + + // Test with Component + let path = Path::new( "/component/path" ); + for component in path.components() + { + let path : &Path = AsPath::as_path( &component ); + println!( "Path from Component: {:?}", path ); + } + + #[ cfg( feature = "path_utf8" ) ] + { + // Test with &Utf8Path + let utf8_path = Utf8Path::new( "/utf8/path" ); + let path : &Path = AsPath::as_path( &utf8_path ); + println!( "Path from &Utf8Path: {:?}", path ); + + // Test with Utf8Path + let path : &Path = AsPath::as_path( &utf8_path ); + println!( "Path from Utf8Path: {:?}", path ); + + // Test with &Utf8PathBuf + let utf8_path_buf = Utf8PathBuf::from( "/utf8/pathbuf" ); + let path : &Path = AsPath::as_path( &utf8_path_buf ); + println!( "Path from &Utf8PathBuf: {:?}", path ); + + // Test with Utf8PathBuf + let path : &Path = AsPath::as_path( &utf8_path_buf ); + println!( "Path from Utf8PathBuf: {:?}", path ); + } +} diff --git a/module/core/proper_path_tools/tests/inc/current_path.rs b/module/core/pth/tests/inc/current_path.rs similarity index 100% rename from module/core/proper_path_tools/tests/inc/current_path.rs rename to module/core/pth/tests/inc/current_path.rs diff --git a/module/core/proper_path_tools/tests/inc/mod.rs b/module/core/pth/tests/inc/mod.rs similarity index 57% rename from module/core/proper_path_tools/tests/inc/mod.rs rename to module/core/pth/tests/inc/mod.rs index 58e8721710..8026b293ba 100644 --- a/module/core/proper_path_tools/tests/inc/mod.rs +++ b/module/core/pth/tests/inc/mod.rs @@ -1,8 +1,14 @@ -#[allow(unused_imports)] use super::*; -mod absolute_path; +mod as_path_test; +mod try_into_path_test; +mod try_into_cow_path_test; + +mod absolute_path_test; +mod path_join_fn_test; +mod path_join_trait_test; + mod current_path; mod path_canonicalize; mod path_change_ext; @@ -10,12 +16,11 @@ mod path_common; mod path_ext; mod path_exts; mod path_is_glob; -mod path_join; mod path_normalize; mod path_relative; mod rebase_path; mod transitive; mod without_ext; -#[cfg(feature = "path_unique_folder_name")] +#[ cfg( feature = "path_unique_folder_name" ) ] mod path_unique_folder_name; diff --git a/module/core/proper_path_tools/tests/inc/path_canonicalize.rs b/module/core/pth/tests/inc/path_canonicalize.rs similarity index 100% rename from module/core/proper_path_tools/tests/inc/path_canonicalize.rs rename to module/core/pth/tests/inc/path_canonicalize.rs diff --git a/module/core/proper_path_tools/tests/inc/path_change_ext.rs b/module/core/pth/tests/inc/path_change_ext.rs similarity index 100% rename from module/core/proper_path_tools/tests/inc/path_change_ext.rs rename to module/core/pth/tests/inc/path_change_ext.rs diff --git a/module/core/proper_path_tools/tests/inc/path_common.rs b/module/core/pth/tests/inc/path_common.rs similarity index 100% rename from module/core/proper_path_tools/tests/inc/path_common.rs rename to module/core/pth/tests/inc/path_common.rs diff --git a/module/core/proper_path_tools/tests/inc/path_ext.rs b/module/core/pth/tests/inc/path_ext.rs similarity index 100% rename from module/core/proper_path_tools/tests/inc/path_ext.rs rename to module/core/pth/tests/inc/path_ext.rs diff --git a/module/core/proper_path_tools/tests/inc/path_exts.rs b/module/core/pth/tests/inc/path_exts.rs similarity index 100% rename from module/core/proper_path_tools/tests/inc/path_exts.rs rename to module/core/pth/tests/inc/path_exts.rs diff --git a/module/core/proper_path_tools/tests/inc/path_is_glob.rs b/module/core/pth/tests/inc/path_is_glob.rs similarity index 100% rename from module/core/proper_path_tools/tests/inc/path_is_glob.rs rename to module/core/pth/tests/inc/path_is_glob.rs diff --git a/module/core/proper_path_tools/tests/inc/path_join.rs b/module/core/pth/tests/inc/path_join_fn_test.rs similarity index 76% rename from module/core/proper_path_tools/tests/inc/path_join.rs rename to module/core/pth/tests/inc/path_join_fn_test.rs index fa526ee19d..f5a2acd005 100644 --- a/module/core/proper_path_tools/tests/inc/path_join.rs +++ b/module/core/pth/tests/inc/path_join_fn_test.rs @@ -5,7 +5,7 @@ use std::path::PathBuf; fn join_empty() { let ( expected, paths ) : ( PathBuf, Vec< PathBuf > ) = ( "".into(), vec![ "".into() ] ); - let result = the_module::path::join_paths( paths.iter().map( |p| p.as_path() ) ); + let result = the_module::path::iter_join( paths.iter().map( |p| p.as_path() ) ); assert_eq! ( result, @@ -21,7 +21,7 @@ fn join_empty() fn join_several_empties() { let ( expected, paths ) : ( PathBuf, Vec< PathBuf > ) = ( "".into(), vec![ "".into(), "".into() ] ); - let result = the_module::path::join_paths( paths.iter().map( |p| p.as_path() ) ); + let result = the_module::path::iter_join( paths.iter().map( |p| p.as_path() ) ); assert_eq! ( result, @@ -37,7 +37,7 @@ fn join_several_empties() fn root_with_absolute() { let ( expected, paths ) : ( PathBuf, Vec< PathBuf > ) = ( "/a/b".into(), vec![ "/".into(), "/a/b".into() ] ); - let result = the_module::path::join_paths( paths.iter().map( |p| p.as_path() ) ); + let result = the_module::path::iter_join( paths.iter().map( |p| p.as_path() ) ); assert_eq! ( result, @@ -53,7 +53,7 @@ fn root_with_absolute() fn root_with_relative() { let ( expected, paths ) : ( PathBuf, Vec< PathBuf > ) = ( "/a/b".into(), vec![ "/".into(), "a/b".into() ] ); - let result = the_module::path::join_paths( paths.iter().map( |p| p.as_path() ) ); + let result = the_module::path::iter_join( paths.iter().map( |p| p.as_path() ) ); assert_eq! ( result, @@ -69,7 +69,7 @@ fn root_with_relative() fn dir_with_absolute() { let ( expected, paths ) : ( PathBuf, Vec< PathBuf > ) = ( "/a/b".into(), vec![ "/dir".into(), "/a/b".into() ] ); - let result = the_module::path::join_paths( paths.iter().map( |p| p.as_path() ) ); + let result = the_module::path::iter_join( paths.iter().map( |p| p.as_path() ) ); assert_eq! ( result, @@ -85,7 +85,7 @@ fn dir_with_absolute() fn dir_with_relative() { let ( expected, paths ) : ( PathBuf, Vec< PathBuf > ) = ( "/dir/a/b".into(), vec![ "/dir".into(), "a/b".into() ] ); - let result = the_module::path::join_paths( paths.iter().map( |p| p.as_path() ) ); + let result = the_module::path::iter_join( paths.iter().map( |p| p.as_path() ) ); assert_eq! ( result, @@ -101,7 +101,7 @@ fn dir_with_relative() fn trailed_dir_with_absolute() { let ( expected, paths ) : ( PathBuf, Vec< PathBuf > ) = ( "/a/b".into(), vec![ "/dir/".into(), "/a/b".into() ] ); - let result = the_module::path::join_paths( paths.iter().map( |p| p.as_path() ) ); + let result = the_module::path::iter_join( paths.iter().map( |p| p.as_path() ) ); assert_eq! ( result, @@ -117,7 +117,7 @@ fn trailed_dir_with_absolute() fn trailed_dir_with_relative() { let ( expected, paths ) : ( PathBuf, Vec< PathBuf > ) = ( "/dir/a/b".into(), vec![ "/dir/".into(), "a/b".into() ] ); - let result = the_module::path::join_paths( paths.iter().map( |p| p.as_path() ) ); + let result = the_module::path::iter_join( paths.iter().map( |p| p.as_path() ) ); assert_eq! ( result, @@ -133,7 +133,7 @@ fn trailed_dir_with_relative() fn dir_with_down() { let ( expected, paths ) : ( PathBuf, Vec< PathBuf > ) = ( "/a/b".into(), vec![ "/dir".into(), "../a/b".into() ] ); - let result = the_module::path::join_paths( paths.iter().map( |p| p.as_path() ) ); + let result = the_module::path::iter_join( paths.iter().map( |p| p.as_path() ) ); assert_eq! ( result, @@ -149,7 +149,7 @@ fn dir_with_down() fn trailed_dir_with_down() { let ( expected, paths ) : ( PathBuf, Vec< PathBuf > ) = ( "/dir/a/b".into(), vec![ "/dir/".into(), "../a/b".into() ] ); - let result = the_module::path::join_paths( paths.iter().map( |p| p.as_path() ) ); + let result = the_module::path::iter_join( paths.iter().map( |p| p.as_path() ) ); assert_eq! ( result, @@ -165,7 +165,7 @@ fn trailed_dir_with_down() fn dir_with_several_down() { let ( expected, paths ) : ( PathBuf, Vec< PathBuf > ) = ( "/a/b".into(), vec![ "/dir/dir2".into(), "../../a/b".into() ] ); - let result = the_module::path::join_paths( paths.iter().map( |p| p.as_path() ) ); + let result = the_module::path::iter_join( paths.iter().map( |p| p.as_path() ) ); assert_eq! ( result, @@ -181,7 +181,7 @@ fn dir_with_several_down() fn trailed_dir_with_several_down() { let ( expected, paths ) : ( PathBuf, Vec< PathBuf > ) = ( "/a/b".into(), vec![ "/dir/".into(), "../../a/b".into() ] ); - let result = the_module::path::join_paths( paths.iter().map( |p| p.as_path() ) ); + let result = the_module::path::iter_join( paths.iter().map( |p| p.as_path() ) ); assert_eq! ( result, @@ -197,7 +197,7 @@ fn trailed_dir_with_several_down() fn dir_with_several_down_go_out_of_root() { let ( expected, paths ) : ( PathBuf, Vec< PathBuf > ) = ( "/../a/b".into(), vec![ "/dir".into(), "../../a/b".into() ] ); - let result = the_module::path::join_paths( paths.iter().map( |p| p.as_path() ) ); + let result = the_module::path::iter_join( paths.iter().map( |p| p.as_path() ) ); assert_eq! ( result, @@ -213,7 +213,7 @@ fn dir_with_several_down_go_out_of_root() fn trailed_absolute_with_trailed_down() { let ( expected, paths ) : ( PathBuf, Vec< PathBuf > ) = ( "/a/b/".into(), vec![ "/a/b/".into(), "../".into() ] ); - let result = the_module::path::join_paths( paths.iter().map( |p| p.as_path() ) ); + let result = the_module::path::iter_join( paths.iter().map( |p| p.as_path() ) ); assert_eq! ( result, @@ -229,7 +229,7 @@ fn trailed_absolute_with_trailed_down() fn absolute_with_trailed_down() { let ( expected, paths ) : ( PathBuf, Vec< PathBuf > ) = ( "/a/".into(), vec![ "/a/b".into(), "../".into() ] ); - let result = the_module::path::join_paths( paths.iter().map( |p| p.as_path() ) ); + let result = the_module::path::iter_join( paths.iter().map( |p| p.as_path() ) ); assert_eq! ( result, @@ -245,7 +245,7 @@ fn absolute_with_trailed_down() fn trailed_absolute_with_down() { let ( expected, paths ) : ( PathBuf, Vec< PathBuf > ) = ( "/a/b".into(), vec![ "/a/b/".into(), "..".into() ] ); - let result = the_module::path::join_paths( paths.iter().map( |p| p.as_path() ) ); + let result = the_module::path::iter_join( paths.iter().map( |p| p.as_path() ) ); assert_eq! ( result, @@ -261,7 +261,7 @@ fn trailed_absolute_with_down() fn trailed_absolute_with_trailed_here() { let ( expected, paths ) : ( PathBuf, Vec< PathBuf > ) = ( "/a/b/".into(), vec![ "/a/b/".into(), "./".into() ] ); - let result = the_module::path::join_paths( paths.iter().map( |p| p.as_path() ) ); + let result = the_module::path::iter_join( paths.iter().map( |p| p.as_path() ) ); assert_eq! ( result, @@ -277,7 +277,7 @@ fn trailed_absolute_with_trailed_here() fn absolute_with_trailed_here() { let ( expected, paths ) : ( PathBuf, Vec< PathBuf > ) = ( "/a/b/".into(), vec![ "/a/b".into(), "./".into() ] ); - let result = the_module::path::join_paths( paths.iter().map( |p| p.as_path() ) ); + let result = the_module::path::iter_join( paths.iter().map( |p| p.as_path() ) ); assert_eq! ( result, @@ -293,7 +293,7 @@ fn absolute_with_trailed_here() fn trailed_absolute_with_here() { let ( expected, paths ) : ( PathBuf, Vec< PathBuf > ) = ( "/a/b".into(), vec![ "/a/b/".into(), ".".into() ] ); - let result = the_module::path::join_paths( paths.iter().map( |p| p.as_path() ) ); + let result = the_module::path::iter_join( paths.iter().map( |p| p.as_path() ) ); assert_eq! ( result, @@ -309,7 +309,7 @@ fn trailed_absolute_with_here() fn join_with_empty() { let ( expected, paths ) : ( PathBuf, Vec< PathBuf > ) = ( "/a/b/c".into(), vec![ "".into(), "a/b".into(), "".into(), "c".into(), "".into() ] ); - let result = the_module::path::join_paths( paths.iter().map( |p| p.as_path() ) ); + let result = the_module::path::iter_join( paths.iter().map( |p| p.as_path() ) ); assert_eq! ( result, @@ -324,8 +324,8 @@ fn join_with_empty() #[ test ] fn join_windows_os_paths() { - let ( expected, paths ) : ( PathBuf, Vec< PathBuf > ) = ( "/c/foo/bar/".into(), vec![ "c:\\".into(), "foo\\".into(), "bar\\".into() ] ); - let result = the_module::path::join_paths( paths.iter().map( |p| p.as_path() ) ); + let ( expected, paths ) : ( PathBuf, Vec< PathBuf > ) = ( "/c:/foo/bar/".into(), vec![ "c:\\".into(), "foo\\".into(), "bar\\".into() ] ); + let result = the_module::path::iter_join( paths.iter().map( |p| p.as_path() ) ); assert_eq! ( result, @@ -341,7 +341,7 @@ fn join_windows_os_paths() fn join_unix_os_paths() { let ( expected, paths ) : ( PathBuf, Vec< PathBuf > ) = ( "/baz/foo".into(), vec![ "/bar/".into(), "/baz".into(), "foo/".into(), ".".into() ] ); - let result = the_module::path::join_paths( paths.iter().map( |p| p.as_path() ) ); + let result = the_module::path::iter_join( paths.iter().map( |p| p.as_path() ) ); assert_eq! ( result, @@ -357,7 +357,7 @@ fn join_unix_os_paths() fn join_unix_os_paths_2() { let ( expected, paths ) : ( PathBuf, Vec< PathBuf > ) = ( "/baz/foo/z".into(), vec![ "/bar/".into(), "/baz".into(), "foo/".into(), ".".into(), "z".into() ] ); - let result = the_module::path::join_paths( paths.iter().map( |p| p.as_path() ) ); + let result = the_module::path::iter_join( paths.iter().map( |p| p.as_path() ) ); assert_eq! ( result, @@ -373,7 +373,7 @@ fn join_unix_os_paths_2() fn more_complicated_cases_1() { let ( expected, paths ) : ( PathBuf, Vec< PathBuf > ) = ( "/aa/bb//cc".into(), vec![ "/aa".into(), "bb//".into(), "cc".into() ] ); - let result = the_module::path::join_paths( paths.iter().map( |p| p.as_path() ) ); + let result = the_module::path::iter_join( paths.iter().map( |p| p.as_path() ) ); assert_eq! ( result, @@ -389,7 +389,7 @@ fn more_complicated_cases_1() fn more_complicated_cases_2() { let ( expected, paths ) : ( PathBuf, Vec< PathBuf > ) = ( "/bb/cc".into(), vec![ "/aa".into(), "/bb".into(), "cc".into() ] ); - let result = the_module::path::join_paths( paths.iter().map( |p| p.as_path() ) ); + let result = the_module::path::iter_join( paths.iter().map( |p| p.as_path() ) ); assert_eq! ( result, @@ -405,7 +405,7 @@ fn more_complicated_cases_2() fn more_complicated_cases_3() { let ( expected, paths ) : ( PathBuf, Vec< PathBuf > ) = ( "//aa/bb//cc//".into(), vec![ "//aa".into(), "bb//".into(), "cc//".into() ] ); - let result = the_module::path::join_paths( paths.iter().map( |p| p.as_path() ) ); + let result = the_module::path::iter_join( paths.iter().map( |p| p.as_path() ) ); assert_eq! ( result, @@ -421,7 +421,7 @@ fn more_complicated_cases_3() fn more_complicated_cases_4() { let ( expected, paths ) : ( PathBuf, Vec< PathBuf > ) = ( "/aa/bb//cc".into(), vec![ "/aa".into(), "bb//".into(), "cc".into(), ".".into() ] ); - let result = the_module::path::join_paths( paths.iter().map( |p| p.as_path() ) ); + let result = the_module::path::iter_join( paths.iter().map( |p| p.as_path() ) ); assert_eq! ( result, @@ -437,7 +437,7 @@ fn more_complicated_cases_4() fn more_complicated_cases_5() { let ( expected, paths ) : ( PathBuf, Vec< PathBuf > ) = ( "//b//d/..e".into(), vec![ "/".into(), "a".into(), "//b//".into(), "././c".into(), "../d".into(), "..e".into() ] ); - let result = the_module::path::join_paths( paths.iter().map( |p| p.as_path() ) ); + let result = the_module::path::iter_join( paths.iter().map( |p| p.as_path() ) ); assert_eq! ( result, diff --git a/module/core/pth/tests/inc/path_join_trait_test.rs b/module/core/pth/tests/inc/path_join_trait_test.rs new file mode 100644 index 0000000000..74f302166b --- /dev/null +++ b/module/core/pth/tests/inc/path_join_trait_test.rs @@ -0,0 +1,206 @@ +use super::*; +use std:: +{ + borrow::Cow, + io, + path::{ Path, PathBuf }, +}; + +#[ test ] +fn basic() -> Result< (), io::Error > +{ + use the_module::PathJoined; + use std::path::PathBuf; + + let path1 : &str = "/some"; + let path2 : String = "path".into(); + let path3 : PathBuf = "to/file".into(); + let path4 : &str = "extra"; + let path5 : String = "components".into(); + + // Test with a tuple of length 1 + let joined1 : PathBuf = ( path1, ).iter_join()?; + println!( "Joined PathBuf (1): {:?}", joined1 ); + + // Test with a tuple of length 2 + let joined2 : PathBuf = ( path1, path2.clone() ).iter_join()?; + println!( "Joined PathBuf (2): {:?}", joined2 ); + + // Test with a tuple of length 3 + let joined3 : PathBuf = ( path1, path2.clone(), path3.clone() ).iter_join()?; + println!( "Joined PathBuf (3): {:?}", joined3 ); + + // Test with a tuple of length 4 + let joined4 : PathBuf = ( path1, path2.clone(), path3.clone(), path4 ).iter_join()?; + println!( "Joined PathBuf (4): {:?}", joined4 ); + + // Test with a tuple of length 5 + let joined5 : PathBuf = ( path1, path2, path3, path4, path5 ).iter_join()?; + println!( "Joined PathBuf (5): {:?}", joined5 ); + + Ok( () ) +} + +#[ test ] +fn array_join_paths_test() -> Result< (), io::Error > +{ + use the_module::{ PathJoined, TryIntoCowPath }; + use std::path::PathBuf; + + // Define a slice of path components + let path_components : [ &str; 3 ] = [ "/some", "path", "to/file" ]; + // Join the path components into a PathBuf + let joined : PathBuf = path_components.iter_join()?; + println!( "Joined PathBuf from slice: {:?}", joined ); + let expected = PathBuf::from( "/some/path/to/file" ); + assert_eq!( joined, expected ); + + Ok( () ) +} + +#[ test ] +fn slice_join_paths_test() -> Result< (), io::Error > +{ + use the_module::{ PathJoined, TryIntoCowPath }; + use std::path::PathBuf; + + // Define a slice of path components + let path_components : [ &str; 3 ] = [ "/some", "path", "to/file" ]; + let slice : &[ &str ] = &path_components[ .. ]; + // Join the path components into a PathBuf + let joined : PathBuf = slice.iter_join()?; + println!( "Joined PathBuf from slice: {:?}", joined ); + let expected = PathBuf::from( "/some/path/to/file" ); + assert_eq!( joined, expected ); + + Ok( () ) +} + +#[ test ] +fn all_types() -> Result< (), io::Error > +{ + use std::path::Path; + use the_module::{ AbsolutePath, CanonicalPath, NativePath, CurrentPath }; + use the_module::{ PathJoined, AsPath, TryIntoPath }; + + // AbsolutePath and CurrentPath + { + let absolute_path = AbsolutePath::try_from( "/absolute/path" ).unwrap(); + let current_path = CurrentPath; + let joined = ( absolute_path.clone(), current_path ).iter_join()?; + let expected = current_path.try_into_path()?; + println!( "Joined PathBuf: {:?}", joined ); + assert_eq!( joined, expected ); + } + + // // CurrentPath and AbsolutePath + // { + // let absolute_path = AbsolutePath::try_from( "/absolute/path" ).unwrap(); + // let current_path = CurrentPath; + // let joined = ( current_path, absolute_path.clone() ).iter_join()?; + // let expected = absolute_path.as_path().to_path_buf(); + // println!( "Joined PathBuf: {:?}", joined ); + // assert_eq!( joined, expected ); + // } + // // qqq : qqq2 : for Denys : bad + + // AbsolutePath and Component + { + let absolute_path = AbsolutePath::try_from( "/absolute/path" ).unwrap(); + let component = Path::new( "/component/path" ).components().next().unwrap(); + println!( "component : {component:?}" ); + let joined = ( absolute_path, component ).iter_join()?; + let expected = component.as_path(); + println!( "Joined PathBuf: {:?}", joined ); + assert_eq!( joined, expected ); + } + + // AbsolutePath and &str + { + let absolute_path = AbsolutePath::try_from( "/absolute/path" ).unwrap(); + let path_str : &str = "additional/str"; + let joined = ( absolute_path, path_str ).iter_join()?; + let expected = PathBuf::from( "/absolute/path/additional/str" ); + println!( "Joined PathBuf: {:?}", joined ); + assert_eq!( joined, expected ); + } + + // AbsolutePath and NativePath + { + let absolute_path = AbsolutePath::try_from( "/absolute/path" ).unwrap(); + let native_path = NativePath::try_from( PathBuf::from( "/native/path" ) ).unwrap(); + let joined = ( absolute_path, native_path ).iter_join()?; + let expected = PathBuf::from( "/native/path" ); + println!( "Joined PathBuf: {:?}", joined ); + assert_eq!( joined, expected ); + } + + // AbsolutePath and CanonicalPath + { + let absolute_path = AbsolutePath::try_from( "/absolute/path" ).unwrap(); + let canonical_path = CanonicalPath::try_from( "/canonical/path" ).unwrap(); + let joined = ( absolute_path, canonical_path ).iter_join()?; + let expected = PathBuf::from( "/canonical/path" ); + println!( "Joined PathBuf: {:?}", joined ); + assert_eq!( joined, expected ); + } + + // NativePath and CurrentPath + { + let native_path = NativePath::try_from( PathBuf::from( "/native/path" ) ).unwrap(); + let current_path = CurrentPath; + let joined = ( native_path, current_path ).iter_join()?; + let expected = current_path.try_into_path()?; + println!( "Joined PathBuf: {:?}", joined ); + assert_eq!( joined, expected ); + } + + // CanonicalPath and Component + { + let canonical_path = CanonicalPath::try_from( "/canonical/path" ).unwrap(); + let component = Path::new( "/component/path" ).components().next().unwrap(); + println!( "component : {component:?}" ); + let joined = ( canonical_path, component ).iter_join()?; + let expected = component.as_path(); + // let expected = PathBuf::from( "/canonical/component" ); + println!( "Joined PathBuf: {:?}", joined ); + assert_eq!( joined, expected ); + } + + Ok( () ) +} + +#[ test ] +fn join_function_test() -> Result< (), io::Error > +{ + use the_module::path; + use std::path::PathBuf; + + // Test joining a tuple of path components + let path1 : &str = "/some"; + let path2 : String = "path".into(); + let path3 : PathBuf = "to/file".into(); + + // Use the join function to join the path components + let joined : PathBuf = path::join( ( path1, path2.clone(), path3.clone() ) )?; + println!( "Joined PathBuf: {:?}", joined ); + // Verify the expected outcome + let expected = PathBuf::from( "/some/path/to/file" ); + assert_eq!( joined, expected ); + + // Test joining a tuple of length 2 + let joined : PathBuf = path::join( ( path1, path2.clone() ) )?; + println!( "Joined PathBuf (2 components): {:?}", joined ); + // Verify the expected outcome + let expected = PathBuf::from( "/some/path" ); + assert_eq!( joined, expected ); + + // Test joining a tuple of length 1 + let joined : PathBuf = path::join( ( path1, ) )?; + println!( "Joined PathBuf (1 component): {:?}", joined ); + // Verify the expected outcome + let expected = PathBuf::from( "/some" ); + assert_eq!( joined, expected ); + + Ok( () ) +} \ No newline at end of file diff --git a/module/core/proper_path_tools/tests/inc/path_normalize.rs b/module/core/pth/tests/inc/path_normalize.rs similarity index 100% rename from module/core/proper_path_tools/tests/inc/path_normalize.rs rename to module/core/pth/tests/inc/path_normalize.rs diff --git a/module/core/proper_path_tools/tests/inc/path_relative.rs b/module/core/pth/tests/inc/path_relative.rs similarity index 100% rename from module/core/proper_path_tools/tests/inc/path_relative.rs rename to module/core/pth/tests/inc/path_relative.rs diff --git a/module/core/proper_path_tools/tests/inc/path_unique_folder_name.rs b/module/core/pth/tests/inc/path_unique_folder_name.rs similarity index 100% rename from module/core/proper_path_tools/tests/inc/path_unique_folder_name.rs rename to module/core/pth/tests/inc/path_unique_folder_name.rs diff --git a/module/core/proper_path_tools/tests/inc/rebase_path.rs b/module/core/pth/tests/inc/rebase_path.rs similarity index 100% rename from module/core/proper_path_tools/tests/inc/rebase_path.rs rename to module/core/pth/tests/inc/rebase_path.rs diff --git a/module/core/proper_path_tools/tests/inc/transitive.rs b/module/core/pth/tests/inc/transitive.rs similarity index 96% rename from module/core/proper_path_tools/tests/inc/transitive.rs rename to module/core/pth/tests/inc/transitive.rs index e0b2da7acc..8224024e5b 100644 --- a/module/core/proper_path_tools/tests/inc/transitive.rs +++ b/module/core/pth/tests/inc/transitive.rs @@ -4,7 +4,7 @@ use super::*; #[ test ] fn basic_from() { - use proper_path_tools::TransitiveTryFrom; + use pth::TransitiveTryFrom; use std::convert::TryFrom; struct InitialType; @@ -42,7 +42,7 @@ fn basic_from() #[ test ] fn test_transitive_try_into() { - use proper_path_tools::TransitiveTryInto; + use pth::TransitiveTryInto; // Define NewType1 wrapping a String #[ derive( Debug, PartialEq ) ] diff --git a/module/core/pth/tests/inc/try_into_cow_path_test.rs b/module/core/pth/tests/inc/try_into_cow_path_test.rs new file mode 100644 index 0000000000..73a3910c52 --- /dev/null +++ b/module/core/pth/tests/inc/try_into_cow_path_test.rs @@ -0,0 +1,124 @@ +use super::*; + +#[ test ] +fn try_into_cow_path_test() +{ + use std:: + { + borrow::Cow, + path::{ Component, Path, PathBuf }, + }; + #[ cfg( feature = "path_utf8" ) ] + use the_module::{ Utf8Path, Utf8PathBuf }; + use the_module:: + { + TryIntoCowPath, AbsolutePath, CanonicalPath, NativePath, CurrentPath, + }; + + // Test with &str + let path_str : &str = "/some/path"; + let cow_path : Cow< '_ , Path > = TryIntoCowPath::try_into_cow_path( path_str ).unwrap(); + println!( "Cow from &str: {:?}", cow_path ); + + // Test with &String + let string_path : String = String::from( "/another/path" ); + let cow_path : Cow< '_ , Path > = TryIntoCowPath::try_into_cow_path( &string_path ).unwrap(); + println!( "Cow from &String: {:?}", cow_path ); + + // Test with String + let cow_path : Cow< '_ , Path > = TryIntoCowPath::try_into_cow_path( string_path.clone() ).unwrap(); + println!( "Cow from String: {:?}", cow_path ); + + // Test with &Path + let path : &Path = Path::new( "/yet/another/path" ); + let cow_path : Cow< '_ , Path > = TryIntoCowPath::try_into_cow_path( path ).unwrap(); + println!( "Cow from &Path: {:?}", cow_path ); + + // Test with &PathBuf + let path_buf : PathBuf = PathBuf::from( "/yet/another/path" ); + let cow_path : Cow< '_ , Path > = TryIntoCowPath::try_into_cow_path( &path_buf ).unwrap(); + println!( "Cow from &PathBuf: {:?}", cow_path ); + + // Test with PathBuf + let cow_path : Cow< '_ , Path > = TryIntoCowPath::try_into_cow_path( path_buf.clone() ).unwrap(); + println!( "Cow from PathBuf: {:?}", cow_path ); + + // Test with &AbsolutePath + let absolute_path : AbsolutePath = AbsolutePath::try_from( "/absolute/path" ).unwrap(); + let cow_path : Cow< '_ , Path > = TryIntoCowPath::try_into_cow_path( &absolute_path ).unwrap(); + println!( "Cow from &AbsolutePath: {:?}", cow_path ); + + // Test with AbsolutePath + let cow_path : Cow< '_ , Path > = TryIntoCowPath::try_into_cow_path( absolute_path.clone() ).unwrap(); + println!( "Cow from AbsolutePath: {:?}", cow_path ); + + // Test with &CanonicalPath + let canonical_path = CanonicalPath::try_from( "/canonical/path" ).unwrap(); + let cow_path : Cow< '_ , Path > = TryIntoCowPath::try_into_cow_path( &canonical_path ).unwrap(); + println!( "Cow from &CanonicalPath: {:?}", cow_path ); + + // Test with CanonicalPath + let cow_path : Cow< '_ , Path > = TryIntoCowPath::try_into_cow_path( canonical_path.clone() ).unwrap(); + println!( "Cow from CanonicalPath: {:?}", cow_path ); + + // Test with &NativePath + let native_path = NativePath::try_from( PathBuf::from( "/native/path" ) ).unwrap(); + let cow_path : Cow< '_ , Path > = TryIntoCowPath::try_into_cow_path( &native_path ).unwrap(); + println!( "Cow from &NativePath: {:?}", cow_path ); + + // Test with NativePath + let cow_path : Cow< '_ , Path > = TryIntoCowPath::try_into_cow_path( native_path.clone() ).unwrap(); + println!( "Cow from NativePath: {:?}", cow_path ); + + // Test with &CurrentPath + let current_path = CurrentPath; + let cow_path : Cow< '_ , Path > = TryIntoCowPath::try_into_cow_path( ¤t_path ).unwrap(); + println!( "Cow from &CurrentPath: {:?}", cow_path ); + assert!( cow_path.to_string_lossy().len() > 1 ); + + // Test with CurrentPath + let cow_path : Cow< '_ , Path > = TryIntoCowPath::try_into_cow_path( current_path ).unwrap(); + println!( "Cow from CurrentPath: {:?}", cow_path ); + assert!( cow_path.to_string_lossy().len() > 1 ); + + // Test with &Component + let root_component : Component< '_ > = Component::RootDir; + let cow_path : Cow< '_ , Path > = TryIntoCowPath::try_into_cow_path( &root_component ).unwrap(); + println!( "Cow from &Component: {:?}", cow_path ); + assert!( cow_path.to_string_lossy().len() >= 1 ); + + // Test with Component + let cow_path : Cow< '_ , Path > = TryIntoCowPath::try_into_cow_path( root_component ).unwrap(); + println!( "Cow from Component: {:?}", cow_path ); + assert!( cow_path.to_string_lossy().len() >= 1 ); + + // Test with Component + let path = Path::new( "/component/path" ); + for component in path.components() + { + let cow_path : Cow< '_ , Path > = TryIntoCowPath::try_into_cow_path( component ).unwrap(); + println!( "Cow from Component: {:?}", cow_path ); + assert!( cow_path.to_string_lossy().len() >= 1 ); + } + + #[ cfg( feature = "path_utf8" ) ] + { + // Test with &Utf8Path + let utf8_path = Utf8Path::new( "/utf8/path" ); + let cow_path : Cow< '_ , Path > = TryIntoCowPath::try_into_cow_path( &utf8_path ).unwrap(); + println!( "Cow from &Utf8Path: {:?}", cow_path ); + + // Test with Utf8Path + let cow_path : Cow< '_ , Path > = TryIntoCowPath::try_into_cow_path( utf8_path ).unwrap(); + println!( "Cow from Utf8Path: {:?}", cow_path ); + + // Test with &Utf8PathBuf + let utf8_path_buf = Utf8PathBuf::from( "/utf8/pathbuf" ); + let cow_path : Cow< '_ , Path > = TryIntoCowPath::try_into_cow_path( &utf8_path_buf ).unwrap(); + println!( "Cow from &Utf8PathBuf: {:?}", cow_path ); + + // Test with Utf8PathBuf + let cow_path : Cow< '_ , Path > = TryIntoCowPath::try_into_cow_path( utf8_path_buf.clone() ).unwrap(); + println!( "Cow from Utf8PathBuf: {:?}", cow_path ); + } +} diff --git a/module/core/pth/tests/inc/try_into_path_test.rs b/module/core/pth/tests/inc/try_into_path_test.rs new file mode 100644 index 0000000000..b7623d5c60 --- /dev/null +++ b/module/core/pth/tests/inc/try_into_path_test.rs @@ -0,0 +1,117 @@ +use super::*; + +#[ test ] +fn try_into_path_test() +{ + use std::path::{ Component, Path, PathBuf }; + #[ cfg( feature = "path_utf8" ) ] + use the_module::{ Utf8Path, Utf8PathBuf }; + use the_module::{ TryIntoPath, AbsolutePath, CanonicalPath, NativePath, CurrentPath }; + + // Test with &str + let path_str : &str = "/some/path"; + let path_buf : PathBuf = TryIntoPath::try_into_path( path_str ).unwrap(); + println!( "PathBuf from &str: {:?}", path_buf ); + + // Test with &String + let string_path : String = String::from( "/another/path" ); + let path_buf : PathBuf = TryIntoPath::try_into_path( &string_path ).unwrap(); + println!( "PathBuf from &String: {:?}", path_buf ); + + // Test with String + let path_buf : PathBuf = TryIntoPath::try_into_path( string_path.clone() ).unwrap(); + println!( "PathBuf from String: {:?}", path_buf ); + + // Test with &Path + let path : &Path = Path::new( "/yet/another/path" ); + let path_buf : PathBuf = TryIntoPath::try_into_path( path ).unwrap(); + println!( "PathBuf from &Path: {:?}", path_buf ); + + // Test with &PathBuf + let path_buf_instance : PathBuf = PathBuf::from( "/yet/another/path" ); + let path_buf : PathBuf = TryIntoPath::try_into_path( &path_buf_instance ).unwrap(); + println!( "PathBuf from &PathBuf: {:?}", path_buf ); + + // Test with PathBuf + let path_buf : PathBuf = TryIntoPath::try_into_path( path_buf_instance.clone() ).unwrap(); + println!( "PathBuf from PathBuf: {:?}", path_buf ); + + // Test with &AbsolutePath + let absolute_path : AbsolutePath = AbsolutePath::try_from( "/absolute/path" ).unwrap(); + let path_buf : PathBuf = TryIntoPath::try_into_path( &absolute_path ).unwrap(); + println!( "PathBuf from &AbsolutePath: {:?}", path_buf ); + + // Test with AbsolutePath + let path_buf : PathBuf = TryIntoPath::try_into_path( absolute_path.clone() ).unwrap(); + println!( "PathBuf from AbsolutePath: {:?}", path_buf ); + + // Test with &CanonicalPath + let canonical_path = CanonicalPath::try_from( "/canonical/path" ).unwrap(); + let path_buf : PathBuf = TryIntoPath::try_into_path( &canonical_path ).unwrap(); + println!( "PathBuf from &CanonicalPath: {:?}", path_buf ); + + // Test with CanonicalPath + let path_buf : PathBuf = TryIntoPath::try_into_path( canonical_path.clone() ).unwrap(); + println!( "PathBuf from CanonicalPath: {:?}", path_buf ); + + // Test with &NativePath + let native_path = NativePath::try_from( PathBuf::from( "/native/path" ) ).unwrap(); + let path_buf : PathBuf = TryIntoPath::try_into_path( &native_path ).unwrap(); + println!( "PathBuf from &NativePath: {:?}", path_buf ); + + // Test with NativePath + let path_buf : PathBuf = TryIntoPath::try_into_path( native_path.clone() ).unwrap(); + println!( "PathBuf from NativePath: {:?}", path_buf ); + + // Test with &CurrentPath + let current_path = CurrentPath; + let path_buf : PathBuf = TryIntoPath::try_into_path( ¤t_path ).unwrap(); + println!( "PathBuf from &CurrentPath: {:?}", path_buf ); + assert!( path_buf.to_string_lossy().len() > 1 ); + + // Test with CurrentPath + let path_buf : PathBuf = TryIntoPath::try_into_path( current_path ).unwrap(); + println!( "PathBuf from CurrentPath: {:?}", path_buf ); + assert!( path_buf.to_string_lossy().len() > 1 ); + + // Test with &Component + let root_component : Component< '_ > = Component::RootDir; + let path_buf : PathBuf = TryIntoPath::try_into_path( &root_component ).unwrap(); + println!( "PathBuf from &Component: {:?}", path_buf ); + assert!( path_buf.to_string_lossy().len() >= 1 ); + + // Test with Component + let path_buf : PathBuf = TryIntoPath::try_into_path( root_component ).unwrap(); + println!( "PathBuf from Component: {:?}", path_buf ); + assert!( path_buf.to_string_lossy().len() >= 1 ); + + // Test with Component + let path = Path::new( "/component/path" ); + for component in path.components() + { + let path_buf : PathBuf = TryIntoPath::try_into_path( component ).unwrap(); + println!( "PathBuf from Component: {:?}", path_buf ); + assert!( path_buf.to_string_lossy().len() >= 1 ); + } + + #[ cfg( feature = "path_utf8" ) ] + { + // Test with &Utf8Path + let utf8_path = Utf8Path::new( "/utf8/path" ); + let path_buf : PathBuf = TryIntoPath::try_into_path( &utf8_path ).unwrap(); + println!( "PathBuf from &Utf8Path: {:?}", path_buf ); + + // Test with Utf8Path + let path_buf : PathBuf = TryIntoPath::try_into_path( utf8_path ).unwrap(); + println!( "PathBuf from Utf8Path: {:?}", path_buf ); + + // Test with &Utf8PathBuf + let utf8_path_buf = Utf8PathBuf::from( "/utf8/pathbuf" ); + let path_buf : PathBuf = TryIntoPath::try_into_path( &utf8_path_buf ).unwrap(); + println!( "PathBuf from &Utf8PathBuf: {:?}", path_buf ); + + // Test with Utf8PathBuf + let path_buf : PathBuf = TryIntoPath::try_into_path( utf8_path_buf.clone() ).unwrap(); + println!( "PathBuf from Utf8PathBuf: {:?}", path_buf ); + } +} diff --git a/module/core/proper_path_tools/tests/inc/without_ext.rs b/module/core/pth/tests/inc/without_ext.rs similarity index 100% rename from module/core/proper_path_tools/tests/inc/without_ext.rs rename to module/core/pth/tests/inc/without_ext.rs diff --git a/module/core/proper_path_tools/tests/smoke_test.rs b/module/core/pth/tests/smoke_test.rs similarity index 100% rename from module/core/proper_path_tools/tests/smoke_test.rs rename to module/core/pth/tests/smoke_test.rs diff --git a/module/core/pth/tests/tests.rs b/module/core/pth/tests/tests.rs new file mode 100644 index 0000000000..49fa343161 --- /dev/null +++ b/module/core/pth/tests/tests.rs @@ -0,0 +1,9 @@ +#![ allow( unused_imports ) ] + +include!( "../../../../module/step/meta/src/module/terminal.rs" ); + +use pth as the_module; +use test_tools::exposed::*; + +#[ cfg( feature = "enabled" ) ] +mod inc; diff --git a/module/core/strs_tools/Cargo.toml b/module/core/strs_tools/Cargo.toml index 77dd2eeab5..37b6231be0 100644 --- a/module/core/strs_tools/Cargo.toml +++ b/module/core/strs_tools/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "strs_tools" -version = "0.17.0" +version = "0.18.0" edition = "2021" authors = [ "Kostiantyn Wandalen ", diff --git a/module/core/test_tools/Cargo.toml b/module/core/test_tools/Cargo.toml index 2df25d0312..6d30222997 100644 --- a/module/core/test_tools/Cargo.toml +++ b/module/core/test_tools/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "test_tools" -version = "0.10.0" +version = "0.11.0" edition = "2021" authors = [ "Kostiantyn Wandalen ", diff --git a/module/core/test_tools/src/lib.rs b/module/core/test_tools/src/lib.rs index cbac84274c..f9870e775e 100644 --- a/module/core/test_tools/src/lib.rs +++ b/module/core/test_tools/src/lib.rs @@ -43,9 +43,10 @@ pub mod dependency #[ doc( inline ) ] #[ allow( unused_imports ) ] pub use ::process_tools; - #[ doc( inline ) ] - #[ allow( unused_imports ) ] - pub use ::process_tools as process_tools; + + // #[ doc( inline ) ] + // #[ allow( unused_imports ) ] + // pub use ::process_tools as process_tools; } diff --git a/module/core/typing_tools/Cargo.toml b/module/core/typing_tools/Cargo.toml index c61c60c278..2d75db2449 100644 --- a/module/core/typing_tools/Cargo.toml +++ b/module/core/typing_tools/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "typing_tools" -version = "0.9.0" +version = "0.10.0" edition = "2021" authors = [ "Kostiantyn Wandalen ", diff --git a/module/core/variadic_from/Cargo.toml b/module/core/variadic_from/Cargo.toml index 2a67bdda26..073a4f994a 100644 --- a/module/core/variadic_from/Cargo.toml +++ b/module/core/variadic_from/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "variadic_from" -version = "0.24.0" +version = "0.27.0" edition = "2021" authors = [ "Kostiantyn Wandalen ", diff --git a/module/core/wtools/Cargo.toml b/module/core/wtools/Cargo.toml index 8488ccd02b..4d2e2f1f29 100644 --- a/module/core/wtools/Cargo.toml +++ b/module/core/wtools/Cargo.toml @@ -318,7 +318,6 @@ dt_default = [ # "dt_use_std", "data_type/default", "dt_either", - "dt_prelude", # "dt_type_constructor", # "dt_make", # "dt_vectorized_from", @@ -329,7 +328,6 @@ dt_full = [ # "dt_use_std", "data_type/full", "dt_either", - "dt_prelude", # "dt_type_constructor", # "dt_make", # "dt_vectorized_from", @@ -341,7 +339,6 @@ dt_full = [ dt_use_alloc = [ "dt", "data_type/use_alloc" ] dt_either = [ "dt", "data_type/dt_either" ] -dt_prelude = [ "dt", "data_type/dt_prelude" ] # dt_type_constructor = [ "dt", "data_type/dt_type_constructor" ] # dt_make = [ "dt", "data_type/dt_make" ] # dt_vectorized_from = [ "dt", "data_type/dt_vectorized_from" ] diff --git a/module/move/crates_tools/Cargo.toml b/module/move/crates_tools/Cargo.toml index d36a25f270..3f14f8e209 100644 --- a/module/move/crates_tools/Cargo.toml +++ b/module/move/crates_tools/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "crates_tools" -version = "0.13.0" +version = "0.14.0" edition = "2021" authors = [ "Kostiantyn Wandalen ", diff --git a/module/move/unitore/Cargo.toml b/module/move/unitore/Cargo.toml index 8f98fba818..fa560e6cae 100644 --- a/module/move/unitore/Cargo.toml +++ b/module/move/unitore/Cargo.toml @@ -32,7 +32,7 @@ enabled = [] [dependencies] error_tools = { workspace = true, features = [ "default" ] } -proper_path_tools = { workspace = true, features = [ "default" ] } +pth = { workspace = true, features = [ "default" ] } tokio = { version = "1.36.0", features = [ "rt", "rt-multi-thread", "io-std", "macros" ] } hyper = { version = "1.1.0", features = [ "client" ] } hyper-tls = "0.6.0" diff --git a/module/move/unitore/src/action/config.rs b/module/move/unitore/src/action/config.rs index 9dfc356fe0..a2da010f41 100644 --- a/module/move/unitore/src/action/config.rs +++ b/module/move/unitore/src/action/config.rs @@ -16,7 +16,7 @@ use gluesql::{ prelude::Payload, sled_storage::SledStorage }; /// Add configuration file with subscriptions to storage. pub async fn config_add( mut storage : FeedStorage< SledStorage >, path : &PathBuf ) -> Result< impl Report > { - let path = proper_path_tools::path::normalize( path ); + let path = pth::path::normalize( path ); let mut err_str = format!( "Invalid path for config file {:?}", path ); @@ -63,7 +63,7 @@ pub async fn config_add( mut storage : FeedStorage< SledStorage >, path : &PathB /// Remove configuration file from storage. pub async fn config_delete( mut storage : FeedStorage< SledStorage >, path : &PathBuf ) -> Result< impl Report > { - let path = proper_path_tools::path::normalize( path ); + let path = pth::path::normalize( path ); let path = path.canonicalize().context( format!( "Invalid path for config file {:?}", path ) )?; let config = Config::new( path.to_string_lossy().to_string() ); diff --git a/module/move/unitore/src/entity/config.rs b/module/move/unitore/src/entity/config.rs index 92f9f550d6..536c1c5142 100644 --- a/module/move/unitore/src/entity/config.rs +++ b/module/move/unitore/src/entity/config.rs @@ -44,7 +44,7 @@ pub trait ConfigStore // qqq : use AbsolutePath newtype from `path_tools` // qqq : normalize all paths with `path_tools::path::normalize` -// https://docs.rs/proper_path_tools/latest/proper_path_tools/path/fn.normalize.html +// https://docs.rs/pth/latest/pth/path/fn.normalize.html // added path normalization // unitore .query.execute \'SELECT \* FROM feed\' diff --git a/module/move/unitore/tests/config_add.rs b/module/move/unitore/tests/config_add.rs index 7f080622b8..6673b0f608 100644 --- a/module/move/unitore/tests/config_add.rs +++ b/module/move/unitore/tests/config_add.rs @@ -12,7 +12,7 @@ use error_tools::untyped::Result; async fn config_add() -> Result< () > { let path = PathBuf::from( "./tests/fixtures/test_config.toml" ); - let temp_path = proper_path_tools::path::unique_folder_name().unwrap(); + let temp_path = pth::path::unique_folder_name().unwrap(); let config = Config::default() .path( format!( "./{}", temp_path ) ) diff --git a/module/move/unitore/tests/config_delete.rs b/module/move/unitore/tests/config_delete.rs index 9a7ffdf10a..c3393702cc 100644 --- a/module/move/unitore/tests/config_delete.rs +++ b/module/move/unitore/tests/config_delete.rs @@ -16,7 +16,7 @@ async fn config_delete() -> Result< () > { let path = std::path::PathBuf::from( "./tests/fixtures/test_config.toml" ); - let temp_path = proper_path_tools::path::unique_folder_name().unwrap(); + let temp_path = pth::path::unique_folder_name().unwrap(); let config = Config::default() .path( format!( "./{}", temp_path ) ) diff --git a/module/move/unitore/tests/frames_download.rs b/module/move/unitore/tests/frames_download.rs index 11494838f9..0b78b077d0 100644 --- a/module/move/unitore/tests/frames_download.rs +++ b/module/move/unitore/tests/frames_download.rs @@ -2,7 +2,7 @@ use feed_rs::parser as feed_parser; use gluesql:: { core:: - { + { chrono::{ DateTime, Utc }, data::Value }, @@ -20,7 +20,7 @@ use error_tools::untyped::Result; #[ tokio::test ] async fn test_save() -> Result< () > { - let temp_path = proper_path_tools::path::unique_folder_name().unwrap(); + let temp_path = pth::path::unique_folder_name().unwrap(); let config = Config::default() .path( format!( "./{}", temp_path ) ) @@ -52,7 +52,7 @@ async fn test_save() -> Result< () > #[ tokio::test ] async fn test_update() -> Result< () > { - let temp_path = proper_path_tools::path::unique_folder_name().unwrap(); + let temp_path = pth::path::unique_folder_name().unwrap(); let config = Config::default() .path( format!( "./{}", temp_path ) ) diff --git a/module/move/unitore/tests/query_execute.rs b/module/move/unitore/tests/query_execute.rs index 4215971781..0e47c9e576 100644 --- a/module/move/unitore/tests/query_execute.rs +++ b/module/move/unitore/tests/query_execute.rs @@ -41,14 +41,14 @@ fn query_execute() -> Result< () > assert!( res.is_ok() ); // test action - let rt = tokio::runtime::Runtime::new()?; + let rt = tokio::runtime::Runtime::new()?; let ca = CommandsAggregator::former() .command( "query.execute" ) .hint( "hint" ) .long_hint( "long_hint" ) .subject().hint( "SQL query" ).kind( Type::String ).optional( false ).end() .routine( move | o : VerifiedCommand | - { + { let mut f_store = MockStore::new(); f_store .expect_query_execute() @@ -62,19 +62,19 @@ fn query_execute() -> Result< () > ] ) ) ) - ; + ; _ = rt.block_on( async move { let query_arg = o.args .get_owned::< String >( 0 ) ; - + let query_str = query_arg.unwrap(); query::query_execute( f_store, query_str ).await - } ); + } ); } ) .end() - .perform(); + .perform(); let entries = ca.perform( vec![ ".query.execute".to_string(), "SELECT title FROM frame".into() ] ); assert!( entries.is_ok() ); Ok( () ) @@ -84,7 +84,7 @@ fn query_execute() -> Result< () > async fn query_feeds() -> Result< () > { let path = PathBuf::from( "./tests/fixtures/test_config.toml" ); - let temp_path = proper_path_tools::path::unique_folder_name().unwrap(); + let temp_path = pth::path::unique_folder_name().unwrap(); let config = sled::Config::default() .path( format!( "./{}", temp_path ) ) @@ -114,7 +114,7 @@ async fn query_feeds() -> Result< () > #[ tokio::test ] async fn query_frames() -> Result< () > { - let temp_path = proper_path_tools::path::unique_folder_name().unwrap(); + let temp_path = pth::path::unique_folder_name().unwrap(); let config = sled::Config::default() .path( format!( "./{}", temp_path ) ) @@ -160,7 +160,7 @@ async fn query_frames() -> Result< () > async fn query_configs() -> Result< () > { let path = PathBuf::from( "./tests/fixtures/test_config.toml" ); - let temp_path = proper_path_tools::path::unique_folder_name().unwrap(); + let temp_path = pth::path::unique_folder_name().unwrap(); let config = sled::Config::default() .path( format!( "./{}", temp_path ) ) @@ -182,6 +182,6 @@ async fn query_configs() -> Result< () > { assert!( false ); } - + Ok( () ) } diff --git a/module/move/unitore/tests/table_list.rs b/module/move/unitore/tests/table_list.rs index ff06deae00..88b16d519e 100644 --- a/module/move/unitore/tests/table_list.rs +++ b/module/move/unitore/tests/table_list.rs @@ -13,7 +13,7 @@ use error_tools::untyped::Result; #[ tokio::test ] async fn table_list() -> Result< () > { - let temp_path = proper_path_tools::path::unique_folder_name().unwrap(); + let temp_path = pth::path::unique_folder_name().unwrap(); let config = Config::default() .path( format!( "./{}", temp_path ) ) @@ -30,7 +30,7 @@ async fn table_list() -> Result< () > .map( | row | row[ 1 ].clone() ) .collect::< Vec< _ > >() ; - + assert_eq!( column_names.len(), 9 ); assert!( column_names.contains( &Str( String::from( "published") ) ) ); assert!( column_names.contains( &Str( String::from( "authors") ) ) ); diff --git a/module/move/unitore/tests/tables_list.rs b/module/move/unitore/tests/tables_list.rs index f740e94b08..7f5fbb57f7 100644 --- a/module/move/unitore/tests/tables_list.rs +++ b/module/move/unitore/tests/tables_list.rs @@ -9,7 +9,7 @@ use error_tools::untyped::Result; #[ tokio::test ] async fn tables_list() -> Result< () > { - let temp_path = proper_path_tools::path::unique_folder_name().unwrap(); + let temp_path = pth::path::unique_folder_name().unwrap(); let config = Config::default() .path( format!( "./{}", temp_path ) ) diff --git a/module/move/wca/Cargo.toml b/module/move/wca/Cargo.toml index ae882ea833..da8d1227b6 100644 --- a/module/move/wca/Cargo.toml +++ b/module/move/wca/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "wca" -version = "0.22.0" +version = "0.23.0" edition = "2021" authors = [ "Kostiantyn Wandalen ", diff --git a/module/move/willbe/Cargo.toml b/module/move/willbe/Cargo.toml index e7152f6149..4f16918129 100644 --- a/module/move/willbe/Cargo.toml +++ b/module/move/willbe/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "willbe" -version = "0.19.0" +version = "0.20.0" edition = "2021" authors = [ "Kostiantyn Wandalen ", @@ -41,7 +41,7 @@ enabled = [ "iter_tools/enabled", "mod_interface/enabled", "wca/enabled", - "proper_path_tools/enabled", + "pth/enabled", "process_tools/enabled", "derive_tools/enabled", "data_type/enabled", @@ -88,7 +88,7 @@ former = { workspace = true, features = [ "default" ] } iter_tools = { workspace = true, features = [ "default" ] } mod_interface = { workspace = true, features = [ "default" ] } wca = { workspace = true, features = [ "default" ] } -proper_path_tools = { workspace = true, features = [ "default", "path_utf8" ] } +pth = { workspace = true, features = [ "default", "path_utf8" ] } process_tools = { workspace = true, features = [ "default" ] } derive_tools = { workspace = true, features = [ "derive_display", "derive_from_str", "derive_deref", "derive_from", "derive_as_ref" ] } data_type = { workspace = true, features = [ "either" ] } diff --git a/module/move/willbe/src/action/readme_modules_headers_renew.rs b/module/move/willbe/src/action/readme_modules_headers_renew.rs index 5370a9d0fa..9b613d97fa 100644 --- a/module/move/willbe/src/action/readme_modules_headers_renew.rs +++ b/module/move/willbe/src/action/readme_modules_headers_renew.rs @@ -179,7 +179,7 @@ mod private &self.module_name ) { - let relative_path = proper_path_tools::path::path_relative + let relative_path = pth::path::path_relative ( workspace_path.try_into().unwrap(), name diff --git a/module/move/willbe/src/tool/path.rs b/module/move/willbe/src/tool/path.rs index c07f0b3d6e..028bbd4189 100644 --- a/module/move/willbe/src/tool/path.rs +++ b/module/move/willbe/src/tool/path.rs @@ -6,7 +6,7 @@ mod private crate::mod_interface! { - use ::proper_path_tools; - own use ::proper_path_tools::own::*; + use ::pth; + own use ::pth::own::*; } diff --git a/module/move/willbe/src/wtools.rs b/module/move/willbe/src/wtools.rs index 1735805c0b..4fe43d10e9 100644 --- a/module/move/willbe/src/wtools.rs +++ b/module/move/willbe/src/wtools.rs @@ -21,8 +21,8 @@ // /// Collection of function and structures to manipulate paths. // pub mod path_tools // { -// // pub use proper_path_tools::own::*; -// // pub use proper_path_tools::own::path; -// // zzz : make use proper_path_tools::own::path working +// // pub use pth::own::*; +// // pub use pth::own::path; +// // zzz : make use pth::own::path working // pub use proper_path::own as path; // } diff --git a/module/postponed/type_constructor/tests/inc/mod.rs b/module/postponed/type_constructor/tests/inc/mod.rs index 199b42411a..da4554b46e 100644 --- a/module/postponed/type_constructor/tests/inc/mod.rs +++ b/module/postponed/type_constructor/tests/inc/mod.rs @@ -8,8 +8,8 @@ use super::*; // mod type_constructor; #[ cfg( feature = "enabled" ) ] -#[ cfg( any( feature = "prelude", feature = "dt_prelude" ) ) ] -mod prelude_test; +// #[ cfg( any( feature = "prelude", feature = "dt_prelude" ) ) ] +// mod prelude_test; // #[ allow( unused_imports ) ] // use super::*; diff --git a/module/postponed/type_constructor/tests/inc/prelude_test.rs b/module/postponed/type_constructor/tests/inc/prelude_test.rs index 4c6ace0d4b..1699fe6b0e 100644 --- a/module/postponed/type_constructor/tests/inc/prelude_test.rs +++ b/module/postponed/type_constructor/tests/inc/prelude_test.rs @@ -1,68 +1,68 @@ -#[ allow( unused_imports ) ] -use super::*; - -// - -#[ cfg( any( not( feature = "no_std" ), feature = "use_alloc" ) ) ] -#[ cfg( feature = "prelude" ) ] -tests_impls! -{ - fn basic() - { - use the_module::prelude::*; - - /* test.case( "Vec" ) */ - let src = Vec::< i32 >::new(); - a_true!( src.is_empty() ); - - /* test.case( "DynList" ) */ - let src = DynList::< i32 >::new(); - a_true!( src.is_empty() ); - - /* test.case( "HashMap" ) */ - let src = HashMap::< i32, i32 >::new(); - a_true!( src.is_empty() ); - - /* test.case( "Map" ) */ - let src = Map::< i32, i32 >::new(); - a_true!( src.is_empty() ); - - /* test.case( "HashSet" ) */ - let src = HashSet::< i32 >::new(); - a_true!( src.is_empty() ); - - /* test.case( "Set" ) */ - let src = Set::< i32 >::new(); - a_true!( src.is_empty() ); - - /* test.case( "BTreeMap" ) */ - let src = BTreeMap::< i32, i32 >::new(); - a_true!( src.is_empty() ); - - /* test.case( "BTreeSet" ) */ - let src = BTreeSet::< i32 >::new(); - a_true!( src.is_empty() ); - - /* test.case( "BinaryHeap" ) */ - let src = BinaryHeap::< i32 >::new(); - a_true!( src.is_empty() ); - - /* test.case( "LinkedList" ) */ - let src = LinkedList::< i32 >::new(); - a_true!( src.is_empty() ); - - /* test.case( "VecDeque" ) */ - let src = VecDeque::< i32 >::new(); - a_true!( src.is_empty() ); - - } -} - -// - -#[ cfg( any( not( feature = "no_std" ), feature = "use_alloc" ) ) ] -#[ cfg( feature = "prelude" ) ] -tests_index! -{ - basic, -} +// #[ allow( unused_imports ) ] +// use super::*; +// +// // +// +// #[ cfg( any( not( feature = "no_std" ), feature = "use_alloc" ) ) ] +// #[ cfg( feature = "prelude" ) ] +// tests_impls! +// { +// fn basic() +// { +// use the_module::prelude::*; +// +// /* test.case( "Vec" ) */ +// let src = Vec::< i32 >::new(); +// a_true!( src.is_empty() ); +// +// /* test.case( "DynList" ) */ +// let src = DynList::< i32 >::new(); +// a_true!( src.is_empty() ); +// +// /* test.case( "HashMap" ) */ +// let src = HashMap::< i32, i32 >::new(); +// a_true!( src.is_empty() ); +// +// /* test.case( "Map" ) */ +// let src = Map::< i32, i32 >::new(); +// a_true!( src.is_empty() ); +// +// /* test.case( "HashSet" ) */ +// let src = HashSet::< i32 >::new(); +// a_true!( src.is_empty() ); +// +// /* test.case( "Set" ) */ +// let src = Set::< i32 >::new(); +// a_true!( src.is_empty() ); +// +// /* test.case( "BTreeMap" ) */ +// let src = BTreeMap::< i32, i32 >::new(); +// a_true!( src.is_empty() ); +// +// /* test.case( "BTreeSet" ) */ +// let src = BTreeSet::< i32 >::new(); +// a_true!( src.is_empty() ); +// +// /* test.case( "BinaryHeap" ) */ +// let src = BinaryHeap::< i32 >::new(); +// a_true!( src.is_empty() ); +// +// /* test.case( "LinkedList" ) */ +// let src = LinkedList::< i32 >::new(); +// a_true!( src.is_empty() ); +// +// /* test.case( "VecDeque" ) */ +// let src = VecDeque::< i32 >::new(); +// a_true!( src.is_empty() ); +// +// } +// } +// +// // +// +// #[ cfg( any( not( feature = "no_std" ), feature = "use_alloc" ) ) ] +// #[ cfg( feature = "prelude" ) ] +// tests_index! +// { +// basic, +// } diff --git a/step/Cargo.toml b/step/Cargo.toml index 70f00c8c96..6e37d39bd0 100644 --- a/step/Cargo.toml +++ b/step/Cargo.toml @@ -21,4 +21,4 @@ all-features = false willbe = { workspace = true, features = [ "full" ] } [dev-dependencies] -test_tools = { workspace = true } +# test_tools = { workspace = true } From 4a8e0c38d23ae5554dfcd80229d678ff14ea4183 Mon Sep 17 00:00:00 2001 From: wandalen Date: Tue, 5 Nov 2024 13:17:50 +0200 Subject: [PATCH 36/67] clean --- cgtools | 1 - 1 file changed, 1 deletion(-) delete mode 160000 cgtools diff --git a/cgtools b/cgtools deleted file mode 160000 index f42bdc878f..0000000000 --- a/cgtools +++ /dev/null @@ -1 +0,0 @@ -Subproject commit f42bdc878f9414f7fd46b212454f615ab6ebcf61 From 99b1fb0bb58472e908838a12ee5b66df5ce339c1 Mon Sep 17 00:00:00 2001 From: wbot <69343704+wtools-bot@users.noreply.github.com> Date: Wed, 6 Nov 2024 09:47:57 +0200 Subject: [PATCH 37/67] AUTO : Forward from refactoring_2 to alpha (#1477) decycling test_tools wip --- Cargo.toml | 22 +- module/core/collection_tools/Cargo.toml | 13 +- module/core/collection_tools/Readme.md | 9 +- .../examples/collection_tools_trivial.rs | 15 +- .../core/collection_tools/src/collection.rs | 32 -- .../collection/{heap.rs => binary_heap.rs} | 9 +- .../src/collection/{bmap.rs => btree_map.rs} | 7 +- .../src/collection/{bset.rs => btree_set.rs} | 7 +- .../src/collection/{hmap.rs => hash_map.rs} | 15 +- .../src/collection/{hset.rs => hash_set.rs} | 14 +- .../collection/{llist.rs => linked_list.rs} | 7 +- .../collection_tools/src/collection/mod.rs | 160 +++++++++ .../src/collection/{deque.rs => vec_deque.rs} | 7 +- .../src/collection/{vec.rs => vector.rs} | 8 +- module/core/collection_tools/src/lib.rs | 101 ++---- .../core/collection_tools/tests/inc/bmap.rs | 4 +- .../core/collection_tools/tests/inc/bset.rs | 4 +- .../core/collection_tools/tests/inc/deque.rs | 6 +- .../core/collection_tools/tests/inc/heap.rs | 4 +- .../core/collection_tools/tests/inc/hmap.rs | 6 +- .../core/collection_tools/tests/inc/hset.rs | 4 +- .../core/collection_tools/tests/inc/llist.rs | 6 +- module/core/collection_tools/tests/inc/mod.rs | 1 + .../tests/inc/namespace_test.rs | 12 + module/core/collection_tools/tests/inc/vec.rs | 8 +- module/core/collection_tools/tests/tests.rs | 2 +- module/core/error_tools/Cargo.toml | 5 +- .../examples/error_tools_trivial.rs | 2 +- .../error_tools/src/{ => error}/assert.rs | 0 .../src/{error.rs => error/mod.rs} | 304 +++++++++++------- .../core/error_tools/src/{ => error}/typed.rs | 0 .../error_tools/src/{ => error}/untyped.rs | 19 +- module/core/error_tools/src/lib.rs | 74 +---- module/core/error_tools/src/result.rs | 43 --- .../core/error_tools/tests/inc/basic_test.rs | 229 +++++++------ module/core/error_tools/tests/inc/mod.rs | 4 +- .../error_tools/tests/inc/namespace_test.rs | 12 + module/core/error_tools/tests/tests.rs | 3 +- module/core/meta_tools/Cargo.toml | 12 +- module/core/meta_tools/Readme.md | 15 - .../meta_tools/examples/meta_tools_trivial.rs | 10 +- module/core/meta_tools/src/lib.rs | 4 +- module/core/meta_tools/src/meta.rs | 4 +- .../tests/inc/meta_constructor_test.rs | 100 +++--- module/core/meta_tools/tests/inc/mod.rs | 8 +- module/core/mod_interface/Readme.md | 3 +- .../mod_interface_trivial/src/main.rs | 2 +- .../tests/inc/derive/attr_debug/mod.rs | 2 +- .../tests/inc/derive/layer/mod.rs | 2 +- .../inc/derive/layer_have_layer/layer_a.rs | 2 +- .../inc/derive/layer_have_layer/layer_b.rs | 2 +- .../tests/inc/derive/layer_have_layer/mod.rs | 2 +- .../derive/layer_have_layer_cfg/layer_a.rs | 2 +- .../derive/layer_have_layer_cfg/layer_b.rs | 2 +- .../inc/derive/layer_have_layer_cfg/mod.rs | 2 +- .../layer_have_layer_separate_use/layer_a.rs | 2 +- .../layer_have_layer_separate_use/layer_b.rs | 2 +- .../layer_have_layer_separate_use/mod.rs | 6 +- .../layer_a.rs | 2 +- .../layer_b.rs | 2 +- .../layer_have_layer_separate_use_two/mod.rs | 6 +- .../inc/derive/layer_have_mod_cfg/mod.rs | 2 +- .../derive/layer_unknown_vis/trybuild.stderr | 2 +- .../tests/inc/derive/layer_use_cfg/layer_a.rs | 2 +- .../tests/inc/derive/layer_use_cfg/layer_b.rs | 2 +- .../tests/inc/derive/layer_use_cfg/mod.rs | 8 +- .../inc/derive/layer_use_macro/layer_a.rs | 2 +- .../tests/inc/derive/layer_use_macro/mod.rs | 2 +- .../tests/inc/derive/micro_modules/mod.rs | 2 +- .../micro_modules_bad_vis/trybuild.stderr | 2 +- .../inc/derive/micro_modules_glob/mod.rs | 2 +- .../tests/inc/derive/micro_modules_two/mod.rs | 2 +- .../derive/micro_modules_two_joined/mod.rs | 2 +- .../micro_modules_unknown_vis/trybuild.stderr | 2 +- .../tests/inc/derive/reuse_basic/child.rs | 2 +- .../tests/inc/derive/reuse_basic/mod.rs | 2 +- .../tests/inc/derive/use_as/derive.rs | 2 +- .../inc/derive/use_bad_vis/trybuild.stderr | 2 +- .../tests/inc/derive/use_basic/mod.rs | 8 +- .../tests/inc/derive/use_layer/layer_a.rs | 2 +- .../tests/inc/derive/use_layer/mod.rs | 4 +- .../inc/derive/use_private_layers/layer_a.rs | 63 ++++ .../inc/derive/use_private_layers/layer_b.rs | 63 ++++ .../inc/derive/use_private_layers/mod.rs | 28 ++ .../derive/use_unknown_vis/trybuild.stderr | 2 +- .../tests/inc/manual/layer/mod.rs | 6 +- .../tests/inc/manual/micro_modules/mod.rs | 1 + .../{layer_use => use_layer}/layer_a.rs | 0 .../{layer_use => use_layer}/layer_b.rs | 0 .../manual/{layer_use => use_layer}/mod.rs | 4 + module/core/mod_interface/tests/inc/mod.rs | 5 +- .../inc/only_test/layer_simple_only_test.rs | 6 + .../mod_interface/tests/inc/trybuild_test.rs | 136 ++++---- module/core/mod_interface/tests/smoke_test.rs | 3 +- module/core/mod_interface/tests/tests.rs | 4 +- module/core/mod_interface_meta/src/impls.rs | 20 ++ .../core/mod_interface_meta/src/use_tree.rs | 62 ++-- module/core/test_tools/Cargo.toml | 83 +++-- module/core/test_tools/src/lib.rs | 235 ++++++++++---- module/core/test_tools/src/test/asset.rs | 69 +++- .../core/test_tools/src/test/compiletime.rs | 74 ++++- module/core/test_tools/src/test/helper.rs | 78 ++++- module/core/test_tools/src/test/mod.rs | 109 ++++++- module/core/test_tools/src/test/smoke_test.rs | 85 ++++- module/core/test_tools/src/test/version.rs | 69 +++- .../core/test_tools/tests/inc/basic_test.rs | 1 - module/core/wtools/Cargo.toml | 6 +- module/move/optimization_tools/Cargo.toml | 4 +- module/move/wca/src/ca/executor/executor.rs | 8 +- module/move/wca/src/ca/grammar/types.rs | 6 +- module/move/wca/src/ca/parser/parser.rs | 25 +- module/move/wca/src/ca/tool/mod.rs | 5 +- module/move/wca/src/ca/tool/table.rs | 4 +- module/move/wca/src/ca/verifier/verifier.rs | 12 +- module/move/willbe/src/action/cicd_renew.rs | 10 +- module/move/willbe/src/action/features.rs | 1 + module/move/willbe/src/action/list.rs | 10 +- module/move/willbe/src/action/main_header.rs | 18 +- module/move/willbe/src/action/publish_diff.rs | 2 +- .../action/readme_modules_headers_renew.rs | 12 +- .../move/willbe/src/action/workspace_renew.rs | 4 +- module/move/willbe/src/command/cicd_renew.rs | 2 +- module/move/willbe/src/command/list.rs | 2 +- module/move/willbe/src/command/main_header.rs | 4 +- .../src/command/readme_headers_renew.rs | 9 +- .../willbe/src/command/workspace_renew.rs | 2 +- module/move/willbe/src/entity/channel.rs | 2 +- module/move/willbe/src/entity/publish.rs | 2 +- module/move/willbe/src/tool/cargo.rs | 10 +- module/move/willbe/src/tool/collection.rs | 12 - module/move/willbe/src/tool/error.rs | 21 -- module/move/willbe/src/tool/git.rs | 14 +- module/move/willbe/src/tool/mod.rs | 11 +- .../tests/inc/action_tests/cicd_renew.rs | 2 +- .../willbe/tests/inc/action_tests/features.rs | 12 +- .../tests/inc/action_tests/list/data.rs | 16 +- .../tests/inc/action_tests/main_header.rs | 18 +- .../action_tests/readme_health_table_renew.rs | 20 +- .../readme_modules_headers_renew.rs | 24 +- .../tests/inc/action_tests/workspace_renew.rs | 4 +- .../move/willbe/tests/inc/tool/graph_test.rs | 2 +- module/postponed/non_std/Cargo.toml | 4 +- module/postponed/std_tools/Cargo.toml | 4 +- module/postponed/std_x/Cargo.toml | 4 +- module/step/meta/src/module/terminal.rs | 4 + .../meta/tests/_conditional/local_module.rs | 4 + module/step/meta/tests/_conditional/wtools.rs | 4 + 147 files changed, 1900 insertions(+), 1072 deletions(-) delete mode 100644 module/core/collection_tools/src/collection.rs rename module/core/collection_tools/src/collection/{heap.rs => binary_heap.rs} (95%) rename module/core/collection_tools/src/collection/{bmap.rs => btree_map.rs} (97%) rename module/core/collection_tools/src/collection/{bset.rs => btree_set.rs} (96%) rename module/core/collection_tools/src/collection/{hmap.rs => hash_map.rs} (93%) rename module/core/collection_tools/src/collection/{hset.rs => hash_set.rs} (94%) rename module/core/collection_tools/src/collection/{llist.rs => linked_list.rs} (97%) create mode 100644 module/core/collection_tools/src/collection/mod.rs rename module/core/collection_tools/src/collection/{deque.rs => vec_deque.rs} (96%) rename module/core/collection_tools/src/collection/{vec.rs => vector.rs} (96%) create mode 100644 module/core/collection_tools/tests/inc/namespace_test.rs rename module/core/error_tools/src/{ => error}/assert.rs (100%) rename module/core/error_tools/src/{error.rs => error/mod.rs} (54%) rename module/core/error_tools/src/{ => error}/typed.rs (100%) rename module/core/error_tools/src/{ => error}/untyped.rs (86%) delete mode 100644 module/core/error_tools/src/result.rs create mode 100644 module/core/error_tools/tests/inc/namespace_test.rs create mode 100644 module/core/mod_interface/tests/inc/derive/use_private_layers/layer_a.rs create mode 100644 module/core/mod_interface/tests/inc/derive/use_private_layers/layer_b.rs create mode 100644 module/core/mod_interface/tests/inc/derive/use_private_layers/mod.rs rename module/core/mod_interface/tests/inc/manual/{layer_use => use_layer}/layer_a.rs (100%) rename module/core/mod_interface/tests/inc/manual/{layer_use => use_layer}/layer_b.rs (100%) rename module/core/mod_interface/tests/inc/manual/{layer_use => use_layer}/mod.rs (93%) delete mode 100644 module/move/willbe/src/tool/collection.rs delete mode 100644 module/move/willbe/src/tool/error.rs diff --git a/Cargo.toml b/Cargo.toml index a940b1b1ec..95a8901253 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -536,4 +536,24 @@ version = "0.1.83" [workspace.dependencies.tokio] version = "1.41.0" features = [] -default-features = false \ No newline at end of file +default-features = false + +[workspace.dependencies.anyhow] +version = "~1.0" +# features = [] +# default-features = false + +[workspace.dependencies.thiserror] +version = "~1.0" +# features = [] +# default-features = false + +[workspace.dependencies.hashbrown] +version = "~0.14.3" +# optional = true +default-features = false +# features = [ "default" ] + +[workspace.dependencies.paste] +version = "~1.0.14" +default-features = false diff --git a/module/core/collection_tools/Cargo.toml b/module/core/collection_tools/Cargo.toml index 86dcfa51b3..d69009a758 100644 --- a/module/core/collection_tools/Cargo.toml +++ b/module/core/collection_tools/Cargo.toml @@ -11,16 +11,14 @@ documentation = "https://docs.rs/collection_tools" repository = "https://github.com/Wandalen/wTools/tree/master/module/core/collection_tools" homepage = "https://github.com/Wandalen/wTools/tree/master/module/core/collection_tools" description = """ -Collection of general purpose tools to manipulate collections( containers like Vec/HashMap/HashSet ). +General purpose tools to manipulate collections( containers like Vec/HashMap/HashSet ). """ categories = [ "algorithms", "development-tools" ] keywords = [ "fundamental", "general-purpose" ] - [lints] workspace = true - [package.metadata.docs.rs] features = [ "full" ] all-features = false @@ -28,31 +26,26 @@ all-features = false [features] no_std = [ - # "test_tools/no_std", ] use_alloc = [ - "no_std", # qqq : for Anton : why is that better? -- use_alloc means that we do not use std, but alloc and hashbrown + "no_std", "hashbrown", - # "test_tools/use_alloc", // why is it needed? -- not needed, removed ] default = [ "enabled", - # "reexports", "collection_constructors", "collection_into_constructors", ] full = [ "enabled", - # "reexports", "collection_constructors", "collection_into_constructors", ] enabled = [] -# reexports = [] # Collection constructors, like `hmap!{ "key" => "val" }` collection_constructors = [] @@ -63,7 +56,7 @@ collection_into_constructors = [] [dependencies] ## external -hashbrown = { version = "~0.14.3", optional = true, default-features = false, features = [ "default" ] } +hashbrown = { workspace = true, optional = true, default-features = false, features = [ "default" ] } [dev-dependencies] test_tools = { workspace = true } diff --git a/module/core/collection_tools/Readme.md b/module/core/collection_tools/Readme.md index 1430c6d6ef..2b6f2b0ab6 100644 --- a/module/core/collection_tools/Readme.md +++ b/module/core/collection_tools/Readme.md @@ -5,7 +5,7 @@ [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) [![rust-status](https://github.com/Wandalen/wTools/actions/workflows/module_collection_tools_push.yml/badge.svg)](https://github.com/Wandalen/wTools/actions/workflows/module_collection_tools_push.yml) [![docs.rs](https://img.shields.io/docsrs/collection_tools?color=e3e8f0&logo=docs.rs)](https://docs.rs/collection_tools) [![Open in Gitpod](https://raster.shields.io/static/v1?label=try&message=online&color=eee&logo=gitpod&logoColor=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=module%2Fcore%2Fcollection_tools%2Fexamples%2Fcollection_tools_trivial.rs,RUN_POSTFIX=--example%20collection_tools_trivial/https://github.com/Wandalen/wTools) [![discord](https://img.shields.io/discord/872391416519737405?color=eee&logo=discord&logoColor=eee&label=ask)](https://discord.gg/m3YfbXpUUY) -Collection of general purpose tools to manipulate collections( containers like Vec/HashMap/HashSet... ). +General purpose tools to manipulate collections( containers like Vec/HashMap/HashSet... ). ### Basic Use Case :: Variadic Constructors for Collections @@ -71,7 +71,7 @@ assert_eq!( meta_list, meta_list ); ### Basic Use Case :: `no_std` `HashSet` / `HashMap` -When implementing a `no_std` environment with the `use_alloc` feature in your Rust project, you'll encounter a challenge: collections like `Vec` are imported differently depending on the availability of the `std` library. Moreover, to use data structures such as `HashSet` or `HashMap` in a `no_std` context, it's necessary to depend on third-party crates, as these are not provided by the `alloc` crate directly. This crate aims to simplify the process of designing Rust libraries or applications that require these collections in a `no_std` environment, offering a more streamlined approach to working with dynamic data structures without the standard library. +When implementing a `no_std` ( `!use_std` ) environment with the `use_alloc` feature in your Rust project, you'll encounter a challenge: collections like `Vec` are imported differently depending on the availability of the `std` library. Moreover, to use data structures such as `HashSet` or `HashMap` in a `no_std` context, it's necessary to depend on third-party crates, as these are not provided by the `alloc` crate directly. This crate aims to simplify the process of designing Rust libraries or applications that require these collections in a `no_std` environment, offering a more streamlined approach to working with dynamic data structures without the standard library. You can do @@ -98,7 +98,7 @@ Instead of # #[ cfg( any( feature = "use_alloc", not( feature = "no_std" ) ) ) ] # { -#[ cfg( feature = "use_alloc" ) ] +#[ cfg( all( feature = "no_std", feature = "use_alloc" ) ) ] use hashbrown::HashSet; // a `no_std` replacement for `HashSet` #[ cfg( not( feature = "no_std" ) ) ] use std::collections::HashSet; @@ -120,7 +120,8 @@ While strict macros require you to have all members of the same type, more relax For example: ```rust -# #[ cfg( all( feature = "enabled", feature = "collection_into_constructors", any( not( feature = "no_std" ), feature = "use_alloc" ) ) ) ] +# #[ cfg( all( feature = "enabled", feature = "collection_into_constructors" ) ) ] +# #[ cfg( any( feature = "use_alloc", not( feature = "no_std" ) ) ) ] # { use std::borrow::Cow; let vec : Vec< String > = collection_tools::into_vec!( "&str", "String".to_string(), Cow::from( "Cow" ) ); diff --git a/module/core/collection_tools/examples/collection_tools_trivial.rs b/module/core/collection_tools/examples/collection_tools_trivial.rs index 8a11bb85bf..79ff09bf0d 100644 --- a/module/core/collection_tools/examples/collection_tools_trivial.rs +++ b/module/core/collection_tools/examples/collection_tools_trivial.rs @@ -19,18 +19,15 @@ //! a `HashMap`, making your code cleaner and more concise. This is particularly useful in cases //! where you need to define a map with a known set of key-value pairs upfront. -#[ cfg( not( all -( -// not( feature = "use_alloc" ) ) ], - all( feature = "enabled", feature = "collection_constructors" ), - any( not( feature = "no_std" ), feature = "use_alloc" ) +#[ cfg( not( all( + feature = "enabled", + feature = "collection_constructors", + any( feature = "use_alloc", not( feature = "no_std" ) ) )))] -fn main(){} +fn main() {} -// zzz : aaa : rid of `#[ cfg( not( feature = "use_alloc" ) ) ]` -- Rid of by not relying on std -// #[ cfg( not( feature = "use_alloc" ) ) ] #[ cfg( all( feature = "enabled", feature = "collection_constructors" ) ) ] -#[ cfg( any( not( feature = "no_std" ), feature = "use_alloc" ) ) ] +#[ cfg( any( feature = "use_alloc", not( feature = "no_std" ) ) ) ] fn main() { use collection_tools::*; diff --git a/module/core/collection_tools/src/collection.rs b/module/core/collection_tools/src/collection.rs deleted file mode 100644 index 67315f35a4..0000000000 --- a/module/core/collection_tools/src/collection.rs +++ /dev/null @@ -1,32 +0,0 @@ -/// Not meant to be called directly. -#[ doc( hidden ) ] -#[ macro_export( local_inner_macros ) ] -macro_rules! count -{ - ( @single $( $x : tt )* ) => ( () ); - - ( - @count $( $rest : expr ),* - ) - => - ( - < [ () ] >::len( &[ $( count!( @single $rest ) ),* ] ) - ); -} - -/// [std::collections::BTreeMap] macros -pub mod bmap; -/// [std::collections::BTreeSet] macros -pub mod bset; -/// [std::collections::BinaryHeap] macros -pub mod heap; -/// [std::collections::HashMap] macros -pub mod hmap; -/// [std::collections::HashSet] macros -pub mod hset; -/// [std::collections::LinkedList] macros -pub mod llist; -/// [Vec] macros -pub mod vec; -/// [std::collections::VecDeque] macros -pub mod deque; diff --git a/module/core/collection_tools/src/collection/heap.rs b/module/core/collection_tools/src/collection/binary_heap.rs similarity index 95% rename from module/core/collection_tools/src/collection/heap.rs rename to module/core/collection_tools/src/collection/binary_heap.rs index 8d38492497..965f5804c5 100644 --- a/module/core/collection_tools/src/collection/heap.rs +++ b/module/core/collection_tools/src/collection/binary_heap.rs @@ -1,6 +1,9 @@ +#[ allow( unused_imports ) ] +use super::*; + #[ doc( inline ) ] #[ allow( unused_imports ) ] - pub use alloc::collections::binary_heap::*; +pub use alloc::collections::binary_heap::*; /// Creates a `BinaryHeap` from a list of elements. /// @@ -57,7 +60,7 @@ macro_rules! heap => {{ let _cap = count!( @count $( $key ),* ); - let mut _heap = $crate::heap::BinaryHeap::with_capacity( _cap ); + let mut _heap = $crate::collection::BinaryHeap::with_capacity( _cap ); $( _heap.push( $key ); )* @@ -146,7 +149,7 @@ macro_rules! into_heap => {{ let _cap = count!( @count $( $key ),* ); - let mut _heap = $crate::heap::BinaryHeap::with_capacity( _cap ); + let mut _heap = $crate::collection::BinaryHeap::with_capacity( _cap ); $( _heap.push( Into::into( $key ) ); )* diff --git a/module/core/collection_tools/src/collection/bmap.rs b/module/core/collection_tools/src/collection/btree_map.rs similarity index 97% rename from module/core/collection_tools/src/collection/bmap.rs rename to module/core/collection_tools/src/collection/btree_map.rs index e96f045e84..2e4ec94f13 100644 --- a/module/core/collection_tools/src/collection/bmap.rs +++ b/module/core/collection_tools/src/collection/btree_map.rs @@ -1,3 +1,6 @@ +#[ allow( unused_imports ) ] +use super::*; + #[ doc( inline ) ] #[ allow( unused_imports ) ] pub use alloc::collections::btree_map::*; @@ -70,7 +73,7 @@ macro_rules! bmap ) => {{ - let mut _map = $crate::bmap::BTreeMap::new(); + let mut _map = $crate::collection::BTreeMap::new(); $( let _ = _map.insert( $key , $value ); )* @@ -163,7 +166,7 @@ macro_rules! into_bmap ) => {{ - let mut _map = $crate::bmap::BTreeMap::new(); + let mut _map = $crate::collection::BTreeMap::new(); $( let _ = _map.insert( Into::into( $key ), Into::into( $value ) ); )* diff --git a/module/core/collection_tools/src/collection/bset.rs b/module/core/collection_tools/src/collection/btree_set.rs similarity index 96% rename from module/core/collection_tools/src/collection/bset.rs rename to module/core/collection_tools/src/collection/btree_set.rs index c0c6d249ed..7111811c2e 100644 --- a/module/core/collection_tools/src/collection/bset.rs +++ b/module/core/collection_tools/src/collection/btree_set.rs @@ -1,3 +1,6 @@ +#[ allow( unused_imports ) ] +use super::*; + #[ doc( inline ) ] #[ allow( unused_imports ) ] pub use alloc::collections::btree_set::*; @@ -56,7 +59,7 @@ macro_rules! bset ) => {{ - let mut _set = $crate::bset::BTreeSet::new(); + let mut _set = $crate::collection::BTreeSet::new(); $( _set.insert( $key ); )* @@ -149,7 +152,7 @@ macro_rules! into_bset ) => {{ - let mut _set = $crate::bset::BTreeSet::new(); + let mut _set = $crate::collection::BTreeSet::new(); $( _set.insert( Into::into( $key ) ); )* diff --git a/module/core/collection_tools/src/collection/hmap.rs b/module/core/collection_tools/src/collection/hash_map.rs similarity index 93% rename from module/core/collection_tools/src/collection/hmap.rs rename to module/core/collection_tools/src/collection/hash_map.rs index eceac4ee9b..8ebbc9f90f 100644 --- a/module/core/collection_tools/src/collection/hmap.rs +++ b/module/core/collection_tools/src/collection/hash_map.rs @@ -1,7 +1,12 @@ -#[ cfg( feature = "use_alloc" ) ] +#[ allow( unused_imports ) ] +use super::*; + +// xxx : qqq : wrong +#[ cfg( all( feature = "no_std", feature = "use_alloc" ) ) ] #[ doc( inline ) ] #[ allow( unused_imports ) ] pub use crate::dependency::hashbrown::hash_map::*; + #[ cfg( not( feature = "no_std" ) ) ] #[ doc( inline ) ] #[ allow( unused_imports ) ] @@ -14,7 +19,7 @@ pub use std::collections::hash_map::*; /// # Origin /// /// This collection can be reexported from different crates: -/// - from `std`, if `no_std` flag if off +/// - from `std`, if `use_std` is on ( `no_std` flag if off ) /// - from `hashbrown`, if `use_alloc` flag if on /// /// # Syntax @@ -77,7 +82,7 @@ macro_rules! hmap => {{ let _cap = count!( @count $( $key ),* ); - let mut _map = $crate::hmap::HashMap::with_capacity( _cap ); + let mut _map = $crate::collection::HashMap::with_capacity( _cap ); $( let _ = _map.insert( $key, $value ); )* @@ -98,7 +103,7 @@ macro_rules! hmap /// # Origin /// /// This collection can be reexported from different crates: -/// - from `std`, if `no_std` flag if off +/// - from `std`, if `use_std` is on ( `no_std` flag if off ) /// - from `hashbrown`, if `use_alloc` flag if on /// /// # Syntax @@ -172,7 +177,7 @@ macro_rules! into_hmap => {{ let _cap = count!( @count $( $key ),* ); - let mut _map = $crate::hmap::HashMap::with_capacity( _cap ); + let mut _map = $crate::collection::HashMap::with_capacity( _cap ); $( let _ = _map.insert( Into::into( $key ), Into::into( $value ) ); )* diff --git a/module/core/collection_tools/src/collection/hset.rs b/module/core/collection_tools/src/collection/hash_set.rs similarity index 94% rename from module/core/collection_tools/src/collection/hset.rs rename to module/core/collection_tools/src/collection/hash_set.rs index b9b2d682da..6fe8d8287a 100644 --- a/module/core/collection_tools/src/collection/hset.rs +++ b/module/core/collection_tools/src/collection/hash_set.rs @@ -1,7 +1,11 @@ +#[ allow( unused_imports ) ] +use super::*; + #[ cfg( feature = "use_alloc" ) ] #[ doc( inline ) ] #[ allow( unused_imports ) ] pub use crate::dependency::hashbrown::hash_set::*; + #[ cfg( not( feature = "no_std" ) ) ] #[ doc( inline ) ] #[ allow( unused_imports ) ] @@ -14,7 +18,7 @@ pub use std::collections::hash_set::*; /// # Origin /// /// This collection can be reexported from different crates: -/// - from `std`, if `no_std` flag if off +/// - from `std`, if `use_std` is on ( `no_std` flag if off ) /// - from `hashbrown`, if `use_alloc` flag if on /// /// # Syntax @@ -77,7 +81,7 @@ macro_rules! hset => {{ let _cap = count!( @count $( $key ),* ); - let mut _set = $crate::hset::HashSet::with_capacity( _cap ); + let mut _set = $crate::collection::HashSet::with_capacity( _cap ); $( let _ = _set.insert( $key ); )* @@ -96,9 +100,9 @@ macro_rules! hset /// type `T` used in the `HashSet`. Also, this means that sometimes you must specify the type of collection's items. /// /// # Origin -/// +/// /// This collection can be reexported from different crates: -/// - from `std`, if `no_std` flag if off +/// - from `std`, if `use_std` is on ( `no_std` flag if off ) /// - from `hashbrown`, if `use_alloc` flag if on /// /// # Syntax @@ -173,7 +177,7 @@ macro_rules! into_hset => {{ let _cap = count!( @count $( $key ),* ); - let mut _set = $crate::hset::HashSet::with_capacity( _cap ); + let mut _set = $crate::collection::HashSet::with_capacity( _cap ); $( let _ = _set.insert( Into::into( $key ) ); )* diff --git a/module/core/collection_tools/src/collection/llist.rs b/module/core/collection_tools/src/collection/linked_list.rs similarity index 97% rename from module/core/collection_tools/src/collection/llist.rs rename to module/core/collection_tools/src/collection/linked_list.rs index e6c8ddbe68..cc23637be1 100644 --- a/module/core/collection_tools/src/collection/llist.rs +++ b/module/core/collection_tools/src/collection/linked_list.rs @@ -1,3 +1,6 @@ +#[ allow( unused_imports ) ] +use super::*; + #[ doc( inline ) ] #[ allow( unused_imports ) ] pub use alloc::collections::linked_list::*; @@ -70,7 +73,7 @@ macro_rules! llist {{ // "The LinkedList allows pushing and popping elements at either end in constant time." // So no `with_capacity` - let mut _lst = $crate::llist::LinkedList::new(); + let mut _lst = $crate::collection::LinkedList::new(); $( _lst.push_back( $key ); )* @@ -164,7 +167,7 @@ macro_rules! into_llist {{ // "The LinkedList allows pushing and popping elements at either end in constant time." // So no `with_capacity` - let mut _lst = $crate::llist::LinkedList::new(); + let mut _lst = $crate::collection::LinkedList::new(); $( _lst.push_back( Into::into( $key ) ); )* diff --git a/module/core/collection_tools/src/collection/mod.rs b/module/core/collection_tools/src/collection/mod.rs new file mode 100644 index 0000000000..0f74158835 --- /dev/null +++ b/module/core/collection_tools/src/collection/mod.rs @@ -0,0 +1,160 @@ +/// Not meant to be called directly. +#[ doc( hidden ) ] +#[ macro_export( local_inner_macros ) ] +macro_rules! count +{ + ( @single $( $x : tt )* ) => ( () ); + + ( + @count $( $rest : expr ),* + ) + => + ( + < [ () ] >::len( &[ $( count!( @single $rest ) ),* ] ) + ); +} + +#[ cfg( feature = "enabled" ) ] +#[ cfg( any( feature = "use_alloc", not( feature = "no_std" ) ) ) ] +extern crate alloc; + +/// [std::collections::BTreeMap] macros +pub mod btree_map; +/// [std::collections::BTreeSet] macros +pub mod btree_set; +/// [std::collections::BinaryHeap] macros +pub mod binary_heap; +/// [std::collections::HashMap] macros +pub mod hash_map; +/// [std::collections::HashSet] macros +pub mod hash_set; +/// [std::collections::LinkedList] macros +pub mod linked_list; +/// [Vec] macros +pub mod vector; +/// [std::collections::VecDeque] macros +pub mod vec_deque; + +#[ doc( inline ) ] +#[ allow( unused_imports ) ] +#[ cfg( feature = "enabled" ) ] +pub use own::*; + +/// Own namespace of the module. +#[ cfg( feature = "enabled" ) ] +#[ allow( unused_imports ) ] +pub mod own +{ + use super::*; + + pub use super:: + { + btree_map, + btree_set, + binary_heap, + hash_map, + hash_set, + linked_list, + vector, + vec_deque, + }; + + #[ doc( inline ) ] + pub use orphan::*; + +} + +/// Parented namespace of the module. +#[ cfg( feature = "enabled" ) ] +#[ allow( unused_imports ) ] +pub mod orphan +{ + use super::*; + #[ doc( inline ) ] + pub use exposed::*; +} + +/// Exposed namespace of the module. +#[ cfg( feature = "enabled" ) ] +#[ allow( unused_imports ) ] +pub mod exposed +{ + use super::*; + + #[ doc( inline ) ] + pub use prelude::*; + + #[ doc( inline ) ] + pub use super::super::collection; + + #[ doc( inline ) ] + #[ cfg( any( feature = "use_alloc", not( feature = "no_std" ) ) ) ] + #[ cfg( feature = "collection_constructors" ) ] + pub use crate:: + { + vec as dlist, + deque, + llist, + hset, + hmap, + bmap, + bset, + }; + + #[ doc( inline ) ] + #[ cfg( any( feature = "use_alloc", not( feature = "no_std" ) ) ) ] + #[ cfg( feature = "collection_into_constructors" ) ] + pub use crate:: + { + into_vec, + into_vec as into_dlist, + into_vecd, + into_llist, + into_hset, + into_hmap, + into_bmap, + into_bset, + }; + + // #[ cfg( feature = "reexports" ) ] + #[ cfg( any( feature = "use_alloc", not( feature = "no_std" ) ) ) ] + #[ doc( inline ) ] + pub use + { + btree_map::BTreeMap, + btree_set::BTreeSet, + binary_heap::BinaryHeap, + hash_map::HashMap, + hash_set::HashSet, + linked_list::LinkedList, + vector::Vec, + vec_deque::VecDeque, + }; + + // #[ cfg( feature = "reexports" ) ] + #[ cfg( any( feature = "use_alloc", not( feature = "no_std" ) ) ) ] + #[ doc( inline ) ] + pub use + { + LinkedList as Llist, + Vec as Dlist, + VecDeque as Deque, + HashMap as Map, + HashMap as Hmap, + HashSet as Set, + HashSet as Hset, + BTreeMap as Bmap, + BTreeSet as Bset, + }; + + // qqq : cover by tests presence of all containers immidiately in collection_tools::* and in collection_tools::exposed::* + +} + +/// Prelude to use essentials: `use my_module::prelude::*`. +#[ cfg( feature = "enabled" ) ] +#[ allow( unused_imports ) ] +pub mod prelude +{ + use super::*; +} diff --git a/module/core/collection_tools/src/collection/deque.rs b/module/core/collection_tools/src/collection/vec_deque.rs similarity index 96% rename from module/core/collection_tools/src/collection/deque.rs rename to module/core/collection_tools/src/collection/vec_deque.rs index 66b106c6ec..1060d01c0b 100644 --- a/module/core/collection_tools/src/collection/deque.rs +++ b/module/core/collection_tools/src/collection/vec_deque.rs @@ -1,3 +1,6 @@ +#[ allow( unused_imports ) ] +use super::*; + #[ doc( inline ) ] #[ allow( unused_imports ) ] pub use alloc::collections::vec_deque::*; @@ -75,7 +78,7 @@ macro_rules! deque => {{ let _cap = count!( @count $( $key ),* ); - let mut _vecd = $crate::deque::VecDeque::with_capacity( _cap ); + let mut _vecd = $crate::collection::VecDeque::with_capacity( _cap ); $( _vecd.push_back( $key ); )* @@ -168,7 +171,7 @@ macro_rules! into_vecd => {{ let _cap = count!( @count $( $key ),* ); - let mut _vecd = $crate::deque::VecDeque::with_capacity( _cap ); + let mut _vecd = $crate::collection::VecDeque::with_capacity( _cap ); $( _vecd.push_back( Into::into( $key ) ); )* diff --git a/module/core/collection_tools/src/collection/vec.rs b/module/core/collection_tools/src/collection/vector.rs similarity index 96% rename from module/core/collection_tools/src/collection/vec.rs rename to module/core/collection_tools/src/collection/vector.rs index 2c19db388f..0ae9ccf6dd 100644 --- a/module/core/collection_tools/src/collection/vec.rs +++ b/module/core/collection_tools/src/collection/vector.rs @@ -1,6 +1,10 @@ +#[ allow( unused_imports ) ] +use super::*; + #[ doc( inline ) ] #[ allow( unused_imports ) ] pub use alloc::vec::*; + #[ doc( inline ) ] #[ allow( unused_imports ) ] pub use core::slice::{ Iter, IterMut }; @@ -73,7 +77,7 @@ macro_rules! vec => {{ let _cap = count!( @count $( $key ),* ); - let mut _vec = $crate::vec::Vec::with_capacity( _cap ); + let mut _vec = $crate::collection::Vec::with_capacity( _cap ); $( _vec.push( $key ); )* @@ -167,7 +171,7 @@ macro_rules! into_vec => {{ let _cap = count!( @count $( $key ),* ); - let mut _vec = $crate::vec::Vec::with_capacity( _cap ); + let mut _vec = $crate::collection::Vec::with_capacity( _cap ); $( _vec.push( Into::into( $key ) ); )* diff --git a/module/core/collection_tools/src/lib.rs b/module/core/collection_tools/src/lib.rs index e447f16f85..bcbbb4bdbc 100644 --- a/module/core/collection_tools/src/lib.rs +++ b/module/core/collection_tools/src/lib.rs @@ -4,17 +4,18 @@ #![ doc( html_root_url = "https://docs.rs/collection_tools/latest/collection_tools/" ) ] #![ doc = include_str!( concat!( env!( "CARGO_MANIFEST_DIR" ), "/", "Readme.md" ) ) ] -#[ cfg( feature = "enabled" ) ] -#[ cfg( any( not( feature = "no_std" ), feature = "use_alloc" ) ) ] -extern crate alloc; +// #[ cfg( feature = "enabled" ) ] +// #[ cfg( any( feature = "use_alloc", not( feature = "no_std" ) ) ) ] +// extern crate alloc; /// Module containing all collection macros #[ cfg( feature = "enabled" ) ] -#[ cfg( any( not( feature = "no_std" ), feature = "use_alloc" ) ) ] -mod collection; -#[ cfg( feature = "enabled" ) ] -#[ cfg( any( not( feature = "no_std" ), feature = "use_alloc" ) ) ] -pub use collection::*; +#[ cfg( any( feature = "use_alloc", not( feature = "no_std" ) ) ) ] +pub mod collection; + +// #[ cfg( feature = "enabled" ) ] +// #[ cfg( any( feature = "use_alloc", not( feature = "no_std" ) ) ) ] +// pub use collection::*; /// Namespace with dependencies. #[ cfg( feature = "enabled" ) ] @@ -36,10 +37,13 @@ pub use own::*; #[ allow( unused_imports ) ] pub mod own { - use super::*; + // use super::*; #[ doc( inline ) ] - pub use orphan::*; + pub use super::orphan::*; + + #[ doc( inline ) ] + pub use super::collection::own::*; } @@ -51,6 +55,10 @@ pub mod orphan use super::*; #[ doc( inline ) ] pub use exposed::*; + + #[ doc( inline ) ] + pub use collection::orphan::*; + } /// Exposed namespace of the module. @@ -64,66 +72,7 @@ pub mod exposed pub use prelude::*; #[ doc( inline ) ] - #[ cfg( any( feature = "use_alloc", all( feature = "collection_constructors", not( feature = "no_std" ) ) ) ) ] - pub use crate:: - { - vec as dlist, - deque, - llist, - hset, - hmap, - bmap, - bset, - }; - - #[ doc( inline ) ] - #[ cfg( any( feature = "use_alloc", all( feature = "collection_into_constructors", not( feature = "no_std" ) ) ) ) ] - pub use crate:: - { - into_vec, - into_vec as into_dlist, - into_vecd, - into_llist, - into_hset, - into_hmap, - into_bmap, - into_bset, - }; - - // #[ cfg( feature = "reexports" ) ] - #[ cfg( any( feature = "use_alloc", not( feature = "no_std" ) ) ) ] - #[ doc( inline ) ] - #[ allow( unused_imports ) ] - pub use crate:: - { - bmap::BTreeMap, - bset::BTreeSet, - heap::BinaryHeap, - hmap::HashMap, - hset::HashSet, - llist::LinkedList, - vec::Vec, - deque::VecDeque, - }; - - // #[ cfg( feature = "reexports" ) ] - #[ cfg( any( feature = "use_alloc", not( feature = "no_std" ) ) ) ] - #[ doc( inline ) ] - #[ allow( unused_imports ) ] - pub use - { - LinkedList as Llist, - Vec as Dlist, - VecDeque as Deque, - HashMap as Map, - HashMap as Hmap, - HashSet as Set, - HashSet as Hset, - BTreeMap as Bmap, - BTreeSet as Bset, - }; - - // qqq : cover by tests presence of all containers immidiately in collection_tools::* and in collection_tools::exposed::* + pub use collection::exposed::*; } @@ -133,4 +82,16 @@ pub mod exposed pub mod prelude { use super::*; + + #[ doc( inline ) ] + pub use collection::prelude::*; + } + +// pub use own::collection as xxx; +// pub use hmap as xxx; +// pub use own::HashMap as xxx; +// pub fn x() +// { +// let x : HashMap< usize, usize > = hmap!{}; +// } diff --git a/module/core/collection_tools/tests/inc/bmap.rs b/module/core/collection_tools/tests/inc/bmap.rs index af3d54dae5..113e69f810 100644 --- a/module/core/collection_tools/tests/inc/bmap.rs +++ b/module/core/collection_tools/tests/inc/bmap.rs @@ -68,7 +68,7 @@ fn iters() impl IntoIterator for MyContainer { type Item = ( i32, i32 ); - type IntoIter = the_module::bmap::IntoIter< i32, i32 >; + type IntoIter = the_module::btree_map::IntoIter< i32, i32 >; fn into_iter( self ) -> Self::IntoIter { @@ -79,7 +79,7 @@ fn iters() impl< 'a > IntoIterator for &'a MyContainer { type Item = ( &'a i32, &'a i32 ); - type IntoIter = the_module::bmap::Iter< 'a, i32, i32 >; + type IntoIter = the_module::btree_map::Iter< 'a, i32, i32 >; fn into_iter( self ) -> Self::IntoIter { diff --git a/module/core/collection_tools/tests/inc/bset.rs b/module/core/collection_tools/tests/inc/bset.rs index 2a427d0a26..9fb625bf30 100644 --- a/module/core/collection_tools/tests/inc/bset.rs +++ b/module/core/collection_tools/tests/inc/bset.rs @@ -67,7 +67,7 @@ fn iters() impl IntoIterator for MyContainer { type Item = i32; - type IntoIter = the_module::bset::IntoIter< i32 >; + type IntoIter = the_module::btree_set::IntoIter< i32 >; fn into_iter( self ) -> Self::IntoIter { @@ -78,7 +78,7 @@ fn iters() impl< 'a > IntoIterator for &'a MyContainer { type Item = &'a i32; - type IntoIter = the_module::bset::Iter< 'a, i32 >; + type IntoIter = the_module::btree_set::Iter< 'a, i32 >; fn into_iter( self ) -> Self::IntoIter { diff --git a/module/core/collection_tools/tests/inc/deque.rs b/module/core/collection_tools/tests/inc/deque.rs index 98ab6498bd..d58c72d8cc 100644 --- a/module/core/collection_tools/tests/inc/deque.rs +++ b/module/core/collection_tools/tests/inc/deque.rs @@ -66,7 +66,7 @@ fn iters() impl IntoIterator for MyContainer { type Item = i32; - type IntoIter = the_module::deque::IntoIter< i32 >; + type IntoIter = the_module::vec_deque::IntoIter< i32 >; fn into_iter( self ) -> Self::IntoIter { @@ -77,7 +77,7 @@ fn iters() impl< 'a > IntoIterator for &'a MyContainer { type Item = &'a i32; - type IntoIter = the_module::deque::Iter< 'a, i32 >; + type IntoIter = the_module::vec_deque::Iter< 'a, i32 >; fn into_iter( self ) -> Self::IntoIter { @@ -88,7 +88,7 @@ fn iters() impl< 'a > IntoIterator for &'a mut MyContainer { type Item = &'a mut i32; - type IntoIter = the_module::deque::IterMut< 'a, i32 >; + type IntoIter = the_module::vec_deque::IterMut< 'a, i32 >; fn into_iter( self ) -> Self::IntoIter { diff --git a/module/core/collection_tools/tests/inc/heap.rs b/module/core/collection_tools/tests/inc/heap.rs index a342548cfc..ad251e0b39 100644 --- a/module/core/collection_tools/tests/inc/heap.rs +++ b/module/core/collection_tools/tests/inc/heap.rs @@ -62,7 +62,7 @@ fn iters() impl IntoIterator for MyContainer { type Item = i32; - type IntoIter = the_module::heap::IntoIter< i32 >; + type IntoIter = the_module::binary_heap::IntoIter< i32 >; fn into_iter( self ) -> Self::IntoIter { @@ -73,7 +73,7 @@ fn iters() impl< 'a > IntoIterator for &'a MyContainer { type Item = &'a i32; - type IntoIter = the_module::heap::Iter< 'a, i32 >; + type IntoIter = the_module::binary_heap::Iter< 'a, i32 >; fn into_iter( self ) -> Self::IntoIter { diff --git a/module/core/collection_tools/tests/inc/hmap.rs b/module/core/collection_tools/tests/inc/hmap.rs index 629c7155a6..042b4c8653 100644 --- a/module/core/collection_tools/tests/inc/hmap.rs +++ b/module/core/collection_tools/tests/inc/hmap.rs @@ -77,7 +77,7 @@ fn iters() impl IntoIterator for MyContainer { type Item = ( i32, i32 ); - type IntoIter = the_module::hmap::IntoIter< i32, i32 >; + type IntoIter = the_module::hash_map::IntoIter< i32, i32 >; fn into_iter( self ) -> Self::IntoIter { @@ -88,7 +88,7 @@ fn iters() impl< 'a > IntoIterator for &'a MyContainer { type Item = ( &'a i32, &'a i32 ); - type IntoIter = the_module::hmap::Iter< 'a, i32, i32 >; + type IntoIter = the_module::hash_map::Iter< 'a, i32, i32 >; fn into_iter( self ) -> Self::IntoIter { @@ -99,7 +99,7 @@ fn iters() impl< 'a > IntoIterator for &'a mut MyContainer { type Item = ( &'a i32, &'a mut i32 ); - type IntoIter = the_module::hmap::IterMut< 'a, i32, i32 >; + type IntoIter = the_module::hash_map::IterMut< 'a, i32, i32 >; fn into_iter( self ) -> Self::IntoIter { diff --git a/module/core/collection_tools/tests/inc/hset.rs b/module/core/collection_tools/tests/inc/hset.rs index c844836874..b3af31cb2d 100644 --- a/module/core/collection_tools/tests/inc/hset.rs +++ b/module/core/collection_tools/tests/inc/hset.rs @@ -74,7 +74,7 @@ fn iters() impl IntoIterator for MyContainer { type Item = i32; - type IntoIter = the_module::hset::IntoIter< i32 >; + type IntoIter = the_module::hash_set::IntoIter< i32 >; fn into_iter( self ) -> Self::IntoIter { @@ -85,7 +85,7 @@ fn iters() impl< 'a > IntoIterator for &'a MyContainer { type Item = &'a i32; - type IntoIter = the_module::hset::Iter< 'a, i32 >; + type IntoIter = the_module::hash_set::Iter< 'a, i32 >; fn into_iter( self ) -> Self::IntoIter { diff --git a/module/core/collection_tools/tests/inc/llist.rs b/module/core/collection_tools/tests/inc/llist.rs index 68620e2a69..3a861b0ec2 100644 --- a/module/core/collection_tools/tests/inc/llist.rs +++ b/module/core/collection_tools/tests/inc/llist.rs @@ -67,7 +67,7 @@ fn iters() impl IntoIterator for MyContainer { type Item = i32; - type IntoIter = the_module::llist::IntoIter< i32 >; + type IntoIter = the_module::linked_list::IntoIter< i32 >; fn into_iter( self ) -> Self::IntoIter { @@ -78,7 +78,7 @@ fn iters() impl< 'a > IntoIterator for &'a MyContainer { type Item = &'a i32; - type IntoIter = the_module::llist::Iter< 'a, i32 >; + type IntoIter = the_module::linked_list::Iter< 'a, i32 >; fn into_iter( self ) -> Self::IntoIter { @@ -89,7 +89,7 @@ fn iters() impl< 'a > IntoIterator for &'a mut MyContainer { type Item = &'a mut i32; - type IntoIter = the_module::llist::IterMut< 'a, i32 >; + type IntoIter = the_module::linked_list::IterMut< 'a, i32 >; fn into_iter( self ) -> Self::IntoIter { diff --git a/module/core/collection_tools/tests/inc/mod.rs b/module/core/collection_tools/tests/inc/mod.rs index ddd10e261d..86855a9d84 100644 --- a/module/core/collection_tools/tests/inc/mod.rs +++ b/module/core/collection_tools/tests/inc/mod.rs @@ -9,6 +9,7 @@ mod llist; mod vec; mod deque; +mod namespace_test; mod components; // qqq : make subdirectory for each container -- done diff --git a/module/core/collection_tools/tests/inc/namespace_test.rs b/module/core/collection_tools/tests/inc/namespace_test.rs new file mode 100644 index 0000000000..841ecac64f --- /dev/null +++ b/module/core/collection_tools/tests/inc/namespace_test.rs @@ -0,0 +1,12 @@ +use super::*; + +#[ test ] +fn exposed_main_namespace() +{ + + let _v : Vec< u32 > = the_module::collection::Vec::new(); + let _v : Vec< u32 > = the_module::exposed::collection::Vec::new(); + use the_module::exposed::*; + let _v : Vec< u32 > = collection::Vec::new(); + +} \ No newline at end of file diff --git a/module/core/collection_tools/tests/inc/vec.rs b/module/core/collection_tools/tests/inc/vec.rs index c1a5f66804..5bf78631ba 100644 --- a/module/core/collection_tools/tests/inc/vec.rs +++ b/module/core/collection_tools/tests/inc/vec.rs @@ -1,7 +1,7 @@ use super::*; #[ test ] -#[ cfg( any( not( feature = "no_std" ), feature = "use_alloc" ) ) ] +#[ cfg( any( feature = "use_alloc", not( feature = "no_std" ) ) ) ] fn reexport() { @@ -86,7 +86,7 @@ fn iters() impl IntoIterator for MyContainer { type Item = i32; - type IntoIter = the_module::vec::IntoIter< i32 >; + type IntoIter = the_module::vector::IntoIter< i32 >; // qqq : should work -- works fn into_iter( self ) -> Self::IntoIter @@ -98,7 +98,7 @@ fn iters() impl< 'a > IntoIterator for &'a MyContainer { type Item = &'a i32; - type IntoIter = the_module::vec::Iter< 'a, i32 >; + type IntoIter = the_module::vector::Iter< 'a, i32 >; fn into_iter( self ) -> Self::IntoIter { @@ -109,7 +109,7 @@ fn iters() impl< 'a > IntoIterator for &'a mut MyContainer { type Item = &'a mut i32; - type IntoIter = the_module::vec::IterMut< 'a, i32 >; + type IntoIter = the_module::vector::IterMut< 'a, i32 >; fn into_iter( self ) -> Self::IntoIter { diff --git a/module/core/collection_tools/tests/tests.rs b/module/core/collection_tools/tests/tests.rs index a36c5debec..ecc936c445 100644 --- a/module/core/collection_tools/tests/tests.rs +++ b/module/core/collection_tools/tests/tests.rs @@ -1,4 +1,4 @@ -// usual tests +#![ allow( unused_imports ) ] #[ path="../../../../module/step/meta/src/module/aggregating.rs" ] mod aggregating; diff --git a/module/core/error_tools/Cargo.toml b/module/core/error_tools/Cargo.toml index de02c81004..dafae99166 100644 --- a/module/core/error_tools/Cargo.toml +++ b/module/core/error_tools/Cargo.toml @@ -48,8 +48,9 @@ error_untyped = [ "anyhow" ] # = entry [dependencies] -anyhow = { version = "~1.0", optional = true } -thiserror = { version = "~1.0", optional = true } +anyhow = { workspace = true, optional = true } +thiserror = { workspace = true, optional = true } [dev-dependencies] test_tools = { workspace = true } +# xxx : qqq : review \ No newline at end of file diff --git a/module/core/error_tools/examples/error_tools_trivial.rs b/module/core/error_tools/examples/error_tools_trivial.rs index cc6fc29f24..e6ddd65432 100644 --- a/module/core/error_tools/examples/error_tools_trivial.rs +++ b/module/core/error_tools/examples/error_tools_trivial.rs @@ -17,5 +17,5 @@ fn main() fn f1() -> error_tools::untyped::Result< () > { let _read = std::fs::read_to_string( "Cargo.toml" )?; - Err( error_tools::BasicError::new( "Some error" ).into() ) + Err( error_tools::untyped::format_err!( "Some error" ) ) } diff --git a/module/core/error_tools/src/assert.rs b/module/core/error_tools/src/error/assert.rs similarity index 100% rename from module/core/error_tools/src/assert.rs rename to module/core/error_tools/src/error/assert.rs diff --git a/module/core/error_tools/src/error.rs b/module/core/error_tools/src/error/mod.rs similarity index 54% rename from module/core/error_tools/src/error.rs rename to module/core/error_tools/src/error/mod.rs index 730f9c477c..cd999e016d 100644 --- a/module/core/error_tools/src/error.rs +++ b/module/core/error_tools/src/error/mod.rs @@ -96,132 +96,178 @@ mod private /// helps in defining such results more concisely. pub type ResultWithReport< Report, Error > = Result< Report, ( Report, Error ) >; - /// - /// Macro to generate an error descriptor. - /// - /// ### Basic use-case. - /// ```rust - /// # use error_tools::{ BasicError, err }; - /// fn f1() -> BasicError - /// { - /// return err!( "No attr" ); - /// } - /// ``` - /// - - #[ macro_export ] - macro_rules! err - { - - ( $msg : expr ) => - { - $crate::BasicError::new( $msg ).into() - }; - ( $msg : expr, $( $arg : expr ),+ $(,)? ) => - { - $crate::BasicError::new( format!( $msg, $( $arg ),+ ) ).into() - }; - - } - - /// - /// Macro to return an Err( error ) generating error descriptor. - /// - /// ### Basic use-case. - /// ```rust - /// # use error_tools::{ BasicError, return_err }; - /// fn f1() -> Result< (), BasicError > - /// { - /// return_err!( "No attr" ); - /// } - /// ``` - /// - - #[ macro_export ] - macro_rules! return_err - { - - ( $msg : expr ) => - { - return Result::Err( $crate::err!( $msg ) ) - }; - ( $msg : expr, $( $arg : expr ),+ $(,)? ) => - { - return Result::Err( $crate::err!( $msg, $( $arg ),+ ) ) - }; - - } - - // zzz : review - - /// baic implementation of generic BasicError - - #[ derive( core::fmt::Debug, core::clone::Clone, core::cmp::PartialEq, core::cmp::Eq ) ] - pub struct BasicError - { - msg : String, - } - - impl BasicError - { - /// Constructor expecting message with description. - pub fn new< Msg : Into< String > >( msg : Msg ) -> BasicError - { - BasicError { msg : msg.into() } - } - /// Message with description getter. - pub fn msg( &self ) -> &String - { - &self.msg - } - } +// /// +// /// Macro to generate an error descriptor. +// /// +// /// ### Basic use-case. +// /// ```rust +// /// # use error_tools::{ BasicError, err }; +// /// fn f1() -> BasicError +// /// { +// /// return err!( "No attr" ); +// /// } +// /// ``` +// /// +// +// #[ macro_export ] +// macro_rules! err +// { +// +// ( $msg : expr ) => +// { +// $crate::BasicError::new( $msg ).into() +// }; +// ( $msg : expr, $( $arg : expr ),+ $(,)? ) => +// { +// $crate::BasicError::new( format!( $msg, $( $arg ),+ ) ).into() +// }; +// +// } +// +// /// +// /// Macro to return an Err( error ) generating error descriptor. +// /// +// /// ### Basic use-case. +// /// ```rust +// /// # use error_tools::{ BasicError, return_err }; +// /// fn f1() -> Result< (), BasicError > +// /// { +// /// return_err!( "No attr" ); +// /// } +// /// ``` +// /// +// +// #[ macro_export ] +// macro_rules! return_err +// { +// +// ( $msg : expr ) => +// { +// return Result::Err( $crate::err!( $msg ) ) +// }; +// ( $msg : expr, $( $arg : expr ),+ $(,)? ) => +// { +// return Result::Err( $crate::err!( $msg, $( $arg ),+ ) ) +// }; +// +// } +// +// // zzz : review +// // xxx : rid of +// +// /// baic implementation of generic BasicError +// +// #[ derive( core::fmt::Debug, core::clone::Clone, core::cmp::PartialEq, core::cmp::Eq ) ] +// pub struct BasicError +// { +// msg : String, +// } +// +// impl BasicError +// { +// /// Constructor expecting message with description. +// pub fn new< Msg : Into< String > >( msg : Msg ) -> BasicError +// { +// BasicError { msg : msg.into() } +// } +// /// Message with description getter. +// pub fn msg( &self ) -> &String +// { +// &self.msg +// } +// } +// +// impl core::fmt::Display for BasicError +// { +// fn fmt(&self, f: &mut core::fmt::Formatter< '_ >) -> core::fmt::Result +// { +// write!( f, "{}", self.msg ) +// } +// } +// +// impl ErrorTrait for BasicError +// { +// fn description( &self ) -> &str +// { +// &self.msg +// } +// } +// +// impl< T > From< BasicError > for Result< T, BasicError > +// { +// /// Returns the argument unchanged. +// #[ inline( always ) ] +// fn from( src : BasicError ) -> Self +// { +// Result::Err( src ) +// } +// } +// +// pub use err; +// pub use return_err; - impl core::fmt::Display for BasicError - { - fn fmt(&self, f: &mut core::fmt::Formatter< '_ >) -> core::fmt::Result - { - write!( f, "{}", self.msg ) - } - } - - impl ErrorTrait for BasicError - { - fn description( &self ) -> &str - { - &self.msg - } - } + // qqq : write standard mod interface without using mod_interface /* aaa : Dmytro : added to each library file */ +} - impl< T > From< BasicError > for Result< T, BasicError > - { - /// Returns the argument unchanged. - #[ inline( always ) ] - fn from( src : BasicError ) -> Self - { - Result::Err( src ) - } - } +/// Assertions. +#[ cfg( feature = "enabled" ) ] +pub mod assert; - pub use err; - pub use return_err; +#[ cfg( feature = "enabled" ) ] +#[ cfg( feature = "error_typed" ) ] +/// Typed exceptions handling mechanism. +pub mod typed; - // qqq : write standard mod interface without using mod_interface /* aaa : Dmytro : added to each library file */ -} +#[ cfg( feature = "enabled" ) ] +#[ cfg( feature = "error_untyped" ) ] +/// Untyped exceptions handling mechanism. +pub mod untyped; +#[ cfg( feature = "enabled" ) ] #[ doc( inline ) ] #[ allow( unused_imports ) ] pub use own::*; /// Own namespace of the module. +#[ cfg( feature = "enabled" ) ] #[ allow( unused_imports ) ] pub mod own { use super::*; + #[ doc( inline ) ] pub use orphan::*; + + #[ doc( inline ) ] + pub use assert::orphan::*; + + #[ cfg( feature = "error_untyped" ) ] + #[ doc( inline ) ] + pub use untyped::orphan::*; + + #[ cfg( feature = "error_typed" ) ] + #[ doc( inline ) ] + pub use typed::orphan::*; + + #[ doc( inline ) ] + pub use private:: + { + // err, + // return_err, + ErrorTrait, + // BasicError, + }; + + pub use super::assert; + #[ cfg( feature = "error_typed" ) ] + pub use super::typed; + #[ cfg( feature = "error_untyped" ) ] + pub use super::untyped; + } /// Shared with parent namespace of the module +#[ cfg( feature = "enabled" ) ] #[ allow( unused_imports ) ] pub mod orphan { @@ -231,11 +277,18 @@ pub mod orphan } /// Exposed namespace of the module. +#[ cfg( feature = "enabled" ) ] #[ allow( unused_imports ) ] pub mod exposed { use super::*; + #[ doc( inline ) ] + pub use prelude::*; + + // Expose itself. + pub use super::super::error; + #[ doc( inline ) ] pub use private:: { @@ -244,22 +297,43 @@ pub mod exposed }; #[ doc( inline ) ] - pub use prelude::*; + pub use assert::exposed::*; + + #[ cfg( feature = "error_untyped" ) ] + #[ doc( inline ) ] + pub use untyped::exposed::*; + + #[ cfg( feature = "error_typed" ) ] + #[ doc( inline ) ] + pub use typed::exposed::*; + } /// Prelude to use essentials: `use my_module::prelude::*`. +#[ cfg( feature = "enabled" ) ] #[ allow( unused_imports ) ] pub mod prelude { use super::*; + // #[ doc( inline ) ] + // pub use private:: + // { + // // err, + // // return_err, + // ErrorTrait, + // // BasicError, + // }; + #[ doc( inline ) ] - pub use private:: - { - err, - return_err, - ErrorTrait, - BasicError, - }; + pub use assert::prelude::*; + + #[ cfg( feature = "error_untyped" ) ] + #[ doc( inline ) ] + pub use untyped::prelude::*; + + #[ cfg( feature = "error_typed" ) ] + #[ doc( inline ) ] + pub use typed::prelude::*; } diff --git a/module/core/error_tools/src/typed.rs b/module/core/error_tools/src/error/typed.rs similarity index 100% rename from module/core/error_tools/src/typed.rs rename to module/core/error_tools/src/error/typed.rs diff --git a/module/core/error_tools/src/untyped.rs b/module/core/error_tools/src/error/untyped.rs similarity index 86% rename from module/core/error_tools/src/untyped.rs rename to module/core/error_tools/src/error/untyped.rs index df16162bab..17e335473a 100644 --- a/module/core/error_tools/src/untyped.rs +++ b/module/core/error_tools/src/error/untyped.rs @@ -13,6 +13,7 @@ pub use own::*; pub mod own { use super::*; + #[ doc( inline ) ] pub use orphan::*; @@ -24,6 +25,10 @@ pub mod own Error, Ok, Result, + format_err, + bail as return_err, + ensure, + bail, }; } @@ -39,13 +44,13 @@ pub mod orphan #[ doc( inline ) ] pub use exposed::*; - #[ doc( inline ) ] - pub use ::anyhow:: - { - format_err, - ensure, - bail, - }; + // #[ doc( inline ) ] + // pub use ::anyhow:: + // { + // format_err, + // ensure, + // bail, + // }; } diff --git a/module/core/error_tools/src/lib.rs b/module/core/error_tools/src/lib.rs index 30a25af03b..9ba2500d42 100644 --- a/module/core/error_tools/src/lib.rs +++ b/module/core/error_tools/src/lib.rs @@ -4,10 +4,6 @@ #![ doc( html_root_url = "https://docs.rs/error_tools/latest/error_tools/" ) ] #![ doc = include_str!( concat!( env!( "CARGO_MANIFEST_DIR" ), "/", "Readme.md" ) ) ] -/// Assertions. -#[ cfg( feature = "enabled" ) ] -pub mod assert; - /// Alias for std::error::BasicError. #[ cfg( feature = "enabled" ) ] #[ cfg( not( feature = "no_std" ) ) ] @@ -19,27 +15,15 @@ pub mod dependency { #[ doc( inline ) ] - #[ allow( unused_imports ) ] #[ cfg( feature = "error_typed" ) ] pub use ::thiserror; #[ doc( inline ) ] - #[ allow( unused_imports ) ] #[ cfg( feature = "error_untyped" ) ] pub use ::anyhow; } -#[ cfg( feature = "enabled" ) ] -#[ cfg( feature = "error_typed" ) ] -/// Typed exceptions handling mechanism. -pub mod typed; - -#[ cfg( feature = "enabled" ) ] -#[ cfg( feature = "error_untyped" ) ] -/// Untyped exceptions handling mechanism. -pub mod untyped; - #[ cfg( feature = "enabled" ) ] #[ doc( inline ) ] #[ allow( unused_imports ) ] @@ -51,27 +35,9 @@ pub use own::*; pub mod own { use super::*; - #[ allow( unused_imports ) ] - use super::*; - - #[ doc( inline ) ] - #[ allow( unused_imports ) ] - pub use assert::orphan::*; - #[ cfg( not( feature = "no_std" ) ) ] #[ doc( inline ) ] - #[ allow( unused_imports ) ] - pub use error::orphan::*; - - #[ cfg( feature = "error_untyped" ) ] - #[ doc( inline ) ] - #[ allow( unused_imports ) ] - pub use untyped::orphan::*; - - #[ cfg( feature = "error_typed" ) ] - #[ doc( inline ) ] - #[ allow( unused_imports ) ] - pub use typed::orphan::*; + pub use error::own::*; } @@ -85,6 +51,9 @@ pub mod orphan #[ doc( inline ) ] pub use exposed::*; + #[ doc( inline ) ] + pub use error::orphan::*; + } /// Exposed namespace of the module. @@ -95,28 +64,11 @@ pub mod exposed use super::*; #[ doc( inline ) ] - #[ allow( unused_imports ) ] pub use prelude::*; #[ doc( inline ) ] - #[ allow( unused_imports ) ] - pub use assert::exposed::*; - - #[ cfg( not( feature = "no_std" ) ) ] - #[ doc( inline ) ] - #[ allow( unused_imports ) ] pub use error::exposed::*; - #[ cfg( feature = "error_untyped" ) ] - #[ doc( inline ) ] - #[ allow( unused_imports ) ] - pub use untyped::exposed::*; - - #[ cfg( feature = "error_typed" ) ] - #[ doc( inline ) ] - #[ allow( unused_imports ) ] - pub use typed::exposed::*; - } /// Prelude to use essentials: `use my_module::prelude::*`. @@ -125,26 +77,8 @@ pub mod exposed pub mod prelude { use super::*; - #[ allow( unused_imports ) ] - use super::*; - - #[ doc( inline ) ] - #[ allow( unused_imports ) ] - pub use assert::prelude::*; - #[ cfg( not( feature = "no_std" ) ) ] #[ doc( inline ) ] - #[ allow( unused_imports ) ] pub use error::prelude::*; - #[ cfg( feature = "error_untyped" ) ] - #[ doc( inline ) ] - #[ allow( unused_imports ) ] - pub use untyped::prelude::*; - - #[ cfg( feature = "error_typed" ) ] - #[ doc( inline ) ] - #[ allow( unused_imports ) ] - pub use typed::prelude::*; - } diff --git a/module/core/error_tools/src/result.rs b/module/core/error_tools/src/result.rs deleted file mode 100644 index ea5a3c3b48..0000000000 --- a/module/core/error_tools/src/result.rs +++ /dev/null @@ -1,43 +0,0 @@ -// /// Internal namespace. -// mod private -// { -// use crate::error::BasicError; -// -// /// Type alias for Result with BasicError. -// pub type Result< T, E = BasicError > = std::result::Result< T, E >; -// } -// -// /// Own namespace of the module. -// pub mod own -// { -// #[ doc( inline ) ] -// #[ allow( unused_imports ) ] -// pub use orphan::*; -// } -// -// #[ doc( inline ) ] -// #[ allow( unused_imports ) ] -// pub use own::*; -// -// /// Shared with parent namespace of the module -// pub mod orphan -// { -// #[ doc( inline ) ] -// #[ allow( unused_imports ) ] -// pub use exposed::*; -// } -// -// /// Exposed namespace of the module. -// pub mod exposed -// { -// #[ doc( inline ) ] -// #[ allow( unused_imports ) ] -// pub use prelude::*; -// } -// -// /// Prelude to use essentials: `use my_module::prelude::*`. -// pub mod prelude -// { -// pub use private::Result; -// } -// diff --git a/module/core/error_tools/tests/inc/basic_test.rs b/module/core/error_tools/tests/inc/basic_test.rs index 61462b17f9..32cb4c4bba 100644 --- a/module/core/error_tools/tests/inc/basic_test.rs +++ b/module/core/error_tools/tests/inc/basic_test.rs @@ -1,5 +1,5 @@ #![ allow( deprecated ) ] -#![ allow( unused_imports ) ] +// #![ allow( unused_imports ) ] use super::*; // @@ -7,117 +7,116 @@ use super::*; #[ cfg( not( feature = "no_std" ) ) ] tests_impls! { - fn basic() - { - use std::error::Error; - - // test.case( "basic" ); - - let err1 = the_module::BasicError::new( "Some error" ); - a_id!( err1.to_string(), "Some error" ); - a_id!( err1.description(), "Some error" ); - a_id!( err1.msg(), "Some error" ); - a_id!( format!( "err1 : {}", err1 ), "err1 : Some error" ); - - // test.case( "compare" ); - - let err1 = the_module::BasicError::new( "Some error" ); - let err2 = the_module::BasicError::new( "Some error" ); - a_id!( err1, err2 ); - a_id!( err1.description(), err2.description() ); - - // test.case( "clone" ); - - let err1 = the_module::BasicError::new( "Some error" ); - let err2 = err1.clone(); - a_id!( err1, err2 ); - a_id!( err1.description(), err2.description() ); - } - - // - - fn use1() - { - use std::error::Error as ErrorTrait; - use the_module::BasicError as Error; - - // test.case( "basic" ); - - let err1 = Error::new( "Some error" ); - a_id!( err1.to_string(), "Some error" ); - a_id!( err1.description(), "Some error" ); - a_id!( err1.msg(), "Some error" ); - a_id!( format!( "err1 : {}", err1 ), "err1 : Some error" ); - } - - // - - fn use2() - { - use the_module::{ BasicError, ErrorTrait }; - - // test.case( "basic" ); - - let err1 = BasicError::new( "Some error" ); - a_id!( err1.to_string(), "Some error" ); - a_id!( err1.description(), "Some error" ); - a_id!( err1.msg(), "Some error" ); - a_id!( format!( "err1 : {}", err1 ), "err1 : Some error" ); - } - - // - - fn use3() - { - use std::error::Error; - - // test.case( "basic" ); - - let err1 = the_module::BasicError::new( "Some error" ); - a_id!( err1.to_string(), "Some error" ); - a_id!( err1.description(), "Some error" ); - a_id!( err1.msg(), "Some error" ); - a_id!( format!( "err1 : {}", err1 ), "err1 : Some error" ); - } - - // - - fn err_basic() - { - // test.case( "basic" ); - let err : the_module::BasicError = the_module::err!( "abc" ); - a_id!( err.to_string(), "abc" ); - - // test.case( "with args" ); - let err : the_module::BasicError = the_module::err!( "abc{}{}", "def", "ghi" ); - a_id!( err.to_string(), "abcdefghi" ); - } +// fn basic() +// { +// use std::error::Error; +// +// // test.case( "basic" ); +// +// let err1 = the_module::BasicError::new( "Some error" ); +// a_id!( err1.to_string(), "Some error" ); +// a_id!( err1.description(), "Some error" ); +// a_id!( err1.msg(), "Some error" ); +// a_id!( format!( "err1 : {}", err1 ), "err1 : Some error" ); +// +// // test.case( "compare" ); +// +// let err1 = the_module::BasicError::new( "Some error" ); +// let err2 = the_module::BasicError::new( "Some error" ); +// a_id!( err1, err2 ); +// a_id!( err1.description(), err2.description() ); +// +// // test.case( "clone" ); +// +// let err1 = the_module::BasicError::new( "Some error" ); +// let err2 = err1.clone(); +// a_id!( err1, err2 ); +// a_id!( err1.description(), err2.description() ); +// } // - fn sample() - { - #[ cfg( not( feature = "no_std" ) ) ] - fn f1() -> the_module::untyped::Result< () > - { - let _read = std::fs::read_to_string( "Cargo.toml" )?; - Err( the_module::BasicError::new( "Some error" ).into() ) - // the_module::BasicError::new( "Some error" ).into() - // zzz : make it working maybe - } - - #[ cfg( not( feature = "no_std" ) ) ] - { - let err = f1(); - println!( "{err:#?}" ); - // < Err( - // < BasicError { - // < msg: "Some error", - // < }, - // < ) - } - } - +// fn use1() +// { +// use std::error::Error as ErrorTrait; +// use the_module::BasicError as Error; +// +// // test.case( "basic" ); +// +// let err1 = Error::new( "Some error" ); +// a_id!( err1.to_string(), "Some error" ); +// a_id!( err1.description(), "Some error" ); +// a_id!( err1.msg(), "Some error" ); +// a_id!( format!( "err1 : {}", err1 ), "err1 : Some error" ); +// } +// +// // +// +// fn use2() +// { +// use the_module::{ BasicError, ErrorTrait }; +// +// // test.case( "basic" ); +// +// let err1 = BasicError::new( "Some error" ); +// a_id!( err1.to_string(), "Some error" ); +// a_id!( err1.description(), "Some error" ); +// a_id!( err1.msg(), "Some error" ); +// a_id!( format!( "err1 : {}", err1 ), "err1 : Some error" ); +// } +// +// // +// +// fn use3() +// { +// use std::error::Error; +// +// // test.case( "basic" ); +// +// let err1 = the_module::BasicError::new( "Some error" ); +// a_id!( err1.to_string(), "Some error" ); +// a_id!( err1.description(), "Some error" ); +// a_id!( err1.msg(), "Some error" ); +// a_id!( format!( "err1 : {}", err1 ), "err1 : Some error" ); +// } +// +// // +// +// fn err_basic() +// { +// // test.case( "basic" ); +// let err : the_module::BasicError = the_module::err!( "abc" ); +// a_id!( err.to_string(), "abc" ); +// +// // test.case( "with args" ); +// let err : the_module::BasicError = the_module::err!( "abc{}{}", "def", "ghi" ); +// a_id!( err.to_string(), "abcdefghi" ); +// } +// +// // +// +// fn sample() +// { +// #[ cfg( not( feature = "no_std" ) ) ] +// fn f1() -> the_module::untyped::Result< () > +// { +// let _read = std::fs::read_to_string( "Cargo.toml" )?; +// Err( the_module::BasicError::new( "Some error" ).into() ) +// // the_module::BasicError::new( "Some error" ).into() +// // zzz : make it working maybe +// } +// +// #[ cfg( not( feature = "no_std" ) ) ] +// { +// let err = f1(); +// println!( "{err:#?}" ); +// // < Err( +// // < BasicError { +// // < msg: "Some error", +// // < }, +// // < ) +// } +// } } @@ -126,10 +125,10 @@ tests_impls! #[ cfg( not( feature = "no_std" ) ) ] tests_index! { - basic, - use1, - use2, - use3, - err_basic, - sample, + // basic, + // use1, + // use2, + // use3, + // err_basic, + // sample, } diff --git a/module/core/error_tools/tests/inc/mod.rs b/module/core/error_tools/tests/inc/mod.rs index 256c6e20bd..dc239e680e 100644 --- a/module/core/error_tools/tests/inc/mod.rs +++ b/module/core/error_tools/tests/inc/mod.rs @@ -1,8 +1,10 @@ #[ allow( unused_imports ) ] use super::*; -mod assert_test; mod basic_test; +mod namespace_test; + +mod assert_test; #[ cfg( not( feature = "no_std" ) ) ] mod err_with_test; mod untyped_test; diff --git a/module/core/error_tools/tests/inc/namespace_test.rs b/module/core/error_tools/tests/inc/namespace_test.rs new file mode 100644 index 0000000000..92e96b0610 --- /dev/null +++ b/module/core/error_tools/tests/inc/namespace_test.rs @@ -0,0 +1,12 @@ +use super::*; + +#[ test ] +fn exposed_main_namespace() +{ + + the_module::error::debug_assert_id!( 1, 1 ); + the_module::exposed::error::debug_assert_id!( 1, 1 ); + use the_module::exposed::*; + error::debug_assert_id!( 1, 1 ); + +} \ No newline at end of file diff --git a/module/core/error_tools/tests/tests.rs b/module/core/error_tools/tests/tests.rs index 0374c10521..f217bd0119 100644 --- a/module/core/error_tools/tests/tests.rs +++ b/module/core/error_tools/tests/tests.rs @@ -1,7 +1,6 @@ +#![ allow( unused_imports ) ] -#[ allow( unused_imports ) ] use error_tools as the_module; -#[ allow( unused_imports ) ] use test_tools::exposed::*; mod inc; diff --git a/module/core/meta_tools/Cargo.toml b/module/core/meta_tools/Cargo.toml index 0fc0b3c61d..842fa3143e 100644 --- a/module/core/meta_tools/Cargo.toml +++ b/module/core/meta_tools/Cargo.toml @@ -32,7 +32,7 @@ default = [ "meta_for_each", "meta_impls_index", # "meta_mod_interface", - "meta_constructors", + # "meta_constructors", "meta_idents_concat", ] full = [ @@ -40,7 +40,7 @@ full = [ "meta_for_each", "meta_impls_index", # "meta_mod_interface", - "meta_constructors", + # "meta_constructors", "meta_idents_concat", ] no_std = [] @@ -52,14 +52,14 @@ meta_impls_index = [ "impls_index/enabled" ] meta_mod_interface = [ "mod_interface/enabled" ] # xxx : qqq : make mod_interface optional maybe -meta_constructors = [ "literally" ] +# meta_constructors = [ "literally" ] meta_idents_concat = [ "paste" ] [dependencies] -## external -literally = { version = "~0.1.3", optional = true, default-features = false } -paste = { version = "~1.0.14", optional = true, default-features = false } +# ## external +# literally = { version = "~0.1.3", optional = true, default-features = false } +paste = { workspace = true, optional = true, default-features = false } ## internal impls_index = { workspace = true } diff --git a/module/core/meta_tools/Readme.md b/module/core/meta_tools/Readme.md index 0d472b069f..c43c980e94 100644 --- a/module/core/meta_tools/Readme.md +++ b/module/core/meta_tools/Readme.md @@ -7,21 +7,6 @@ Collection of general purpose meta tools. -### Basic use-case :: variadic constructor of collections - -Among other useful meta tools the module aggregates variadic constructors of collections. For example macro `hmap!` for constructing a hash map. - - - -```rust -use meta_tools::*; - -let meta_map = hmap! { 3 => 13 }; -let mut std_map = std::collections::HashMap::new(); -std_map.insert( 3, 13 ); -assert_eq!( meta_map, std_map ); -``` - ### Basic Use Case :: function-style call Apply a macro for each element of a list. diff --git a/module/core/meta_tools/examples/meta_tools_trivial.rs b/module/core/meta_tools/examples/meta_tools_trivial.rs index 75d17ddace..983e55c9d6 100644 --- a/module/core/meta_tools/examples/meta_tools_trivial.rs +++ b/module/core/meta_tools/examples/meta_tools_trivial.rs @@ -3,8 +3,10 @@ use meta_tools::*; fn main() { - let meta_map = hmap! { 3 => 13 }; - let mut std_map = std::collections::HashMap::new(); - std_map.insert( 3, 13 ); - assert_eq!( meta_map, std_map ); + for_each!( dbg, "a", "b", "c" ); + + // generates + dbg!( "a" ); + dbg!( "b" ); + dbg!( "c" ); } diff --git a/module/core/meta_tools/src/lib.rs b/module/core/meta_tools/src/lib.rs index 352f7e0f3b..391eaf5050 100644 --- a/module/core/meta_tools/src/lib.rs +++ b/module/core/meta_tools/src/lib.rs @@ -17,8 +17,8 @@ pub mod dependency #[ cfg( feature = "meta_impls_index" ) ] pub use ::impls_index; - #[ cfg( feature = "meta_constructors" ) ] - pub use ::literally; + // #[ cfg( feature = "meta_constructors" ) ] + // pub use ::literally; #[ cfg( feature = "meta_idents_concat" ) ] pub use ::paste; diff --git a/module/core/meta_tools/src/meta.rs b/module/core/meta_tools/src/meta.rs index e05ad7deec..1cda0b024d 100644 --- a/module/core/meta_tools/src/meta.rs +++ b/module/core/meta_tools/src/meta.rs @@ -22,8 +22,8 @@ mod_interface::mod_interface! // #[ cfg( feature = "meta_mod_interface" ) ] prelude use ::mod_interface::mod_interface; - #[ cfg( feature = "meta_constructors" ) ] - prelude use ::literally::*; + // #[ cfg( feature = "meta_constructors" ) ] + // prelude use ::literally::*; #[ cfg( feature = "meta_idents_concat" ) ] prelude use ::paste::paste as meta_idents_concat; diff --git a/module/core/meta_tools/tests/inc/meta_constructor_test.rs b/module/core/meta_tools/tests/inc/meta_constructor_test.rs index acee680259..d4cffdf307 100644 --- a/module/core/meta_tools/tests/inc/meta_constructor_test.rs +++ b/module/core/meta_tools/tests/inc/meta_constructor_test.rs @@ -1,50 +1,50 @@ -use super::*; - -// - -tests_impls! -{ - - fn hash_map() - { - - // test.case( "empty" ); - let got : std::collections::HashMap< i32, i32 > = the_module::hmap!{}; - let exp = std::collections::HashMap::new(); - a_id!( got, exp ); - - // test.case( "single entry" ); - let got = the_module::hmap!{ 3 => 13 }; - let mut exp = std::collections::HashMap::new(); - exp.insert( 3, 13 ); - a_id!( got, exp ); - - } - - // - - - fn hash_set() - { - - // test.case( "empty" ); - let got : std::collections::HashSet< i32 > = the_module::hset!{}; - let exp = std::collections::HashSet::new(); - a_id!( got, exp ); - - // test.case( "single entry" ); - let got = the_module::hset!{ 13 }; - let mut exp = std::collections::HashSet::new(); - exp.insert( 13 ); - a_id!( got, exp ); - - } -} - -// - -tests_index! -{ - hash_map, - hash_set, -} +// use super::*; +// +// // +// +// tests_impls! +// { +// +// fn hash_map() +// { +// +// // test.case( "empty" ); +// let got : std::collections::HashMap< i32, i32 > = the_module::hmap!{}; +// let exp = std::collections::HashMap::new(); +// a_id!( got, exp ); +// +// // test.case( "single entry" ); +// let got = the_module::hmap!{ 3 => 13 }; +// let mut exp = std::collections::HashMap::new(); +// exp.insert( 3, 13 ); +// a_id!( got, exp ); +// +// } +// +// // +// +// +// fn hash_set() +// { +// +// // test.case( "empty" ); +// let got : std::collections::HashSet< i32 > = the_module::hset!{}; +// let exp = std::collections::HashSet::new(); +// a_id!( got, exp ); +// +// // test.case( "single entry" ); +// let got = the_module::hset!{ 13 }; +// let mut exp = std::collections::HashSet::new(); +// exp.insert( 13 ); +// a_id!( got, exp ); +// +// } +// } +// +// // +// +// tests_index! +// { +// hash_map, +// hash_set, +// } diff --git a/module/core/meta_tools/tests/inc/mod.rs b/module/core/meta_tools/tests/inc/mod.rs index 9fc942d2c2..98e402d4c3 100644 --- a/module/core/meta_tools/tests/inc/mod.rs +++ b/module/core/meta_tools/tests/inc/mod.rs @@ -1,17 +1,17 @@ #[ allow( unused_imports ) ] use super::*; -#[ cfg( any( feature = "meta_constructors", feature = "meta_constructors" ) ) ] -mod meta_constructor_test; +// #[ cfg( any( feature = "meta_constructors", feature = "meta_constructors" ) ) ] +// mod meta_constructor_test; #[ cfg( any( feature = "meta_idents_concat", feature = "meta_idents_concat" ) ) ] mod indents_concat_test; -#[ cfg( any( feature = "for_each", feature = "meta_for_each" ) ) ] +#[ cfg( any( feature = "meta_for_each" ) ) ] #[ path = "../../../for_each/tests/inc/mod.rs" ] mod for_each_test; -#[ cfg( any( feature = "impls_index", feature = "meta_impls_index" ) ) ] +#[ cfg( any( feature = "meta_impls_index" ) ) ] #[ path = "../../../impls_index/tests/inc/mod.rs" ] mod impls_index; diff --git a/module/core/mod_interface/Readme.md b/module/core/mod_interface/Readme.md index 47115efb68..d9b6f668c5 100644 --- a/module/core/mod_interface/Readme.md +++ b/module/core/mod_interface/Readme.md @@ -25,7 +25,7 @@ This example demonstrates how to use the `mod_interface` crate to organize a Rus use mod_interface::mod_interface; // Define a module named `child`. -mod child +pub mod child { // Define a private namespace for all its items. @@ -162,6 +162,7 @@ pub mod own use super::*; pub use orphan::*; pub use super::child::orphan::*; + pub use super::child; } /// Orphan namespace of the module. diff --git a/module/core/mod_interface/examples/mod_interface_trivial/src/main.rs b/module/core/mod_interface/examples/mod_interface_trivial/src/main.rs index 420a6c9fb4..f834f8b12f 100644 --- a/module/core/mod_interface/examples/mod_interface_trivial/src/main.rs +++ b/module/core/mod_interface/examples/mod_interface_trivial/src/main.rs @@ -3,7 +3,7 @@ use mod_interface::mod_interface; /// Children. -mod child; +pub mod child; // Priave namespaces is necessary. mod private {} diff --git a/module/core/mod_interface/tests/inc/derive/attr_debug/mod.rs b/module/core/mod_interface/tests/inc/derive/attr_debug/mod.rs index 57b54aff39..7b425682cc 100644 --- a/module/core/mod_interface/tests/inc/derive/attr_debug/mod.rs +++ b/module/core/mod_interface/tests/inc/derive/attr_debug/mod.rs @@ -3,7 +3,7 @@ use super::*; mod private {} -mod_interface! +the_module::mod_interface! { // #![ debug ] diff --git a/module/core/mod_interface/tests/inc/derive/layer/mod.rs b/module/core/mod_interface/tests/inc/derive/layer/mod.rs index d8e79fb20b..8a567560f7 100644 --- a/module/core/mod_interface/tests/inc/derive/layer/mod.rs +++ b/module/core/mod_interface/tests/inc/derive/layer/mod.rs @@ -6,7 +6,7 @@ mod private { } -mod_interface! +the_module::mod_interface! { /// layer_a diff --git a/module/core/mod_interface/tests/inc/derive/layer_have_layer/layer_a.rs b/module/core/mod_interface/tests/inc/derive/layer_have_layer/layer_a.rs index dfffa8d8a8..082005e6be 100644 --- a/module/core/mod_interface/tests/inc/derive/layer_have_layer/layer_a.rs +++ b/module/core/mod_interface/tests/inc/derive/layer_have_layer/layer_a.rs @@ -33,7 +33,7 @@ mod private // -mod_interface! +the_module::mod_interface! { // orphan use super::private:: diff --git a/module/core/mod_interface/tests/inc/derive/layer_have_layer/layer_b.rs b/module/core/mod_interface/tests/inc/derive/layer_have_layer/layer_b.rs index 9f17f61637..1d265d3c4f 100644 --- a/module/core/mod_interface/tests/inc/derive/layer_have_layer/layer_b.rs +++ b/module/core/mod_interface/tests/inc/derive/layer_have_layer/layer_b.rs @@ -39,7 +39,7 @@ pub struct SubStruct2 // -mod_interface! +the_module::mod_interface! { own use layer_b_own; diff --git a/module/core/mod_interface/tests/inc/derive/layer_have_layer/mod.rs b/module/core/mod_interface/tests/inc/derive/layer_have_layer/mod.rs index 6cf3f6db29..219360f435 100644 --- a/module/core/mod_interface/tests/inc/derive/layer_have_layer/mod.rs +++ b/module/core/mod_interface/tests/inc/derive/layer_have_layer/mod.rs @@ -11,7 +11,7 @@ mod private { } -mod_interface! +the_module::mod_interface! { /// layer_a diff --git a/module/core/mod_interface/tests/inc/derive/layer_have_layer_cfg/layer_a.rs b/module/core/mod_interface/tests/inc/derive/layer_have_layer_cfg/layer_a.rs index dfffa8d8a8..082005e6be 100644 --- a/module/core/mod_interface/tests/inc/derive/layer_have_layer_cfg/layer_a.rs +++ b/module/core/mod_interface/tests/inc/derive/layer_have_layer_cfg/layer_a.rs @@ -33,7 +33,7 @@ mod private // -mod_interface! +the_module::mod_interface! { // orphan use super::private:: diff --git a/module/core/mod_interface/tests/inc/derive/layer_have_layer_cfg/layer_b.rs b/module/core/mod_interface/tests/inc/derive/layer_have_layer_cfg/layer_b.rs index 9f17f61637..1d265d3c4f 100644 --- a/module/core/mod_interface/tests/inc/derive/layer_have_layer_cfg/layer_b.rs +++ b/module/core/mod_interface/tests/inc/derive/layer_have_layer_cfg/layer_b.rs @@ -39,7 +39,7 @@ pub struct SubStruct2 // -mod_interface! +the_module::mod_interface! { own use layer_b_own; diff --git a/module/core/mod_interface/tests/inc/derive/layer_have_layer_cfg/mod.rs b/module/core/mod_interface/tests/inc/derive/layer_have_layer_cfg/mod.rs index 09c564f64b..b0fc4d5d70 100644 --- a/module/core/mod_interface/tests/inc/derive/layer_have_layer_cfg/mod.rs +++ b/module/core/mod_interface/tests/inc/derive/layer_have_layer_cfg/mod.rs @@ -11,7 +11,7 @@ mod private { } -mod_interface! +the_module::mod_interface! { /// layer_a diff --git a/module/core/mod_interface/tests/inc/derive/layer_have_layer_separate_use/layer_a.rs b/module/core/mod_interface/tests/inc/derive/layer_have_layer_separate_use/layer_a.rs index 8d8d6b1faf..c71e0af7d2 100644 --- a/module/core/mod_interface/tests/inc/derive/layer_have_layer_separate_use/layer_a.rs +++ b/module/core/mod_interface/tests/inc/derive/layer_have_layer_separate_use/layer_a.rs @@ -33,7 +33,7 @@ mod private // -mod_interface! +the_module::mod_interface! { own use { layer_a_own }; diff --git a/module/core/mod_interface/tests/inc/derive/layer_have_layer_separate_use/layer_b.rs b/module/core/mod_interface/tests/inc/derive/layer_have_layer_separate_use/layer_b.rs index 9f17f61637..1d265d3c4f 100644 --- a/module/core/mod_interface/tests/inc/derive/layer_have_layer_separate_use/layer_b.rs +++ b/module/core/mod_interface/tests/inc/derive/layer_have_layer_separate_use/layer_b.rs @@ -39,7 +39,7 @@ pub struct SubStruct2 // -mod_interface! +the_module::mod_interface! { own use layer_b_own; diff --git a/module/core/mod_interface/tests/inc/derive/layer_have_layer_separate_use/mod.rs b/module/core/mod_interface/tests/inc/derive/layer_have_layer_separate_use/mod.rs index c34df1c831..0d2ec22d26 100644 --- a/module/core/mod_interface/tests/inc/derive/layer_have_layer_separate_use/mod.rs +++ b/module/core/mod_interface/tests/inc/derive/layer_have_layer_separate_use/mod.rs @@ -12,11 +12,11 @@ mod private } /// layer_a -mod layer_a; +pub mod layer_a; /// layer_b -mod layer_b; +pub mod layer_b; -mod_interface! +the_module::mod_interface! { /// layer_a diff --git a/module/core/mod_interface/tests/inc/derive/layer_have_layer_separate_use_two/layer_a.rs b/module/core/mod_interface/tests/inc/derive/layer_have_layer_separate_use_two/layer_a.rs index 8d8d6b1faf..c71e0af7d2 100644 --- a/module/core/mod_interface/tests/inc/derive/layer_have_layer_separate_use_two/layer_a.rs +++ b/module/core/mod_interface/tests/inc/derive/layer_have_layer_separate_use_two/layer_a.rs @@ -33,7 +33,7 @@ mod private // -mod_interface! +the_module::mod_interface! { own use { layer_a_own }; diff --git a/module/core/mod_interface/tests/inc/derive/layer_have_layer_separate_use_two/layer_b.rs b/module/core/mod_interface/tests/inc/derive/layer_have_layer_separate_use_two/layer_b.rs index 9f17f61637..1d265d3c4f 100644 --- a/module/core/mod_interface/tests/inc/derive/layer_have_layer_separate_use_two/layer_b.rs +++ b/module/core/mod_interface/tests/inc/derive/layer_have_layer_separate_use_two/layer_b.rs @@ -39,7 +39,7 @@ pub struct SubStruct2 // -mod_interface! +the_module::mod_interface! { own use layer_b_own; diff --git a/module/core/mod_interface/tests/inc/derive/layer_have_layer_separate_use_two/mod.rs b/module/core/mod_interface/tests/inc/derive/layer_have_layer_separate_use_two/mod.rs index 4774b23347..c20f8d770a 100644 --- a/module/core/mod_interface/tests/inc/derive/layer_have_layer_separate_use_two/mod.rs +++ b/module/core/mod_interface/tests/inc/derive/layer_have_layer_separate_use_two/mod.rs @@ -12,11 +12,11 @@ mod private } /// layer_a -mod layer_a; +pub mod layer_a; /// layer_b -mod layer_b; +pub mod layer_b; -mod_interface! +the_module::mod_interface! { // zzz : test with `layer { layer_a, layer_a };` diff --git a/module/core/mod_interface/tests/inc/derive/layer_have_mod_cfg/mod.rs b/module/core/mod_interface/tests/inc/derive/layer_have_mod_cfg/mod.rs index 2188f4a6b3..38ff58d0eb 100644 --- a/module/core/mod_interface/tests/inc/derive/layer_have_mod_cfg/mod.rs +++ b/module/core/mod_interface/tests/inc/derive/layer_have_mod_cfg/mod.rs @@ -11,7 +11,7 @@ mod private { } -mod_interface! +the_module::mod_interface! { /// mod_a diff --git a/module/core/mod_interface/tests/inc/derive/layer_unknown_vis/trybuild.stderr b/module/core/mod_interface/tests/inc/derive/layer_unknown_vis/trybuild.stderr index 2d2aa78ea8..125e29bbdb 100644 --- a/module/core/mod_interface/tests/inc/derive/layer_unknown_vis/trybuild.stderr +++ b/module/core/mod_interface/tests/inc/derive/layer_unknown_vis/trybuild.stderr @@ -1,4 +1,4 @@ -error: expected one of: `mod`, `use`, `layer` +error: expected one of: `mod`, `use`, `layer`, `reuse` --> tests/inc/derive/layer_unknown_vis/mod.rs | | xyz layer layer_a; diff --git a/module/core/mod_interface/tests/inc/derive/layer_use_cfg/layer_a.rs b/module/core/mod_interface/tests/inc/derive/layer_use_cfg/layer_a.rs index dfffa8d8a8..082005e6be 100644 --- a/module/core/mod_interface/tests/inc/derive/layer_use_cfg/layer_a.rs +++ b/module/core/mod_interface/tests/inc/derive/layer_use_cfg/layer_a.rs @@ -33,7 +33,7 @@ mod private // -mod_interface! +the_module::mod_interface! { // orphan use super::private:: diff --git a/module/core/mod_interface/tests/inc/derive/layer_use_cfg/layer_b.rs b/module/core/mod_interface/tests/inc/derive/layer_use_cfg/layer_b.rs index 9f17f61637..1d265d3c4f 100644 --- a/module/core/mod_interface/tests/inc/derive/layer_use_cfg/layer_b.rs +++ b/module/core/mod_interface/tests/inc/derive/layer_use_cfg/layer_b.rs @@ -39,7 +39,7 @@ pub struct SubStruct2 // -mod_interface! +the_module::mod_interface! { own use layer_b_own; diff --git a/module/core/mod_interface/tests/inc/derive/layer_use_cfg/mod.rs b/module/core/mod_interface/tests/inc/derive/layer_use_cfg/mod.rs index 0c730db3a7..d9eedf0a3e 100644 --- a/module/core/mod_interface/tests/inc/derive/layer_use_cfg/mod.rs +++ b/module/core/mod_interface/tests/inc/derive/layer_use_cfg/mod.rs @@ -12,12 +12,13 @@ mod private } /// layer_a -mod layer_a; +pub mod layer_a; /// layer_b -mod layer_b; +pub mod layer_b; -mod_interface! +the_module::mod_interface! { + // #![ debug ] /// layer_a use super::layer_a; @@ -33,4 +34,3 @@ mod_interface! // include!( "../../only_test/layer_simple_only_test.rs" ); - diff --git a/module/core/mod_interface/tests/inc/derive/layer_use_macro/layer_a.rs b/module/core/mod_interface/tests/inc/derive/layer_use_macro/layer_a.rs index 12db22ecfc..b37c839cd0 100644 --- a/module/core/mod_interface/tests/inc/derive/layer_use_macro/layer_a.rs +++ b/module/core/mod_interface/tests/inc/derive/layer_use_macro/layer_a.rs @@ -35,7 +35,7 @@ mod private // -mod_interface! +the_module::mod_interface! { // exposed( crate ) use macro1; diff --git a/module/core/mod_interface/tests/inc/derive/layer_use_macro/mod.rs b/module/core/mod_interface/tests/inc/derive/layer_use_macro/mod.rs index 1bb9569c90..67a972b145 100644 --- a/module/core/mod_interface/tests/inc/derive/layer_use_macro/mod.rs +++ b/module/core/mod_interface/tests/inc/derive/layer_use_macro/mod.rs @@ -11,7 +11,7 @@ mod private { } -mod_interface! +the_module::mod_interface! { /// layer_a diff --git a/module/core/mod_interface/tests/inc/derive/micro_modules/mod.rs b/module/core/mod_interface/tests/inc/derive/micro_modules/mod.rs index 80d4c7218a..57adff6ff2 100644 --- a/module/core/mod_interface/tests/inc/derive/micro_modules/mod.rs +++ b/module/core/mod_interface/tests/inc/derive/micro_modules/mod.rs @@ -6,7 +6,7 @@ mod private { } -mod_interface! +the_module::mod_interface! { // #![ debug ] diff --git a/module/core/mod_interface/tests/inc/derive/micro_modules_bad_vis/trybuild.stderr b/module/core/mod_interface/tests/inc/derive/micro_modules_bad_vis/trybuild.stderr index b84160eec0..d73c3c374c 100644 --- a/module/core/mod_interface/tests/inc/derive/micro_modules_bad_vis/trybuild.stderr +++ b/module/core/mod_interface/tests/inc/derive/micro_modules_bad_vis/trybuild.stderr @@ -1,4 +1,4 @@ -error: To include a non-standard module use either [ private, protected, orphan, exposed, prelude ] visibility: +error: To include a non-standard module use either [ private, own, orphan, exposed, prelude ] visibility: #[doc = " mod_exposed"] pub mod mod_exposed; --> tests/inc/derive/micro_modules_bad_vis/mod.rs | diff --git a/module/core/mod_interface/tests/inc/derive/micro_modules_glob/mod.rs b/module/core/mod_interface/tests/inc/derive/micro_modules_glob/mod.rs index 40ccb61f64..b9c78c9198 100644 --- a/module/core/mod_interface/tests/inc/derive/micro_modules_glob/mod.rs +++ b/module/core/mod_interface/tests/inc/derive/micro_modules_glob/mod.rs @@ -10,7 +10,7 @@ mod private // -crate::mod_interface! +crate::the_module::mod_interface! { own use { diff --git a/module/core/mod_interface/tests/inc/derive/micro_modules_two/mod.rs b/module/core/mod_interface/tests/inc/derive/micro_modules_two/mod.rs index 9071caf2d1..c62e6f7e18 100644 --- a/module/core/mod_interface/tests/inc/derive/micro_modules_two/mod.rs +++ b/module/core/mod_interface/tests/inc/derive/micro_modules_two/mod.rs @@ -6,7 +6,7 @@ mod private { } -mod_interface! +the_module::mod_interface! { /// mod_own1 diff --git a/module/core/mod_interface/tests/inc/derive/micro_modules_two_joined/mod.rs b/module/core/mod_interface/tests/inc/derive/micro_modules_two_joined/mod.rs index ced5712479..de2d1c2e88 100644 --- a/module/core/mod_interface/tests/inc/derive/micro_modules_two_joined/mod.rs +++ b/module/core/mod_interface/tests/inc/derive/micro_modules_two_joined/mod.rs @@ -6,7 +6,7 @@ mod private { } -mod_interface! +the_module::mod_interface! { own mod diff --git a/module/core/mod_interface/tests/inc/derive/micro_modules_unknown_vis/trybuild.stderr b/module/core/mod_interface/tests/inc/derive/micro_modules_unknown_vis/trybuild.stderr index 8df4ef8899..0fd927c7e0 100644 --- a/module/core/mod_interface/tests/inc/derive/micro_modules_unknown_vis/trybuild.stderr +++ b/module/core/mod_interface/tests/inc/derive/micro_modules_unknown_vis/trybuild.stderr @@ -1,4 +1,4 @@ -error: expected one of: `mod`, `use`, `layer` +error: expected one of: `mod`, `use`, `layer`, `reuse` --> tests/inc/derive/micro_modules_unknown_vis/mod.rs | | not_vis mod mod_exposed; diff --git a/module/core/mod_interface/tests/inc/derive/reuse_basic/child.rs b/module/core/mod_interface/tests/inc/derive/reuse_basic/child.rs index d94a40b5bd..19fcd7abde 100644 --- a/module/core/mod_interface/tests/inc/derive/reuse_basic/child.rs +++ b/module/core/mod_interface/tests/inc/derive/reuse_basic/child.rs @@ -6,7 +6,7 @@ mod private pub struct Prelude; } -crate::mod_interface! +crate::the_module::mod_interface! { own use Own; orphan use Orphan; diff --git a/module/core/mod_interface/tests/inc/derive/reuse_basic/mod.rs b/module/core/mod_interface/tests/inc/derive/reuse_basic/mod.rs index 55daeb2a1c..89ae6afbe8 100644 --- a/module/core/mod_interface/tests/inc/derive/reuse_basic/mod.rs +++ b/module/core/mod_interface/tests/inc/derive/reuse_basic/mod.rs @@ -10,7 +10,7 @@ mod child; // -crate::mod_interface! +crate::the_module::mod_interface! { reuse child; } diff --git a/module/core/mod_interface/tests/inc/derive/use_as/derive.rs b/module/core/mod_interface/tests/inc/derive/use_as/derive.rs index a70260fc3c..1d0b464591 100644 --- a/module/core/mod_interface/tests/inc/derive/use_as/derive.rs +++ b/module/core/mod_interface/tests/inc/derive/use_as/derive.rs @@ -6,7 +6,7 @@ pub mod layer_x; mod private {} -mod_interface! +the_module::mod_interface! { // #![ debug ] diff --git a/module/core/mod_interface/tests/inc/derive/use_bad_vis/trybuild.stderr b/module/core/mod_interface/tests/inc/derive/use_bad_vis/trybuild.stderr index b63d146f04..cb5d08876c 100644 --- a/module/core/mod_interface/tests/inc/derive/use_bad_vis/trybuild.stderr +++ b/module/core/mod_interface/tests/inc/derive/use_bad_vis/trybuild.stderr @@ -1,4 +1,4 @@ -error: Use either [ private, protected, orphan, exposed, prelude ] visibility: +error: Use either [ private, own, orphan, exposed, prelude ] visibility: #[doc = " layer_a"] pub use; --> tests/inc/derive/use_bad_vis/mod.rs | diff --git a/module/core/mod_interface/tests/inc/derive/use_basic/mod.rs b/module/core/mod_interface/tests/inc/derive/use_basic/mod.rs index fe3862d5b3..4afc8262c6 100644 --- a/module/core/mod_interface/tests/inc/derive/use_basic/mod.rs +++ b/module/core/mod_interface/tests/inc/derive/use_basic/mod.rs @@ -1,12 +1,14 @@ use super::*; -mod layer_a; -mod layer_b; +// private layer +pub mod layer_a; +// private layer +pub mod layer_b; mod private {} -mod_interface! +the_module::mod_interface! { /// layer_a diff --git a/module/core/mod_interface/tests/inc/derive/use_layer/layer_a.rs b/module/core/mod_interface/tests/inc/derive/use_layer/layer_a.rs index 14ecb25b3e..1b892a03b1 100644 --- a/module/core/mod_interface/tests/inc/derive/use_layer/layer_a.rs +++ b/module/core/mod_interface/tests/inc/derive/use_layer/layer_a.rs @@ -34,7 +34,7 @@ pub struct SubStruct4 // -mod_interface! +the_module::mod_interface! { orphan use ::std::vec::Vec; diff --git a/module/core/mod_interface/tests/inc/derive/use_layer/mod.rs b/module/core/mod_interface/tests/inc/derive/use_layer/mod.rs index a7f1790c60..4b4bfaa581 100644 --- a/module/core/mod_interface/tests/inc/derive/use_layer/mod.rs +++ b/module/core/mod_interface/tests/inc/derive/use_layer/mod.rs @@ -6,7 +6,7 @@ mod tools pub use super::super::*; } -mod layer_a; +pub mod layer_a; /// SuperStruct1. #[ derive( Debug, PartialEq ) ] @@ -16,7 +16,7 @@ pub struct SuperStruct1 mod private {} -mod_interface! +the_module::mod_interface! { /// layer_a diff --git a/module/core/mod_interface/tests/inc/derive/use_private_layers/layer_a.rs b/module/core/mod_interface/tests/inc/derive/use_private_layers/layer_a.rs new file mode 100644 index 0000000000..8c49982711 --- /dev/null +++ b/module/core/mod_interface/tests/inc/derive/use_private_layers/layer_a.rs @@ -0,0 +1,63 @@ + +/// Private namespace of the module. +mod private +{ +} + +/// Own namespace of the module. +#[ allow( unused_imports ) ] +pub mod own +{ + use super::*; + #[ doc( inline ) ] + pub use orphan::*; + /// layer_a_own + pub fn layer_a_own() -> bool + { + true + } +} + +#[ doc( inline ) ] +#[ allow( unused_imports ) ] +pub use own::*; + +/// Orphan namespace of the module. +#[ allow( unused_imports ) ] +pub mod orphan +{ + use super::*; + #[ doc( inline ) ] + pub use exposed::*; + /// layer_a_orphan + pub fn layer_a_orphan() -> bool + { + true + } +} + +/// Exposed namespace of the module. +#[ allow( unused_imports ) ] +pub mod exposed +{ + use super::*; + #[ doc( inline ) ] + pub use prelude::*; + /// layer_a_exposed + pub fn layer_a_exposed() -> bool + { + true + } +} + +/// Prelude to use essentials: `use my_module::prelude::*`. +#[ allow( unused_imports ) ] +pub mod prelude +{ + use super::*; + /// layer_a_prelude + pub fn layer_a_prelude() -> bool + { + true + } +} diff --git a/module/core/mod_interface/tests/inc/derive/use_private_layers/layer_b.rs b/module/core/mod_interface/tests/inc/derive/use_private_layers/layer_b.rs new file mode 100644 index 0000000000..1e15689f05 --- /dev/null +++ b/module/core/mod_interface/tests/inc/derive/use_private_layers/layer_b.rs @@ -0,0 +1,63 @@ + +/// Private namespace of the module. +mod private +{ +} + +/// Own namespace of the module. +#[ allow( unused_imports ) ] +pub mod own +{ + use super::*; + #[ doc( inline ) ] + pub use orphan::*; + /// layer_b_own + pub fn layer_b_own() -> bool + { + true + } +} + +#[ doc( inline ) ] +#[ allow( unused_imports ) ] +pub use own::*; + +/// Orphan namespace of the module. +#[ allow( unused_imports ) ] +pub mod orphan +{ + use super::*; + #[ doc( inline ) ] + pub use exposed::*; + /// layer_b_orphan + pub fn layer_b_orphan() -> bool + { + true + } +} + +/// Exposed namespace of the module. +#[ allow( unused_imports ) ] +pub mod exposed +{ + use super::*; + #[ doc( inline ) ] + pub use prelude::*; + /// layer_b_exposed + pub fn layer_b_exposed() -> bool + { + true + } +} + +/// Prelude to use essentials: `use my_module::prelude::*`. +#[ allow( unused_imports ) ] +pub mod prelude +{ + use super::*; + /// layer_b_prelude + pub fn layer_b_prelude() -> bool + { + true + } +} diff --git a/module/core/mod_interface/tests/inc/derive/use_private_layers/mod.rs b/module/core/mod_interface/tests/inc/derive/use_private_layers/mod.rs new file mode 100644 index 0000000000..531513253f --- /dev/null +++ b/module/core/mod_interface/tests/inc/derive/use_private_layers/mod.rs @@ -0,0 +1,28 @@ +#![ allow( dead_code ) ] +#![ allow( unused_imports ) ] + +use super::*; + +// private layer +mod layer_a; +// private layer +mod layer_b; + +mod private {} + +// xxx : qqq : make it working + +// the_module::mod_interface! +// { +// +// /// layer_a +// priv use super::layer_a; +// +// /// layer_b +// priv use super::layer_b; +// +// } +// +// // +// +// include!( "../../only_test/layer_simple_only_test.rs" ); diff --git a/module/core/mod_interface/tests/inc/derive/use_unknown_vis/trybuild.stderr b/module/core/mod_interface/tests/inc/derive/use_unknown_vis/trybuild.stderr index 530570d39a..0dc9fb08bc 100644 --- a/module/core/mod_interface/tests/inc/derive/use_unknown_vis/trybuild.stderr +++ b/module/core/mod_interface/tests/inc/derive/use_unknown_vis/trybuild.stderr @@ -1,4 +1,4 @@ -error: expected one of: `mod`, `use`, `layer` +error: expected one of: `mod`, `use`, `layer`, `reuse` --> tests/inc/derive/use_unknown_vis/mod.rs | | xyz use f1; diff --git a/module/core/mod_interface/tests/inc/manual/layer/mod.rs b/module/core/mod_interface/tests/inc/manual/layer/mod.rs index 044ff08dbf..adb8be65df 100644 --- a/module/core/mod_interface/tests/inc/manual/layer/mod.rs +++ b/module/core/mod_interface/tests/inc/manual/layer/mod.rs @@ -19,11 +19,13 @@ pub mod own #[ doc( inline ) ] pub use orphan::*; #[ doc( inline ) ] - #[ allow( unused_imports ) ] pub use super::layer_a::orphan::*; #[ doc( inline ) ] - #[ allow( unused_imports ) ] pub use super::layer_b::orphan::*; + #[ doc( inline ) ] + pub use super::layer_a; + #[ doc( inline ) ] + pub use super::layer_b; } #[ doc( inline ) ] diff --git a/module/core/mod_interface/tests/inc/manual/micro_modules/mod.rs b/module/core/mod_interface/tests/inc/manual/micro_modules/mod.rs index 65ba341ba1..5aa53251a1 100644 --- a/module/core/mod_interface/tests/inc/manual/micro_modules/mod.rs +++ b/module/core/mod_interface/tests/inc/manual/micro_modules/mod.rs @@ -1,3 +1,4 @@ +#![ allow( dead_code ) ] use super::*; diff --git a/module/core/mod_interface/tests/inc/manual/layer_use/layer_a.rs b/module/core/mod_interface/tests/inc/manual/use_layer/layer_a.rs similarity index 100% rename from module/core/mod_interface/tests/inc/manual/layer_use/layer_a.rs rename to module/core/mod_interface/tests/inc/manual/use_layer/layer_a.rs diff --git a/module/core/mod_interface/tests/inc/manual/layer_use/layer_b.rs b/module/core/mod_interface/tests/inc/manual/use_layer/layer_b.rs similarity index 100% rename from module/core/mod_interface/tests/inc/manual/layer_use/layer_b.rs rename to module/core/mod_interface/tests/inc/manual/use_layer/layer_b.rs diff --git a/module/core/mod_interface/tests/inc/manual/layer_use/mod.rs b/module/core/mod_interface/tests/inc/manual/use_layer/mod.rs similarity index 93% rename from module/core/mod_interface/tests/inc/manual/layer_use/mod.rs rename to module/core/mod_interface/tests/inc/manual/use_layer/mod.rs index 044ff08dbf..43622260c8 100644 --- a/module/core/mod_interface/tests/inc/manual/layer_use/mod.rs +++ b/module/core/mod_interface/tests/inc/manual/use_layer/mod.rs @@ -24,6 +24,10 @@ pub mod own #[ doc( inline ) ] #[ allow( unused_imports ) ] pub use super::layer_b::orphan::*; + #[ doc( inline ) ] + pub use super::layer_a; + #[ doc( inline ) ] + pub use super::layer_b; } #[ doc( inline ) ] diff --git a/module/core/mod_interface/tests/inc/mod.rs b/module/core/mod_interface/tests/inc/mod.rs index f838c57b50..1809e2f2e8 100644 --- a/module/core/mod_interface/tests/inc/mod.rs +++ b/module/core/mod_interface/tests/inc/mod.rs @@ -9,7 +9,7 @@ mod manual mod micro_modules; mod micro_modules_two; mod layer; - mod layer_use; + mod use_layer; } @@ -37,6 +37,7 @@ mod derive // use mod use_layer; mod use_basic; + mod use_private_layers; #[ path = "./use_as/derive.rs" ] mod use_as_derive; #[ path = "./use_as/manual.rs" ] @@ -50,6 +51,6 @@ mod derive } -// mod trybuild_test; +mod trybuild_test; // xxx : enable \ No newline at end of file diff --git a/module/core/mod_interface/tests/inc/only_test/layer_simple_only_test.rs b/module/core/mod_interface/tests/inc/only_test/layer_simple_only_test.rs index 93b1190705..f62756f61a 100644 --- a/module/core/mod_interface/tests/inc/only_test/layer_simple_only_test.rs +++ b/module/core/mod_interface/tests/inc/only_test/layer_simple_only_test.rs @@ -7,6 +7,12 @@ tests_impls! fn basic() { + /* test.case( "layers themself" ); */ + { + a_id!( own::layer_a::layer_a_own(), true ); + a_id!( own::layer_b::layer_b_own(), true ); + } + /* test.case( "root" ); */ { a_id!( layer_a::layer_a_own(), true ); diff --git a/module/core/mod_interface/tests/inc/trybuild_test.rs b/module/core/mod_interface/tests/inc/trybuild_test.rs index 5acc2a4f29..f5dbbbaece 100644 --- a/module/core/mod_interface/tests/inc/trybuild_test.rs +++ b/module/core/mod_interface/tests/inc/trybuild_test.rs @@ -5,6 +5,8 @@ use super::*; // #[ cfg_attr( feature = "enabled", module_mod_interface ) ] +// xxx : qqq : enable it + // #[ cfg( module_mod_interface ) ] // #[ cfg( module_is_terminal ) ] #[ test_tools::nightly ] @@ -12,50 +14,49 @@ use super::*; fn trybuild_tests() { // qqq : fix test : if run its test with --target-dir flag it's fall (for example : cargo test --target-dir C:\foo\bar ) - // // use test_tools::dependency::trybuild; - // println!( "current_dir : {:?}", std::env::current_dir().unwrap() ); - // // let t = trybuild::TestCases::new(); - // let t = test_tools::compiletime::TestCases::new(); - // - // let current_exe_path = std::env::current_exe().expect( "No such file or directory" ); - // - // let exe_directory = dbg!(current_exe_path.parent().expect("No such file or directory")); - // fn find_workspace_root( start_path : &std::path::Path ) -> Option< &std::path::Path > - // { - // start_path - // .ancestors() - // .find( |path| path.join( "Cargo.toml" ).exists() ) - // } - // - // let workspace_root = find_workspace_root( exe_directory ).expect( "No such file or directory" ); - // let current_dir = workspace_root.join( "module/core/mod_interface" ); - // - // // micro module - // - // t.pass( current_dir.join( "tests/inc/derive/micro_modules/trybuild.rs" ) ); - // t.pass( current_dir.join( "tests/inc/derive/micro_modules_two/trybuild.rs" ) ); - // t.pass( current_dir.join( "tests/inc/derive/micro_modules_two_joined/trybuild.rs" ) ); - // - // // layer - // - // t.pass( current_dir.join( "tests/inc/derive/layer/trybuild.rs" ) ); - // t.pass( current_dir.join( "tests/inc/derive/layer_have_layer/trybuild.rs" ) ); - // t.pass( current_dir.join( "tests/inc/derive/layer_have_layer_separate_use/trybuild.rs" ) ); - // t.pass( current_dir.join( "tests/inc/derive/layer_have_layer_separate_use_two/trybuild.rs" ) ); - // t.pass( current_dir.join( "tests/inc/derive/layer_have_layer_cfg/trybuild.rs" ) ); - // t.pass( current_dir.join( "tests/inc/derive/layer_use_cfg/trybuild.rs" ) ); - // t.pass( current_dir.join( "tests/inc/derive/layer_have_mod_cfg/trybuild.rs" ) ); - // t.pass( current_dir.join( "tests/inc/derive/layer_use_macro/trybuild.rs" ) ); - // - // // use - // - // t.pass( current_dir.join( "tests/inc/derive/use_basic/trybuild.rs" ) ); - // t.pass( current_dir.join( "tests/inc/derive/use_layer/trybuild.rs" ) ); - // t.pass( current_dir.join( "tests/inc/derive/use_as/trybuild.rs" ) ); - // - // // attr - // - // t.pass( current_dir.join( "tests/inc/derive/attr_debug/trybuild.rs" ) ); + // use test_tools::dependency::trybuild; + println!( "current_dir : {:?}", std::env::current_dir().unwrap() ); + let t = test_tools::compiletime::TestCases::new(); + + let current_exe_path = std::env::current_exe().expect( "No such file or directory" ); + + let exe_directory = dbg!(current_exe_path.parent().expect("No such file or directory")); + fn find_workspace_root( start_path : &std::path::Path ) -> Option< &std::path::Path > + { + start_path + .ancestors() + .find( |path| path.join( "Cargo.toml" ).exists() ) + } + + let workspace_root = find_workspace_root( exe_directory ).expect( "No such file or directory" ); + let current_dir = workspace_root.join( "module/core/mod_interface" ); + + // micro module + + t.pass( current_dir.join( "tests/inc/derive/micro_modules/trybuild.rs" ) ); + t.pass( current_dir.join( "tests/inc/derive/micro_modules_two/trybuild.rs" ) ); + t.pass( current_dir.join( "tests/inc/derive/micro_modules_two_joined/trybuild.rs" ) ); + + // layer + + t.pass( current_dir.join( "tests/inc/derive/layer/trybuild.rs" ) ); + t.pass( current_dir.join( "tests/inc/derive/layer_have_layer/trybuild.rs" ) ); + t.pass( current_dir.join( "tests/inc/derive/layer_have_layer_separate_use/trybuild.rs" ) ); + t.pass( current_dir.join( "tests/inc/derive/layer_have_layer_separate_use_two/trybuild.rs" ) ); + t.pass( current_dir.join( "tests/inc/derive/layer_have_layer_cfg/trybuild.rs" ) ); + t.pass( current_dir.join( "tests/inc/derive/layer_use_cfg/trybuild.rs" ) ); + t.pass( current_dir.join( "tests/inc/derive/layer_have_mod_cfg/trybuild.rs" ) ); + t.pass( current_dir.join( "tests/inc/derive/layer_use_macro/trybuild.rs" ) ); + + // use + + t.pass( current_dir.join( "tests/inc/derive/use_basic/trybuild.rs" ) ); + t.pass( current_dir.join( "tests/inc/derive/use_layer/trybuild.rs" ) ); + t.pass( current_dir.join( "tests/inc/derive/use_as/trybuild.rs" ) ); + + // attr + + t.pass( current_dir.join( "tests/inc/derive/attr_debug/trybuild.rs" ) ); // } @@ -69,29 +70,28 @@ only_for_terminal_module! fn cta_trybuild_tests() { // qqq : fix test : if run its test with --target-dir flag it's fall (for example : cargo test --target-dir C:\foo\bar ) - // use test_tools::dependency::trybuild; - // println!( "current_dir : {:?}", std::env::current_dir().unwrap() ); - // // let t = trybuild::TestCases::new(); - // let t = test_tools::compiletime::TestCases::new(); - // - // let current_exe_path = std::env::current_exe().expect( "No such file or directory" ); - // - // let exe_directory = current_exe_path.parent().expect( "No such file or directory" ); - // fn find_workspace_root( start_path : &std::path::Path ) -> Option< &std::path::Path > - // { - // start_path - // .ancestors() - // .find( |path| path.join( "Cargo.toml" ).exists() ) - // } - // - // let workspace_root = find_workspace_root( exe_directory ).expect( "No such file or directory" ); - // let current_dir = workspace_root.join( "module/core/mod_interface" ); - // - // t.compile_fail( current_dir.join( "tests/inc/derive/micro_modules_bad_vis/trybuild.rs" ) ); - // t.compile_fail( current_dir.join( "tests/inc/derive/micro_modules_unknown_vis/trybuild.rs" ) ); - // t.compile_fail( current_dir.join( "tests/inc/derive/layer_bad_vis/trybuild.rs" ) ); - // t.compile_fail( current_dir.join( "tests/inc/derive/layer_unknown_vis/trybuild.rs" ) ); - // t.compile_fail( current_dir.join( "tests/inc/derive/use_bad_vis/trybuild.rs" ) ); - // t.compile_fail( current_dir.join( "tests/inc/derive/use_unknown_vis/trybuild.rs" ) ); + use test_tools::dependency::trybuild; + println!( "current_dir : {:?}", std::env::current_dir().unwrap() ); + let t = test_tools::compiletime::TestCases::new(); + + let current_exe_path = std::env::current_exe().expect( "No such file or directory" ); + + let exe_directory = current_exe_path.parent().expect( "No such file or directory" ); + fn find_workspace_root( start_path : &std::path::Path ) -> Option< &std::path::Path > + { + start_path + .ancestors() + .find( |path| path.join( "Cargo.toml" ).exists() ) + } + + let workspace_root = find_workspace_root( exe_directory ).expect( "No such file or directory" ); + let current_dir = workspace_root.join( "module/core/mod_interface" ); + + t.compile_fail( current_dir.join( "tests/inc/derive/micro_modules_bad_vis/trybuild.rs" ) ); + t.compile_fail( current_dir.join( "tests/inc/derive/micro_modules_unknown_vis/trybuild.rs" ) ); + t.compile_fail( current_dir.join( "tests/inc/derive/layer_bad_vis/trybuild.rs" ) ); + t.compile_fail( current_dir.join( "tests/inc/derive/layer_unknown_vis/trybuild.rs" ) ); + t.compile_fail( current_dir.join( "tests/inc/derive/use_bad_vis/trybuild.rs" ) ); + t.compile_fail( current_dir.join( "tests/inc/derive/use_unknown_vis/trybuild.rs" ) ); } } diff --git a/module/core/mod_interface/tests/smoke_test.rs b/module/core/mod_interface/tests/smoke_test.rs index 828e9b016b..d826b0e72a 100644 --- a/module/core/mod_interface/tests/smoke_test.rs +++ b/module/core/mod_interface/tests/smoke_test.rs @@ -1,4 +1,4 @@ - +//! Smoke tests #[ test ] fn local_smoke_test() @@ -6,7 +6,6 @@ fn local_smoke_test() ::test_tools::smoke_test_for_local_run(); } - #[ test ] fn published_smoke_test() { diff --git a/module/core/mod_interface/tests/tests.rs b/module/core/mod_interface/tests/tests.rs index 33120affda..7736531699 100644 --- a/module/core/mod_interface/tests/tests.rs +++ b/module/core/mod_interface/tests/tests.rs @@ -1,3 +1,5 @@ +//! Main tests +#![ allow( unused_imports ) ] /// A struct for testing purpose. #[ derive( Debug, PartialEq ) ] @@ -5,9 +7,7 @@ pub struct CrateStructForTesting1 { } -#[ allow( unused_imports ) ] use ::mod_interface as the_module; -#[ allow( unused_imports ) ] use test_tools::exposed::*; #[ path="../../../../module/step/meta/src/module/terminal.rs" ] mod terminal; diff --git a/module/core/mod_interface_meta/src/impls.rs b/module/core/mod_interface_meta/src/impls.rs index 58fec18e93..84fd04d881 100644 --- a/module/core/mod_interface_meta/src/impls.rs +++ b/module/core/mod_interface_meta/src/impls.rs @@ -204,6 +204,16 @@ mod private pub use #adjsuted_path::orphan::*; }); + // export layer as own field of current layer + let prefixed_with_super_maybe = path.prefixed_with_super_maybe(); + c.clauses_map.get_mut( &VisOwn::Kind() ).unwrap().push( qt! + { + #[ doc( inline ) ] + #[ allow( unused_imports ) ] + #attrs1 + pub use #prefixed_with_super_maybe; + }); + c.clauses_map.get_mut( &VisExposed::Kind() ).unwrap().push( qt! { #[ doc( inline ) ] @@ -365,6 +375,16 @@ mod private pub use __all__::#path::orphan::*; }); + // export layer as own field of current layer + // let prefixed_with_super_maybe = path.prefixed_with_super_maybe(); + c.clauses_map.get_mut( &VisOwn::Kind() ).unwrap().push( qt! + { + #[ doc( inline ) ] + #[ allow( unused_imports ) ] + #attrs1 + pub use super::#path; + }); + c.clauses_map.get_mut( &VisExposed::Kind() ).unwrap().push( qt! { #[ doc( inline ) ] diff --git a/module/core/mod_interface_meta/src/use_tree.rs b/module/core/mod_interface_meta/src/use_tree.rs index de6805dd90..2191171530 100644 --- a/module/core/mod_interface_meta/src/use_tree.rs +++ b/module/core/mod_interface_meta/src/use_tree.rs @@ -24,12 +24,12 @@ mod private /// Is adding prefix to the tree path required? /// Add `super::private::` to path unless it starts from `::` or `super` or `crate`. - pub fn prefix_is_needed( &self ) -> bool + pub fn private_prefix_is_needed( &self ) -> bool { use syn::UseTree::*; - // println!( "prefix_is_needed : {:?}", self ); - // println!( "prefix_is_needed : self.leading_colon : {:?}", self.leading_colon ); + // println!( "private_prefix_is_needed : {:?}", self ); + // println!( "private_prefix_is_needed : self.leading_colon : {:?}", self.leading_colon ); if self.leading_colon.is_some() { @@ -105,47 +105,12 @@ mod private Ok( path ) } -// /// Adjusted path. -// /// Add `super::private::` to path unless it starts from `::` or `super` or `crate`. -// pub fn adjsuted_implicit_path( &self ) -> syn::Result< syn::punctuated::Punctuated< syn::Ident, Token![::] > > -// { -// // use syn::UseTree::*; -// let pure_path = self.pure_path()?; -// if self.prefix_is_needed() -// { -// Ok( parse_qt!{ super::private::#pure_path } ) -// } -// else -// { -// Ok( pure_path ) -// } -// } -// -// /// Adjusted path. -// /// Add `super::private::` to path unless it starts from `::` or `super` or `crate`. -// // pub fn adjsuted_explicit_path( &self ) -> syn::UseTree -// pub fn adjsuted_explicit_path( &self ) -> Self -// { -// // use syn::UseTree::*; -// if self.prefix_is_needed() -// { -// let mut clone = self.clone(); -// let tree = parse_qt!{ super::private::#self }; -// clone.tree = tree; -// clone -// } -// else -// { -// self.clone() -// } -// } - /// Prefix path with __all__ if it's appropriate. pub fn prefixed_with_all( &self ) -> Self { // use syn::UseTree::*; - if self.prefix_is_needed() + if self.private_prefix_is_needed() { let mut clone = self.clone(); let tree = parse_qt!{ __all__::#self }; @@ -159,6 +124,25 @@ mod private } + /// Prefix path with `super::` if it's appropriate to avoid "re-export of crate public `child`" problem. + pub fn prefixed_with_super_maybe( &self ) -> Self + { + + // use syn::UseTree::*; + if self.private_prefix_is_needed() + { + let mut clone = self.clone(); + let tree = parse_qt!{ super::#self }; + clone.tree = tree; + clone + } + else + { + self.clone() + } + + } + } impl syn::parse::Parse for UseTree diff --git a/module/core/test_tools/Cargo.toml b/module/core/test_tools/Cargo.toml index 6d30222997..f1e7576436 100644 --- a/module/core/test_tools/Cargo.toml +++ b/module/core/test_tools/Cargo.toml @@ -24,13 +24,15 @@ workspace = true features = [ "full" ] all-features = false - - # = features [features] -default = [ "enabled" ] -full = [ "enabled" ] +default = [ + "enabled", + # "standalone", + "normal", +] +full = [ "default" ] no_std = [ # "error_tools/no_std", # "meta_tools/no_std", @@ -53,39 +55,76 @@ use_alloc = [ # "former_stable/use_alloc", ] enabled = [ - "error_tools/enabled", - "meta_tools/enabled", - "mem_tools/enabled", - "typing_tools/enabled", - "data_type/enabled", - "diagnostics_tools/enabled", - "process_tools/enabled", - "collection_tools/enabled", + # "error_tools/enabled", + # "meta_tools/enabled", + # "mem_tools/enabled", + # "typing_tools/enabled", + # "data_type/enabled", + # "diagnostics_tools/enabled", + # "process_tools/enabled", + # "collection_tools/enabled", ] # nightly = [ "typing_tools/nightly" ] +normal = [ + "dep:error_tools", + "dep:collection_tools", + "dep:meta_tools", + "dep:mem_tools", + "dep:typing_tools", + "dep:diagnostics_tools", + "dep:process_tools", +] +standalone = [ + "standalone_error_tools", + "standalone_collection_tools", + + "dep:meta_tools", + "dep:mem_tools", + "dep:typing_tools", + "dep:diagnostics_tools", + "dep:process_tools", +] +standalone_error_tools = [ "dep:anyhow", "dep:thiserror", "error_typed", "error_untyped" ] +standalone_collection_tools = [ "dep:hashbrown", "collection_constructors", "collection_into_constructors" ] + +# error_tools +error_typed = [] +error_untyped = [] +# collection_tools +collection_constructors = [] +collection_into_constructors = [] + [dependencies] ## external -paste = "~1.0" # zzz : remove laster +# paste = "~1.0" # zzz : remove later rustversion = "~1.0" -# anyhow = "~1.0" num-traits = "~0.2" trybuild = { version = "1.0.85", features = [ "diff" ] } rand = "0.8.5" ## internal -error_tools = { workspace = true, features = [ "full" ] } -meta_tools = { workspace = true, features = [ "full" ] } -mem_tools = { workspace = true, features = [ "full" ] } -typing_tools = { workspace = true, features = [ "full" ] } -data_type = { workspace = true, features = [ "full" ] } -diagnostics_tools = { workspace = true, features = [ "full" ] } -process_tools = { workspace = true, features = [ "full" ] } -collection_tools = { workspace = true, features = [ "full" ] } +error_tools = { workspace = true, features = [ "full" ], optional = true } +collection_tools = { workspace = true, features = [ "full" ], optional = true } + +meta_tools = { workspace = true, features = [ "full" ], optional = true } +mem_tools = { workspace = true, features = [ "full" ], optional = true } +typing_tools = { workspace = true, features = [ "full" ], optional = true } +diagnostics_tools = { workspace = true, features = [ "full" ], optional = true } +process_tools = { workspace = true, features = [ "full" ], optional = true } + # former_stable = { workspace = true, features = [ "full" ] } +## transient + +# error_tools +anyhow = { workspace = true, optional = true } +thiserror = { workspace = true, optional = true } +# collection_tools +hashbrown = { workspace = true, optional = true } + [build-dependencies] rustc_version = "0.4" diff --git a/module/core/test_tools/src/lib.rs b/module/core/test_tools/src/lib.rs index f9870e775e..ced13dc4d8 100644 --- a/module/core/test_tools/src/lib.rs +++ b/module/core/test_tools/src/lib.rs @@ -6,43 +6,34 @@ /// Namespace with dependencies. +#[ allow( unused_imports ) ] #[ cfg( feature = "enabled" ) ] pub mod dependency { - // zzz : exclude later - #[ doc( inline ) ] - #[ allow( unused_imports ) ] - pub use ::paste; + // // zzz : exclude later + // #[ doc( inline ) ] + // pub use ::paste; #[ doc( inline ) ] - #[ allow( unused_imports ) ] pub use ::trybuild; #[ doc( inline ) ] - #[ allow( unused_imports ) ] pub use ::rustversion; - #[ doc( inline ) ] - #[ allow( unused_imports ) ] - pub use ::error_tools; - #[ doc( inline ) ] - #[ allow( unused_imports ) ] - pub use ::meta_tools; - #[ doc( inline ) ] - #[ allow( unused_imports ) ] - pub use ::mem_tools; - #[ doc( inline ) ] - #[ allow( unused_imports ) ] - pub use ::typing_tools; - #[ doc( inline ) ] - #[ allow( unused_imports ) ] - pub use ::num_traits; - #[ doc( inline ) ] - #[ allow( unused_imports ) ] - pub use ::diagnostics_tools; - - #[ doc( inline ) ] - #[ allow( unused_imports ) ] - pub use ::process_tools; + // #[ doc( inline ) ] + // pub use ::error_tools; +// #[ doc( inline ) ] +// pub use ::meta_tools; +// #[ doc( inline ) ] +// pub use ::mem_tools; +// #[ doc( inline ) ] +// pub use ::typing_tools; +// #[ doc( inline ) ] +// pub use ::num_traits; +// #[ doc( inline ) ] +// pub use ::diagnostics_tools; +// +// #[ doc( inline ) ] +// pub use ::process_tools; // #[ doc( inline ) ] // #[ allow( unused_imports ) ] @@ -54,56 +45,168 @@ mod private {} // -#[ cfg( feature = "enabled" ) ] +// #[ cfg( feature = "enabled" ) ] +// // #[ cfg( not( feature = "no_std" ) ) ] +// ::meta_tools::mod_interface! +// { +// // #![ debug ] +// +// own use super::dependency::*; +// +// layer test; +// +// // xxx : comment out +// use super::exposed::meta; +// use super::exposed::mem; +// use super::exposed::typing; +// use super::exposed::dt; +// use super::exposed::diagnostics; +// use super::exposed::collection; +// // use super::exposed::process; +// +// // prelude use ::rustversion::{ nightly, stable }; +// +// // // xxx : eliminate need to do such things, putting itself to proper category +// // exposed use super::test::compiletime; +// // exposed use super::test::helper; +// // exposed use super::test::smoke_test; +// +// prelude use ::meta_tools as meta; +// prelude use ::mem_tools as mem; +// prelude use ::typing_tools as typing; +// prelude use ::data_type as dt; +// prelude use ::diagnostics_tools as diagnostics; +// prelude use ::collection_tools as collection; +// // prelude use ::process_tools as process; +// +// use ::collection_tools; // xxx : do that for all dependencies +// +// prelude use ::meta_tools:: +// { +// impls, +// index, +// tests_impls, +// tests_impls_optional, +// tests_index, +// }; +// +// prelude use ::typing_tools::{ implements }; +// +// } + +// xxx : use module namespaces +// #[ cfg( feature = "enabled" ) ] // #[ cfg( not( feature = "no_std" ) ) ] -::meta_tools::mod_interface! +// pub use test::{ compiletime, helper, smoke_test }; + +#[ cfg( feature = "enabled" ) ] +pub mod test; + +/// Error tools. +#[ cfg( feature = "standalone" ) ] +#[ path = "../../../core/error_tools/src/error/mod.rs" ] +pub mod error; +#[ cfg( feature = "standalone" ) ] +pub use error as error_tools; + +/// Collection tools. +#[ cfg( feature = "standalone" ) ] +#[ path = "../../../core/collection_tools/src/collection/mod.rs" ] +pub mod collection; +#[ cfg( feature = "standalone" ) ] +pub use collection as collection_tools; + +#[ cfg( feature = "enabled" ) ] +#[ doc( inline ) ] +#[ allow( unused_imports ) ] +pub use own::*; + +/// Own namespace of the module. +#[ cfg( feature = "enabled" ) ] +#[ allow( unused_imports ) ] +pub mod own { - // #![ debug ] + use super::*; - own use super::dependency::*; + #[ doc( inline ) ] + pub use orphan::*; - layer test; + #[ doc( inline ) ] + pub use test::own::*; - // xxx : comment out - use super::exposed::meta; - use super::exposed::mem; - use super::exposed::typing; - use super::exposed::dt; - use super::exposed::diagnostics; - use super::exposed::collection; - // use super::exposed::process; + #[ doc( inline ) ] + pub use + { + error_tools::orphan::*, + collection_tools::orphan::*, + // meta_tools::orphan::*, + mem_tools::orphan::*, + typing_tools::orphan::*, + diagnostics_tools::orphan::*, + }; - // prelude use ::rustversion::{ nightly, stable }; +} - // // xxx : eliminate need to do such things, putting itself to proper category - // exposed use super::test::compiletime; - // exposed use super::test::helper; - // exposed use super::test::smoke_test; +/// Shared with parent namespace of the module +#[ cfg( feature = "enabled" ) ] +#[ allow( unused_imports ) ] +pub mod orphan +{ + use super::*; + + #[ doc( inline ) ] + pub use exposed::*; - prelude use ::meta_tools as meta; - prelude use ::mem_tools as mem; - prelude use ::typing_tools as typing; - prelude use ::data_type as dt; - prelude use ::diagnostics_tools as diagnostics; - prelude use ::collection_tools as collection; - // prelude use ::process_tools as process; + #[ doc( inline ) ] + pub use test::orphan::*; + +} + +/// Exposed namespace of the module. +#[ cfg( feature = "enabled" ) ] +#[ allow( unused_imports ) ] +pub mod exposed +{ + use super::*; + + #[ doc( inline ) ] + pub use prelude::*; - use ::collection_tools; // xxx : do that for all dependencies + #[ doc( inline ) ] + pub use test::exposed::*; - prelude use ::meta_tools:: + #[ doc( inline ) ] + pub use { - impls, - index, - tests_impls, - tests_impls_optional, - tests_index, + error_tools::exposed::*, + collection_tools::exposed::*, + // meta_tools::exposed::*, + mem_tools::exposed::*, + typing_tools::exposed::*, + diagnostics_tools::exposed::*, }; - prelude use ::typing_tools::{ implements }; - } -// xxx : use module namespaces -// #[ cfg( feature = "enabled" ) ] -// #[ cfg( not( feature = "no_std" ) ) ] -// pub use test::{ compiletime, helper, smoke_test }; +/// Prelude to use essentials: `use my_module::prelude::*`. +#[ cfg( feature = "enabled" ) ] +#[ allow( unused_imports ) ] +pub mod prelude +{ + use super::*; + + #[ doc( inline ) ] + pub use test::prelude::*; + + #[ doc( inline ) ] + pub use + { + error_tools::prelude::*, + collection_tools::prelude::*, + // meta_tools::prelude::*, + mem_tools::prelude::*, + typing_tools::prelude::*, + diagnostics_tools::prelude::*, + }; + +} diff --git a/module/core/test_tools/src/test/asset.rs b/module/core/test_tools/src/test/asset.rs index 410707ed36..e206fc8c0f 100644 --- a/module/core/test_tools/src/test/asset.rs +++ b/module/core/test_tools/src/test/asset.rs @@ -34,14 +34,73 @@ mod private } +// // +// // #[ cfg( not( feature = "no_std" ) ) ] +// crate::mod_interface! +// { // -// #[ cfg( not( feature = "no_std" ) ) ] -crate::mod_interface! +// // exposed use super; +// exposed use super::super::asset; +// +// // own use path_to_exe; +// +// } + +#[ doc( inline ) ] +#[ allow( unused_imports ) ] +pub use own::*; + +/// Own namespace of the module. +#[ allow( unused_imports ) ] +pub mod own { + use super::*; + + #[ doc( inline ) ] + pub use + { + }; + +} + +/// Shared with parent namespace of the module +#[ allow( unused_imports ) ] +pub mod orphan +{ + use super::*; + + #[ doc( inline ) ] + pub use exposed::*; - // exposed use super; - exposed use super::super::asset; + pub use super::super::asset; + +} + +/// Exposed namespace of the module. +#[ allow( unused_imports ) ] +pub mod exposed +{ + use super::*; + + #[ doc( inline ) ] + pub use prelude::*; + + #[ doc( inline ) ] + pub use + { + }; + +} + +/// Prelude to use essentials: `use my_module::prelude::*`. +#[ allow( unused_imports ) ] +pub mod prelude +{ + use super::*; - // own use path_to_exe; + #[ doc( inline ) ] + pub use + { + }; } diff --git a/module/core/test_tools/src/test/compiletime.rs b/module/core/test_tools/src/test/compiletime.rs index 4f29ec998e..7ea5edf31f 100644 --- a/module/core/test_tools/src/test/compiletime.rs +++ b/module/core/test_tools/src/test/compiletime.rs @@ -73,14 +73,74 @@ mod private // } // } -crate::mod_interface! +// crate::mod_interface! +// { +// // #![ debug ] +// // xxx : make it working +// // exposed use super; +// exposed use super::super::compiletime; +// own use +// { +// * +// }; +// } + +#[ doc( inline ) ] +#[ allow( unused_imports ) ] +pub use own::*; + +/// Own namespace of the module. +#[ allow( unused_imports ) ] +pub mod own { - // #![ debug ] - // xxx : make it working - // exposed use super; - exposed use super::super::compiletime; - own use + use super::*; + + #[ doc( inline ) ] + pub use { - * + private::*, }; + +} + +/// Shared with parent namespace of the module +#[ allow( unused_imports ) ] +pub mod orphan +{ + use super::*; + + #[ doc( inline ) ] + pub use exposed::*; + + pub use super::super::compiletime; + +} + +/// Exposed namespace of the module. +#[ allow( unused_imports ) ] +pub mod exposed +{ + use super::*; + + #[ doc( inline ) ] + pub use prelude::*; + + #[ doc( inline ) ] + pub use + { + }; + +} + +/// Prelude to use essentials: `use my_module::prelude::*`. +#[ allow( unused_imports ) ] +pub mod prelude +{ + use super::*; + + #[ doc( inline ) ] + pub use + { + }; + } diff --git a/module/core/test_tools/src/test/helper.rs b/module/core/test_tools/src/test/helper.rs index 49675e2ada..4a97b134da 100644 --- a/module/core/test_tools/src/test/helper.rs +++ b/module/core/test_tools/src/test/helper.rs @@ -75,16 +75,78 @@ mod private pub use doc_file_test; } -crate::mod_interface! +// crate::mod_interface! +// { +// // xxx +// // #![ debug ] +// // exposed use super; +// exposed use super::super::helper; +// +// prelude use +// { +// num, +// doc_file_test, +// }; +// } + +#[ doc( inline ) ] +#[ allow( unused_imports ) ] +pub use own::*; + +/// Own namespace of the module. +#[ allow( unused_imports ) ] +pub mod own { - // xxx - // #![ debug ] - // exposed use super; - exposed use super::super::helper; + use super::*; - prelude use + #[ doc( inline ) ] + pub use { - num, - doc_file_test, + private::*, }; + +} + +/// Shared with parent namespace of the module +#[ allow( unused_imports ) ] +pub mod orphan +{ + use super::*; + + #[ doc( inline ) ] + pub use exposed::*; + + pub use super::super::helper; + +} + +/// Exposed namespace of the module. +#[ allow( unused_imports ) ] +pub mod exposed +{ + use super::*; + + #[ doc( inline ) ] + pub use prelude::*; + + #[ doc( inline ) ] + pub use + { + private::num, + private::doc_file_test, + }; + +} + +/// Prelude to use essentials: `use my_module::prelude::*`. +#[ allow( unused_imports ) ] +pub mod prelude +{ + use super::*; + + #[ doc( inline ) ] + pub use + { + }; + } diff --git a/module/core/test_tools/src/test/mod.rs b/module/core/test_tools/src/test/mod.rs index 158406fbd1..0d62f6e9d2 100644 --- a/module/core/test_tools/src/test/mod.rs +++ b/module/core/test_tools/src/test/mod.rs @@ -5,12 +5,107 @@ mod private {} -// #[ cfg( not( feature = "no_std" ) ) ] -crate::mod_interface! +// // #[ cfg( not( feature = "no_std" ) ) ] +// crate::mod_interface! +// { +// layer asset; +// layer compiletime; +// layer helper; +// layer smoke_test; +// layer version; +// } + +pub mod asset; +pub mod compiletime; +pub mod helper; +pub mod smoke_test; +pub mod version; + +#[ cfg( feature = "enabled" ) ] +#[ doc( inline ) ] +#[ allow( unused_imports ) ] +pub use own::*; + +/// Own namespace of the module. +#[ cfg( feature = "enabled" ) ] +#[ allow( unused_imports ) ] +pub mod own +{ + use super::*; + + #[ doc( inline ) ] + pub use orphan::*; + + #[ doc( inline ) ] + pub use + { + asset::orphan::*, + compiletime::orphan::*, + helper::orphan::*, + smoke_test::orphan::*, + version::orphan::*, + }; + +} + +/// Shared with parent namespace of the module +#[ cfg( feature = "enabled" ) ] +#[ allow( unused_imports ) ] +pub mod orphan { - layer asset; - layer compiletime; - layer helper; - layer smoke_test; - layer version; + use super::*; + + #[ doc( inline ) ] + pub use exposed::*; + +} + +/// Exposed namespace of the module. +#[ cfg( feature = "enabled" ) ] +#[ allow( unused_imports ) ] +pub mod exposed +{ + use super::*; + + #[ doc( inline ) ] + pub use prelude::*; + + #[ doc( inline ) ] + pub use + { + asset::exposed::*, + compiletime::exposed::*, + helper::exposed::*, + smoke_test::exposed::*, + version::exposed::*, + }; + + pub use meta_tools:: + { + impls, + index, + tests_impls, + tests_impls_optional, + tests_index, + }; + +} + +/// Prelude to use essentials: `use my_module::prelude::*`. +#[ cfg( feature = "enabled" ) ] +#[ allow( unused_imports ) ] +pub mod prelude +{ + use super::*; + + #[ doc( inline ) ] + pub use + { + asset::prelude::*, + compiletime::prelude::*, + helper::prelude::*, + smoke_test::prelude::*, + version::prelude::*, + }; + } diff --git a/module/core/test_tools/src/test/smoke_test.rs b/module/core/test_tools/src/test/smoke_test.rs index 34ebd2f55f..0074d4f090 100644 --- a/module/core/test_tools/src/test/smoke_test.rs +++ b/module/core/test_tools/src/test/smoke_test.rs @@ -11,8 +11,9 @@ /// Internal namespace. mod private { + #[ allow( unused_imports ) ] use crate::*; - use dependency::process_tools::environment; + use process_tools::environment; // zzz : comment out // pub mod environment // { @@ -316,17 +317,83 @@ mod private } +// // +// crate::mod_interface! +// { // -crate::mod_interface! +// // exposed use super; +// exposed use super::super::smoke_test; +// +// exposed use SmokeModuleTest; +// exposed use smoke_test_run; +// exposed use smoke_tests_run; +// exposed use smoke_test_for_local_run; +// exposed use smoke_test_for_published_run; +// +// } + + +#[ doc( inline ) ] +#[ allow( unused_imports ) ] +pub use own::*; + +/// Own namespace of the module. +#[ allow( unused_imports ) ] +pub mod own +{ + use super::*; + + #[ doc( inline ) ] + pub use + { + private::*, + }; + +} + +/// Shared with parent namespace of the module +#[ allow( unused_imports ) ] +pub mod orphan +{ + use super::*; + + #[ doc( inline ) ] + pub use exposed::*; + + pub use super::super::smoke_test; + +} + +/// Exposed namespace of the module. +#[ allow( unused_imports ) ] +pub mod exposed { + use super::*; + + #[ doc( inline ) ] + pub use prelude::*; - // exposed use super; - exposed use super::super::smoke_test; + #[ doc( inline ) ] + pub use private:: + { + SmokeModuleTest, + smoke_test_run, + smoke_tests_run, + smoke_test_for_local_run, + smoke_test_for_published_run, + }; + +} - exposed use SmokeModuleTest; - exposed use smoke_test_run; - exposed use smoke_tests_run; - exposed use smoke_test_for_local_run; - exposed use smoke_test_for_published_run; +/// Prelude to use essentials: `use my_module::prelude::*`. +#[ allow( unused_imports ) ] +pub mod prelude +{ + use super::*; + + #[ doc( inline ) ] + pub use + { + }; } diff --git a/module/core/test_tools/src/test/version.rs b/module/core/test_tools/src/test/version.rs index 7737b5b456..8cdc5cbbc8 100644 --- a/module/core/test_tools/src/test/version.rs +++ b/module/core/test_tools/src/test/version.rs @@ -10,14 +10,73 @@ mod private } +// // +// // #[ cfg( not( feature = "no_std" ) ) ] +// crate::mod_interface! +// { // -// #[ cfg( not( feature = "no_std" ) ) ] -crate::mod_interface! +// // exposed use super; +// exposed use super::super::version; +// +// prelude use ::rustversion::{ nightly, stable }; +// +// } + + +#[ doc( inline ) ] +#[ allow( unused_imports ) ] +pub use own::*; + +/// Own namespace of the module. +#[ allow( unused_imports ) ] +pub mod own +{ + use super::*; + + #[ doc( inline ) ] + pub use + { + private::*, + }; + +} + +/// Shared with parent namespace of the module +#[ allow( unused_imports ) ] +pub mod orphan { + use super::*; - // exposed use super; - exposed use super::super::version; + #[ doc( inline ) ] + pub use exposed::*; + + pub use super::super::version; + +} + +/// Exposed namespace of the module. +#[ allow( unused_imports ) ] +pub mod exposed +{ + use super::*; + + #[ doc( inline ) ] + pub use prelude::*; + + #[ doc( inline ) ] + pub use rustversion::{ nightly, stable }; + +} + +/// Prelude to use essentials: `use my_module::prelude::*`. +#[ allow( unused_imports ) ] +pub mod prelude +{ + use super::*; - prelude use ::rustversion::{ nightly, stable }; + #[ doc( inline ) ] + pub use + { + }; } diff --git a/module/core/test_tools/tests/inc/basic_test.rs b/module/core/test_tools/tests/inc/basic_test.rs index 8e631611f4..9b1133fb91 100644 --- a/module/core/test_tools/tests/inc/basic_test.rs +++ b/module/core/test_tools/tests/inc/basic_test.rs @@ -15,7 +15,6 @@ use super::*; use ::test_tools as the_module; - #[ cfg( feature = "enabled" ) ] #[ cfg( not( feature = "no_std" ) ) ] the_module::tests_impls! diff --git a/module/core/wtools/Cargo.toml b/module/core/wtools/Cargo.toml index 4d2e2f1f29..c8d108f307 100644 --- a/module/core/wtools/Cargo.toml +++ b/module/core/wtools/Cargo.toml @@ -58,7 +58,7 @@ meta_default = [ "meta_mod_interface", # "meta_former", # "meta_options", - "meta_constructors", + # "meta_constructors", "meta_idents_concat", ] meta_full = [ @@ -68,7 +68,7 @@ meta_full = [ "meta_mod_interface", # "meta_former", # "meta_options", - "meta_constructors", + # "meta_constructors", "meta_idents_concat", ] # meta_use_std = [ "meta", "meta_tools/use_std" ] @@ -79,7 +79,7 @@ meta_for_each = [ "meta", "meta_tools/meta_for_each" ] meta_impls_index = [ "meta", "meta_tools/meta_impls_index" ] meta_mod_interface = [ "meta" ] # meta_mod_interface = [ "meta", "meta_tools/mod_interface" ] -meta_constructors = [ "meta", "meta_tools/meta_constructors" ] +# meta_constructors = [ "meta", "meta_tools/meta_constructors" ] meta_idents_concat = [ "meta", "meta_tools/meta_idents_concat" ] # meta_former = [ "meta", "meta_tools/former" ] # meta_options = [ "meta", "meta_tools/options" ] diff --git a/module/move/optimization_tools/Cargo.toml b/module/move/optimization_tools/Cargo.toml index de8500b846..af2f73c222 100644 --- a/module/move/optimization_tools/Cargo.toml +++ b/module/move/optimization_tools/Cargo.toml @@ -40,7 +40,9 @@ lp_parse = [ "dep:exmex" ] derive_tools = { workspace = true, features = [ "derive_more", "full", "strum" ] } deterministic_rand = { workspace = true, features = [ "default" ] } iter_tools = { workspace = true, features = [ "default" ] } -meta_tools = { workspace = true, features = [ "meta_constructors" ] } +# meta_tools = { workspace = true, features = [ "meta_constructors" ] } +meta_tools = { workspace = true, features = [] } +collection_tools = { workspace = true } # qqq : use intead of meta_tools error_tools = { workspace = true, features = ["default"] } env_logger = "0.10.1" log = "0.4.20" diff --git a/module/move/wca/src/ca/executor/executor.rs b/module/move/wca/src/ca/executor/executor.rs index 224aacd489..22e44fc19b 100644 --- a/module/move/wca/src/ca/executor/executor.rs +++ b/module/move/wca/src/ca/executor/executor.rs @@ -3,7 +3,7 @@ mod private use crate::*; // use wtools::error::Result; - use error::return_err; + // use error::return_err; use ca::help::{ HelpGeneratorOptions, generate_help_content, LevelOfDetail }; // aaa : for Bohdan : how is it useful? where is it used? @@ -122,7 +122,7 @@ mod private let commands = dictionary.search( name.strip_prefix( '.' ).unwrap_or( name ) ); if commands.is_empty() { - return_err!( "Not found command that starts with `.{}`.", name ); + error::untyped::return_err!( "Not found command that starts with `.{}`.", name ); } let generator_args = HelpGeneratorOptions::former() .command_prefix( "." ) @@ -151,10 +151,10 @@ mod private } else { - return_err!( "Not found command that starts with `.{}`.", name ); + error::untyped::return_err!( "Not found command that starts with `.{}`.", name ); } } - unexpected => return_err!( "Encountered an unrecognized internal command: `.{}`.", unexpected ), + unexpected => error::untyped::return_err!( "Encountered an unrecognized internal command: `.{}`.", unexpected ), } Ok( () ) diff --git a/module/move/wca/src/ca/grammar/types.rs b/module/move/wca/src/ca/grammar/types.rs index d5c6e971df..2141ca335b 100644 --- a/module/move/wca/src/ca/grammar/types.rs +++ b/module/move/wca/src/ca/grammar/types.rs @@ -8,7 +8,7 @@ mod private }; // use wtools; // use wtools::{ error::Result, err }; - use error::err; + // use error::err; use iter_tools::Itertools; /// Available types that can be converted to a `Value` @@ -186,9 +186,9 @@ mod private match self { Self::String => Ok( Value::String( value ) ), - Self::Number => value.parse().map_err( | _ | err!( "Can not parse number from `{}`", value ) ).map( Value::Number ), + Self::Number => value.parse().map_err( | _ | error::untyped::format_err!( "Can not parse number from `{}`", value ) ).map( Value::Number ), Self::Path => Ok( Value::Path( value.into() ) ), - Self::Bool => Ok( Value::Bool( match value.as_str() { "1" | "true" => true, "0" | "false" => false, _ => return Err( err!( "Can not parse bool from `{}`", value ) ) } ) ), + Self::Bool => Ok( Value::Bool( match value.as_str() { "1" | "true" => true, "0" | "false" => false, _ => return Err( error::untyped::format_err!( "Can not parse bool from `{}`", value ) ) } ) ), Self::List( kind, delimeter ) => { let values = value diff --git a/module/move/wca/src/ca/parser/parser.rs b/module/move/wca/src/ca/parser/parser.rs index 1efe959495..4a23f24db3 100644 --- a/module/move/wca/src/ca/parser/parser.rs +++ b/module/move/wca/src/ca/parser/parser.rs @@ -4,7 +4,7 @@ mod private use std::collections::HashMap; - use error::{ return_err }; + // use error::{ return_err }; /// `Parser` is a struct used for parsing data. #[ derive( Debug ) ] @@ -58,14 +58,14 @@ mod private fn parse_command( args : &[ String ] ) -> error::untyped::Result< ( ParsedCommand, usize ) > { if args.is_empty() { - return_err!( "Unexpected behaviour: Try to parse command without input" ); + error::untyped::return_err!( "Unexpected behaviour: Try to parse command without input" ); } let mut i = 0; if !Self::valid_command_name( &args[ i ] ) { - return_err!( "Unexpected input: Expected a command, found: `{}`", args[ i ] ); + error::untyped::return_err!( "Unexpected input: Expected a command, found: `{}`", args[ i ] ); } let name = match args[ i ].strip_prefix( '.' ).unwrap() { @@ -125,7 +125,7 @@ mod private // prop: else { - return_err!( "Unexpected input '{}': Detected a possible property key preceding the ':' character. However, no corresponding value was found.", item ); + error::untyped::return_err!( "Unexpected input '{}': Detected a possible property key preceding the ':' character. However, no corresponding value was found.", item ); } } // prop : value | prop :value @@ -146,13 +146,22 @@ mod private // : else { - return_err!( "Unexpected input '{} :': Detected a possible property key preceding the ':' character. However, no corresponding value was found.", item ); + error::untyped::return_err! + ( + "Unexpected input '{} :': Detected a possible property key preceding the ':' character. However, no corresponding value was found.", + item, + ); } } - else if !properties_turn { subjects.push( item.to_string() ); } - - else { return_err!( "Unexpected input: Expected `command` or `property`, found: `{}`", item ); } + else if !properties_turn + { + subjects.push( item.to_string() ); + } + else + { + error::untyped::return_err!( "Unexpected input: Expected `command` or `property`, found: `{}`", item ); + } i += 1; } diff --git a/module/move/wca/src/ca/tool/mod.rs b/module/move/wca/src/ca/tool/mod.rs index 91290592a7..3f400c96a9 100644 --- a/module/move/wca/src/ca/tool/mod.rs +++ b/module/move/wca/src/ca/tool/mod.rs @@ -7,7 +7,10 @@ crate::mod_interface! layer table; orphan use super::super::tool; - orphan use ::error_tools as error; + + // orphan use ::error_tools as error; + use ::error_tools; + orphan use ::iter_tools; // use ::strs_tools as string; // xxx : check diff --git a/module/move/wca/src/ca/tool/table.rs b/module/move/wca/src/ca/tool/table.rs index 192caa0396..424356530b 100644 --- a/module/move/wca/src/ca/tool/table.rs +++ b/module/move/wca/src/ca/tool/table.rs @@ -3,7 +3,7 @@ mod private use crate::*; // use wtools::error::{ Result, err }; - use error::err; + // use error::err; /// Represents a table composed of multiple rows. /// @@ -98,7 +98,7 @@ mod private let table = table.into(); if !table.validate() { - return Err( err!( "Invalid table" ) ); + return Err( error::untyped::format_err!( "Invalid table" ) ); } let max_lengths = max_column_lengths( &table ); diff --git a/module/move/wca/src/ca/verifier/verifier.rs b/module/move/wca/src/ca/verifier/verifier.rs index a595521755..0202279546 100644 --- a/module/move/wca/src/ca/verifier/verifier.rs +++ b/module/move/wca/src/ca/verifier/verifier.rs @@ -7,7 +7,7 @@ mod private use std::collections::HashMap; use indexmap::IndexMap; // use wtools::{ error, error::Result, err }; - use error::err; + // use error::err; use ca::help::{ HelpGeneratorOptions, LevelOfDetail, generate_help_content }; // xxx @@ -134,12 +134,12 @@ mod private { Some( v ) => v, None if *optional => continue, - _ => return Err( err!( "Missing not optional subject" ) ), + _ => return Err( error::untyped::format_err!( "Missing not optional subject" ) ), }; subjects.push( value ); current = rc_subjects_iter.next(); } - if let Some( value ) = current { return Err( err!( "Can not identify a subject: `{}`", value ) ) } + if let Some( value ) = current { return Err( error::untyped::format_err!( "Can not identify a subject: `{}`", value ) ) } Ok( subjects ) } @@ -214,8 +214,10 @@ mod private { #[ cfg( feature = "on_unknown_suggest" ) ] if let Some( phrase ) = Self::suggest_command( dictionary, &raw_command.name ) - { return err!( "Command not found. Maybe you mean `.{}`?", phrase ) } - err!( "Command not found. Please use `.` command to see the list of available commands." ) + { + return error::untyped::format_err!( "Command not found. Maybe you mean `.{}`?", phrase ) + } + error::untyped::format_err!( "Command not found. Please use `.` command to see the list of available commands." ) } )?; diff --git a/module/move/willbe/src/action/cicd_renew.rs b/module/move/willbe/src/action/cicd_renew.rs index fdf14a1216..5814e2802c 100644 --- a/module/move/willbe/src/action/cicd_renew.rs +++ b/module/move/willbe/src/action/cicd_renew.rs @@ -19,7 +19,7 @@ mod private use error:: { typed::Error, - err, + // err, }; #[ derive( Debug, Error ) ] @@ -42,7 +42,7 @@ mod private // qqq : for Petro : should return Report and typed error in Result /// Generate workflows for modules in .github/workflows directory. - pub fn cicd_renew( base_path : &Path ) -> Result< (), CiCdGenerateError > + pub fn action( base_path : &Path ) -> Result< (), CiCdGenerateError > { let workspace_cache = Workspace::try_from( CrateDir::try_from( base_path )? )?; let packages = workspace_cache.packages(); @@ -375,7 +375,7 @@ mod private return url::repo_url_extract( &url ) .and_then( | url | url::git_info_extract( &url ).ok() ) .map( UsernameAndRepository ) - .ok_or_else( || err!( "Fail to parse repository url from workspace Cargo.toml")) + .ok_or_else( || error::untyped::format_err!( "Fail to parse repository url from workspace Cargo.toml")) } else { @@ -393,7 +393,7 @@ mod private .and_then( | url | url::repo_url_extract( &url ) ) .and_then( | url | url::git_info_extract( &url ).ok() ) .map( UsernameAndRepository ) - .ok_or_else( || err!( "Fail to extract repository url") ) + .ok_or_else( || error::untyped::format_err!( "Fail to extract repository url") ) } } @@ -401,5 +401,5 @@ mod private crate::mod_interface! { - exposed use cicd_renew; + own use action; } diff --git a/module/move/willbe/src/action/features.rs b/module/move/willbe/src/action/features.rs index 26b8701cc2..ca7312d289 100644 --- a/module/move/willbe/src/action/features.rs +++ b/module/move/willbe/src/action/features.rs @@ -112,3 +112,4 @@ crate::mod_interface! orphan use FeaturesOptions; orphan use FeaturesReport; } +// qqq : don't use orphan here \ No newline at end of file diff --git a/module/move/willbe/src/action/list.rs b/module/move/willbe/src/action/list.rs index 6f2708217b..074189cd7f 100644 --- a/module/move/willbe/src/action/list.rs +++ b/module/move/willbe/src/action/list.rs @@ -13,7 +13,7 @@ mod private }; use error:: { - ErrWith, err, + ErrWith, untyped::{ Context, format_err }, }; use tool::{ TreePrinter, ListNodeReport }; @@ -39,7 +39,7 @@ mod private { "tree" => ListFormat::Tree, "toposort" => ListFormat::Topological, - e => return Err( err!( "Unknown format '{}'. Available values : [tree, toposort]", e )) + e => return Err( error::untyped::format_err!( "Unknown format '{}'. Available values : [tree, toposort]", e )) }; Ok( value ) @@ -105,7 +105,7 @@ mod private { "nothing" => ListFilter::Nothing, "local" => ListFilter::Local, - e => return Err( err!( "Unknown filter '{}'. Available values : [nothing, local]", e ) ) + e => return Err( error::untyped::format_err!( "Unknown filter '{}'. Available values : [nothing, local]", e ) ) }; Ok( value ) @@ -436,7 +436,7 @@ mod private /// - `Result` - A result containing the list report if successful, /// or a tuple containing the list report and error if not successful. #[ cfg_attr( feature = "tracing", tracing::instrument ) ] - pub fn list( args : ListOptions ) + pub fn list_all( args : ListOptions ) -> ResultWithReport< ListReport, error::untyped::Error > // qqq : should be specific error // qqq : use typed error @@ -849,5 +849,5 @@ crate::mod_interface! /// Contains output of a single node of the action. // own use ListNodeReport; /// List packages in workspace. - orphan use list; + orphan use list_all; } diff --git a/module/move/willbe/src/action/main_header.rs b/module/move/willbe/src/action/main_header.rs index 7c1b5af526..05ca9ec085 100644 --- a/module/move/willbe/src/action/main_header.rs +++ b/module/move/willbe/src/action/main_header.rs @@ -18,8 +18,8 @@ mod private use entity::{ PathError, WorkspaceInitError }; use error:: { - err, - untyped::Error, + // err, + // untyped::Error, }; use workspace_md_extension::WorkspaceMdExtension; @@ -86,7 +86,7 @@ mod private { /// Represents a common error. #[ error( "Common error: {0}" ) ] - Common(#[ from ] Error ), + Common( #[ from ] error::untyped::Error ), // qqq : rid of /// Represents an I/O error. #[ error( "I/O error: {0}" ) ] IO( #[ from ] std::io::Error ), @@ -116,14 +116,14 @@ mod private // aaa : done let repository_url = workspace .repository_url() - .ok_or_else::< Error, _ > - ( || err!( "repo_url not found in workspace Cargo.toml" ) )?; + .ok_or_else::< error::untyped::Error, _ > + ( || error::untyped::format_err!( "repo_url not found in workspace Cargo.toml" ) )?; let master_branch = workspace.master_branch().unwrap_or( "master".into() ); let workspace_name = workspace .workspace_name() - .ok_or_else::< Error, _ > - ( || err!( "workspace_name not found in workspace Cargo.toml" ) )?; + .ok_or_else::< error::untyped::Error, _ > + ( || error::untyped::format_err!( "workspace_name not found in workspace Cargo.toml" ) )?; let discord_url = workspace.discord_url(); @@ -193,7 +193,7 @@ mod private /// [![docs.rs](https://raster.shields.io/static/v1?label=docs&message=online&color=eee&logo=docsdotrs&logoColor=eee)](https://docs.rs/wtools) /// /// ``` - pub fn readme_header_renew( crate_dir : CrateDir ) + pub fn action( crate_dir : CrateDir ) // -> Result< MainHeaderRenewReport, ( MainHeaderRenewReport, MainHeaderRenewError ) > -> ResultWithReport< MainHeaderRenewReport, MainHeaderRenewError > { @@ -265,7 +265,7 @@ mod private crate::mod_interface! { /// Generate header. - orphan use readme_header_renew; + own use action; /// Report. orphan use MainHeaderRenewReport; /// Error. diff --git a/module/move/willbe/src/action/publish_diff.rs b/module/move/willbe/src/action/publish_diff.rs index d27920c7bc..11b1758ca8 100644 --- a/module/move/willbe/src/action/publish_diff.rs +++ b/module/move/willbe/src/action/publish_diff.rs @@ -105,7 +105,7 @@ mod private let path = AbsolutePath::try_from( o.path )?; let dir = CrateDir::try_from( path.clone() )?; - let list = action::list + let list = action::list_all ( action::list::ListOptions::former() .path_to_manifest( dir ) diff --git a/module/move/willbe/src/action/readme_modules_headers_renew.rs b/module/move/willbe/src/action/readme_modules_headers_renew.rs index 9b613d97fa..aeba473c74 100644 --- a/module/move/willbe/src/action/readme_modules_headers_renew.rs +++ b/module/move/willbe/src/action/readme_modules_headers_renew.rs @@ -20,11 +20,11 @@ mod private use package::Package; use error:: { - err, + // err, untyped:: { // Result, - Error as wError, + // Error as wError, Context, }, }; @@ -101,7 +101,7 @@ mod private { /// Represents a common error. #[ error( "Common error: {0}" ) ] - Common(#[ from ] wError ), + Common(#[ from ] error::untyped::Error ), // qqq : rid of /// Represents an I/O error. #[ error( "I/O error: {0}" ) ] IO( #[ from ] std::io::Error ), @@ -140,7 +140,7 @@ mod private let stability = package.stability()?; let module_name = package.name()?; let repository_url = package.repository()? - .ok_or_else::< wError, _ >( || err!( "Fail to find repository_url in module`s Cargo.toml" ) )?; + .ok_or_else::< error::untyped::Error, _ >( || error::untyped::format_err!( "Fail to find repository_url in module`s Cargo.toml" ) )?; let discord_url = package .discord_url()? @@ -172,7 +172,7 @@ mod private let repo_url = url::repo_url_extract( &self.repository_url ) .and_then( | r | url::git_info_extract( &r ).ok() ) - .ok_or_else::< wError, _ >( || err!( "Fail to parse repository url" ) )?; + .ok_or_else::< error::untyped::Error, _ >( || error::untyped::format_err!( "Fail to parse repository url" ) )?; let example= if let Some( name ) = find_example_file ( self.module_path.as_path(), @@ -269,7 +269,7 @@ mod private .join ( repository::readme_path( path.parent().unwrap().as_ref() ) - // .ok_or_else::< wError, _ >( || err!( "Fail to find README.md at {}", &path ) ) + // .ok_or_else::< error::untyped::Error, _ >( || error::untyped::format_err!( "Fail to find README.md at {}", &path ) ) .err_with_report( &report )? ); diff --git a/module/move/willbe/src/action/workspace_renew.rs b/module/move/willbe/src/action/workspace_renew.rs index 58e4ad61ea..05936758d9 100644 --- a/module/move/willbe/src/action/workspace_renew.rs +++ b/module/move/willbe/src/action/workspace_renew.rs @@ -134,7 +134,7 @@ mod private // qqq : for Petro : should return report // qqq : for Petro : should have typed error /// Creates workspace template - pub fn workspace_renew + pub fn action ( path : &Path, mut template : WorkspaceTemplate, @@ -172,6 +172,6 @@ mod private crate::mod_interface! { - exposed use workspace_renew; + own use action; orphan use WorkspaceTemplate; } diff --git a/module/move/willbe/src/command/cicd_renew.rs b/module/move/willbe/src/command/cicd_renew.rs index 50b1a8de91..c82b45b8da 100644 --- a/module/move/willbe/src/command/cicd_renew.rs +++ b/module/move/willbe/src/command/cicd_renew.rs @@ -10,7 +10,7 @@ mod private // qqq : typed error pub fn cicd_renew() -> error::untyped::Result< () > { - action::cicd_renew + action::cicd_renew::action ( &std::env::current_dir()? ) diff --git a/module/move/willbe/src/command/list.rs b/module/move/willbe/src/command/list.rs index c1bb086099..83e252fc65 100644 --- a/module/move/willbe/src/command/list.rs +++ b/module/move/willbe/src/command/list.rs @@ -80,7 +80,7 @@ mod private .dependency_categories( categories ) .form(); - match action::list( o ) + match action::list_all( o ) { Ok( report ) => { diff --git a/module/move/willbe/src/command/main_header.rs b/module/move/willbe/src/command/main_header.rs index efd23e67c4..b29229e9bd 100644 --- a/module/move/willbe/src/command/main_header.rs +++ b/module/move/willbe/src/command/main_header.rs @@ -1,14 +1,14 @@ mod private { use crate::*; - use action; + // use action; use error::untyped::{ Error }; /// Generates header to main Readme.md file. // qqq : typed error pub fn readme_header_renew() -> error::untyped::Result< () > { - match action::readme_header_renew + match crate::action::main_header::action ( CrateDir::transitive_try_from::< AbsolutePath >( CurrentPath )? ) diff --git a/module/move/willbe/src/command/readme_headers_renew.rs b/module/move/willbe/src/command/readme_headers_renew.rs index 9a79a2b144..3f7af310a7 100644 --- a/module/move/willbe/src/command/readme_headers_renew.rs +++ b/module/move/willbe/src/command/readme_headers_renew.rs @@ -1,8 +1,8 @@ mod private { use crate::*; - use action; - use error::{ err }; + // use action; + // use error::{ err }; use std::fmt::{ Display, Formatter }; #[ derive( Debug, Default ) ] @@ -64,7 +64,6 @@ mod private } } - /// Aggregates two commands: `generate_modules_headers` & `generate_main_header` pub fn readme_headers_renew() -> error::untyped::Result< () > // qqq : use typed error { @@ -73,7 +72,7 @@ mod private let crate_dir = CrateDir::transitive_try_from::< AbsolutePath >( CurrentPath )?; let mut fail = false; - match action::readme_header_renew( crate_dir.clone() ) + match crate::action::main_header::action( crate_dir.clone() ) { Ok( r ) => { @@ -103,7 +102,7 @@ mod private if fail { eprintln!( "{report}" ); - Err( err!( "Something went wrong" ) ) + Err( error::untyped::format_err!( "Something went wrong" ) ) } else { diff --git a/module/move/willbe/src/command/workspace_renew.rs b/module/move/willbe/src/command/workspace_renew.rs index 7baa1515f6..6e43d29eea 100644 --- a/module/move/willbe/src/command/workspace_renew.rs +++ b/module/move/willbe/src/command/workspace_renew.rs @@ -23,7 +23,7 @@ mod private { let WorkspaceNewProperties { repository_url, branches } = o.props.try_into()?; let template = WorkspaceTemplate::default(); - action::workspace_renew + action::workspace_renew::action ( &std::env::current_dir()?, template, diff --git a/module/move/willbe/src/entity/channel.rs b/module/move/willbe/src/entity/channel.rs index cb45418c06..65fe6b7716 100644 --- a/module/move/willbe/src/entity/channel.rs +++ b/module/move/willbe/src/entity/channel.rs @@ -61,7 +61,7 @@ mod private .bin_path( program ) .args( options.into_iter().map( OsString::from ).collect::< Vec< _ > >() ) .current_path( path.as_ref().to_path_buf() ) - .run().map_err::< Error, _ >( | report | err!( report.to_string() ) )?; + .run().map_err::< Error, _ >( | report | error::untyped::format_err!( report.to_string() ) )?; let list = report .out diff --git a/module/move/willbe/src/entity/publish.rs b/module/move/willbe/src/entity/publish.rs index ed1e336129..497ee6c90f 100644 --- a/module/move/willbe/src/entity/publish.rs +++ b/module/move/willbe/src/entity/publish.rs @@ -161,7 +161,7 @@ mod private .collect(); for wanted in &self.roots { - let list = action::list + let list = action::list_all ( action::list::ListOptions::former() .path_to_manifest( wanted.clone() ) diff --git a/module/move/willbe/src/tool/cargo.rs b/module/move/willbe/src/tool/cargo.rs index 71590ecd45..4e0bd9a330 100644 --- a/module/move/willbe/src/tool/cargo.rs +++ b/module/move/willbe/src/tool/cargo.rs @@ -6,8 +6,8 @@ mod private use std::ffi::OsString; use std::path::PathBuf; - use error::err; - use error::untyped::format_err; + // use error::err; + // use error::untyped::format_err; use former::Former; use process_tools::process; // use process_tools::process::*; @@ -118,7 +118,7 @@ mod private .bin_path( program ) .args( options.into_iter().map( OsString::from ).collect::< Vec< _ > >() ) .current_path( args.path ) - .run().map_err( | report | err!( report.to_string() ) ) + .run().map_err( | report | error::untyped::format_err!( report.to_string() ) ) } } @@ -197,11 +197,11 @@ mod private } if args.retry_count > 0 { - Err( format_err!( "It took {} attempts, but still failed. Here are the errors:\n{}", args.retry_count + 1, results.into_iter().map( | r | format!( "- {r}" ) ).collect::< Vec< _ > >().join( "\n" ) ) ) + Err( error::untyped::format_err!( "It took {} attempts, but still failed. Here are the errors:\n{}", args.retry_count + 1, results.into_iter().map( | r | format!( "- {r}" ) ).collect::< Vec< _ > >().join( "\n" ) ) ) } else { - Err( results.remove( 0 ) ).map_err( | report | err!( report.to_string() ) ) + Err( results.remove( 0 ) ).map_err( | report | error::untyped::format_err!( report.to_string() ) ) } } } diff --git a/module/move/willbe/src/tool/collection.rs b/module/move/willbe/src/tool/collection.rs deleted file mode 100644 index edd7bec8c8..0000000000 --- a/module/move/willbe/src/tool/collection.rs +++ /dev/null @@ -1,12 +0,0 @@ -/// Internal namespace. -mod private -{ -} - -crate::mod_interface! -{ - - use ::collection_tools; - own use ::collection_tools::own::*; - -} diff --git a/module/move/willbe/src/tool/error.rs b/module/move/willbe/src/tool/error.rs deleted file mode 100644 index bc00b92ba9..0000000000 --- a/module/move/willbe/src/tool/error.rs +++ /dev/null @@ -1,21 +0,0 @@ -/// Internal namespace. -#[ allow( unused_imports ) ] -mod private -{ - use crate::tool::*; - use ::error_tools::own::*; - -} - -crate::mod_interface! -{ - // #![ debug ] - - use ::error_tools; - own use ::error_tools::own::*; - - // exposed use ErrWith; - // exposed use ResultWithReport; - // exposed use ::error_tools::Result; - -} diff --git a/module/move/willbe/src/tool/git.rs b/module/move/willbe/src/tool/git.rs index 828e4d3c64..05b582cd8a 100644 --- a/module/move/willbe/src/tool/git.rs +++ b/module/move/willbe/src/tool/git.rs @@ -7,7 +7,7 @@ mod private use std::ffi::OsString; use std::path::Path; use process_tools::process::*; - use error::err; + // use error::err; // qqq : group dependencies /// Adds changes to the Git staging area. @@ -56,7 +56,7 @@ mod private .bin_path( program ) .args( args.into_iter().map( OsString::from ).collect::< Vec< _ > >() ) .current_path( path.as_ref().to_path_buf() ) - .run().map_err( | report | err!( report.to_string() ) ) + .run().map_err( | report | error::untyped::format_err!( report.to_string() ) ) } } @@ -102,7 +102,7 @@ mod private .bin_path( program ) .args( args.into_iter().map( OsString::from ).collect::< Vec< _ > >() ) .current_path( path.as_ref().to_path_buf() ) - .run().map_err( | report | err!( report.to_string() ) ) + .run().map_err( | report | error::untyped::format_err!( report.to_string() ) ) } } @@ -148,7 +148,7 @@ mod private .bin_path( program ) .args( args.into_iter().map( OsString::from ).collect::< Vec< _ > >() ) .current_path( path.as_ref().to_path_buf() ) - .run().map_err( | report | err!( report.to_string() ) ) + .run().map_err( | report | error::untyped::format_err!( report.to_string() ) ) } } @@ -173,7 +173,7 @@ mod private where P : AsRef< Path >, { - if commits_count < 1 { return Err( err!( "Cannot reset, the count of commits must be greater than 0" ) ) } + if commits_count < 1 { return Err( error::untyped::format_err!( "Cannot reset, the count of commits must be greater than 0" ) ) } let ( program, args ) : ( _, Vec< _ > ) = ( "git", @@ -205,7 +205,7 @@ mod private .bin_path( program ) .args( args.into_iter().map( OsString::from ).collect::< Vec< _ > >() ) .current_path( path.as_ref().to_path_buf() ) - .run().map_err( | report | err!( report.to_string() ) ) + .run().map_err( | report | error::untyped::format_err!( report.to_string() ) ) } } @@ -232,7 +232,7 @@ mod private .bin_path( program ) .args( args.into_iter().map( OsString::from ).collect::< Vec< _ > >() ) .current_path( path.as_ref().to_path_buf() ) - .run().map_err( | report | err!( report.to_string() ) ) + .run().map_err( | report | error::untyped::format_err!( report.to_string() ) ) } } diff --git a/module/move/willbe/src/tool/mod.rs b/module/move/willbe/src/tool/mod.rs index 719b616b4b..7864cdc660 100644 --- a/module/move/willbe/src/tool/mod.rs +++ b/module/move/willbe/src/tool/mod.rs @@ -8,12 +8,15 @@ crate::mod_interface! orphan use super::cargo; /// Function and structures to work with collections. - layer collection; - orphan use super::collection; + // layer collection; + // orphan use super::collection; + use ::collection_tools; + // own use ::collection_tools::own::*; /// Errors handling. - layer error; - orphan use super::error; + // layer error; + // orphan use super::error; + use ::error_tools; /// Operate over files. layer files; diff --git a/module/move/willbe/tests/inc/action_tests/cicd_renew.rs b/module/move/willbe/tests/inc/action_tests/cicd_renew.rs index ffbbe5b570..75370b0db5 100644 --- a/module/move/willbe/tests/inc/action_tests/cicd_renew.rs +++ b/module/move/willbe/tests/inc/action_tests/cicd_renew.rs @@ -90,7 +90,7 @@ fn default_case() }; // Act - _ = action::cicd_renew( &temp ).unwrap(); + _ = action::cicd_renew::action( &temp ).unwrap(); dbg!( &file_path ); // Assert diff --git a/module/move/willbe/tests/inc/action_tests/features.rs b/module/move/willbe/tests/inc/action_tests/features.rs index 37a4b63cae..ea0e4e80a0 100644 --- a/module/move/willbe/tests/inc/action_tests/features.rs +++ b/module/move/willbe/tests/inc/action_tests/features.rs @@ -24,7 +24,7 @@ fn package_no_features() .form(); // Act - let report = willbe::action::features( options ).unwrap().to_string(); + let report = willbe::action::features::orphan::features( options ).unwrap().to_string(); // Assert assert!( report.contains( @@ -43,7 +43,7 @@ fn package_features() .form(); // Act - let report = willbe::action::features( options ).unwrap().to_string(); + let report = willbe::action::features::orphan::features( options ).unwrap().to_string(); // Assert assert!( report.contains( @@ -66,7 +66,7 @@ fn package_features_with_features_deps() .form(); // Act - let report = willbe::action::features( options ).unwrap().to_string(); + let report = willbe::action::features::orphan::features( options ).unwrap().to_string(); // Assert assert!( report.contains( @@ -89,7 +89,7 @@ fn workspace_no_features() .form(); // Act - let report = willbe::action::features( options ).unwrap().to_string(); + let report = willbe::action::features::orphan::features( options ).unwrap().to_string(); // Assert assert!( report.contains( @@ -118,7 +118,7 @@ fn workspace_features() .form(); // Act - let report = willbe::action::features( options ).unwrap().to_string(); + let report = willbe::action::features::orphan::features( options ).unwrap().to_string(); // Assert assert!( report.contains( @@ -156,7 +156,7 @@ fn workspace_features_with_features_deps() .form(); // Act - let report = willbe::action::features( options ).unwrap().to_string(); + let report = willbe::action::features::orphan::features( options ).unwrap().to_string(); // Assert assert!( report.contains( diff --git a/module/move/willbe/tests/inc/action_tests/list/data.rs b/module/move/willbe/tests/inc/action_tests/list/data.rs index 423baf654c..9100cf5dfb 100644 --- a/module/move/willbe/tests/inc/action_tests/list/data.rs +++ b/module/move/willbe/tests/inc/action_tests/list/data.rs @@ -44,7 +44,7 @@ mod chain_of_three_packages .form(); // Act - let output = action::list( args ).unwrap(); + let output = action::list_all( args ).unwrap(); // Assert let ListReport::Tree( trees ) = &output else { panic!( "Expected `Tree` format, but found another" ) }; @@ -85,7 +85,7 @@ mod chain_of_three_packages .form(); // Act - let output = action::list( args ).unwrap(); + let output = action::list_all( args ).unwrap(); // Assert let ListReport::List( names ) = &output else { panic!("Expected `Topological` format, but found another") }; @@ -106,7 +106,7 @@ mod chain_of_three_packages .form(); // Act - let output = action::list( args ).unwrap(); + let output = action::list_all( args ).unwrap(); // Assert let ListReport::List( names ) = &output else { panic!( "Expected `Topological` format, but found another" ) }; @@ -145,7 +145,7 @@ mod package_with_remote_dependency .form(); // Act - let output = action::list( args ).unwrap(); + let output = action::list_all( args ).unwrap(); // Assert let ListReport::Tree( trees ) = &output else { panic!( "Expected `Tree` format, but found another" ) }; @@ -183,7 +183,7 @@ mod package_with_remote_dependency .form(); // Act - let output = action::list( args ).unwrap(); + let output = action::list_all( args ).unwrap(); // Assert let ListReport::List( names ) = &output else { panic!( "Expected `Topological` format, but found another" ) }; @@ -208,7 +208,7 @@ mod package_with_remote_dependency .form(); // Act - let output = action::list( args ).unwrap(); + let output = action::list_all( args ).unwrap(); // Assert let ListReport::List( names ) = &output else { panic!( "Expected `Topological` format, but found another" ) }; @@ -242,7 +242,7 @@ mod workspace_with_cyclic_dependency .form(); // Act - let output = action::list( args ).unwrap(); + let output = action::list_all( args ).unwrap(); // Assert let ListReport::Tree( trees ) = &output else { panic!( "Expected `Tree` format, but found another" ) }; @@ -304,7 +304,7 @@ mod workspace_with_cyclic_dependency .form(); // Act - let output = action::list( args ); + let output = action::list_all( args ); // Assert diff --git a/module/move/willbe/tests/inc/action_tests/main_header.rs b/module/move/willbe/tests/inc/action_tests/main_header.rs index 82f1b89fba..6f65b44495 100644 --- a/module/move/willbe/tests/inc/action_tests/main_header.rs +++ b/module/move/willbe/tests/inc/action_tests/main_header.rs @@ -25,7 +25,7 @@ fn tag_shout_stay() let temp = arrange( "single_module" ); // Act - _ = action::readme_header_renew( AbsolutePath::try_from( temp.path() ).unwrap() ).unwrap(); + _ = action::readme_header_renew::orphan::readme_header_renew( AbsolutePath::try_from( temp.path() ).unwrap() ).unwrap(); let mut file = std::fs::File::open( temp.path().join( "Readme.md" ) ).unwrap(); @@ -45,7 +45,7 @@ fn branch_cell() let temp = arrange( "single_module" ); // Act - _ = action::readme_header_renew( AbsolutePath::try_from( temp.path() ).unwrap() ).unwrap(); + _ = action::readme_header_renew::orphan::readme_header_renew( AbsolutePath::try_from( temp.path() ).unwrap() ).unwrap(); let mut file = std::fs::File::open( temp.path().join( "Readme.md" ) ).unwrap(); @@ -64,7 +64,7 @@ fn discord_cell() let temp = arrange( "single_module" ); // Act - _ = action::readme_header_renew( AbsolutePath::try_from( temp.path() ).unwrap() ).unwrap(); + _ = action::readme_header_renew::orphan::readme_header_renew( AbsolutePath::try_from( temp.path() ).unwrap() ).unwrap(); let mut file = std::fs::File::open( temp.path().join( "Readme.md" ) ).unwrap(); @@ -83,7 +83,7 @@ fn gitpod_cell() let temp = arrange( "single_module" ); // Act - _ = action::readme_header_renew( AbsolutePath::try_from( temp.path() ).unwrap() ).unwrap(); + _ = action::readme_header_renew::orphan::readme_header_renew( AbsolutePath::try_from( temp.path() ).unwrap() ).unwrap(); let mut file = std::fs::File::open( temp.path().join( "Readme.md" ) ).unwrap(); @@ -102,7 +102,7 @@ fn docs_cell() let temp = arrange( "single_module" ); // Act - _ = action::readme_header_renew( AbsolutePath::try_from( temp.path() ).unwrap() ).unwrap(); + _ = action::readme_header_renew::orphan::readme_header_renew( AbsolutePath::try_from( temp.path() ).unwrap() ).unwrap(); let mut file = std::fs::File::open( temp.path().join( "Readme.md" ) ).unwrap(); @@ -121,7 +121,7 @@ fn without_fool_config() let temp = arrange( "single_module_without_master_branch_and_discord" ); // Act - _ = action::readme_header_renew( AbsolutePath::try_from( temp.path() ).unwrap() ).unwrap(); + _ = action::readme_header_renew::orphan::readme_header_renew( AbsolutePath::try_from( temp.path() ).unwrap() ).unwrap(); let mut file = std::fs::File::open( temp.path().join( "Readme.md" ) ).unwrap(); @@ -141,13 +141,13 @@ fn idempotency() let temp = arrange( "single_module" ); // Act - _ = action::readme_header_renew( AbsolutePath::try_from( temp.path() ).unwrap() ).unwrap(); + _ = action::readme_header_renew::orphan::readme_header_renew( AbsolutePath::try_from( temp.path() ).unwrap() ).unwrap(); let mut file = std::fs::File::open( temp.path().join( "Readme.md" ) ).unwrap(); let mut actual1 = String::new(); _ = file.read_to_string( &mut actual1 ).unwrap(); drop( file ); - _ = action::readme_header_renew( AbsolutePath::try_from( temp.path() ).unwrap() ).unwrap(); + _ = action::readme_header_renew::orphan::readme_header_renew( AbsolutePath::try_from( temp.path() ).unwrap() ).unwrap(); let mut file = std::fs::File::open( temp.path().join( "Readme.md" ) ).unwrap(); let mut actual2 = String::new(); _ = file.read_to_string( &mut actual2 ).unwrap(); @@ -164,5 +164,5 @@ fn without_needed_config() // Arrange let temp = arrange( "variadic_tag_configurations" ); // Act - _ = action::readme_header_renew( AbsolutePath::try_from( temp.path() ).unwrap() ).unwrap(); + _ = action::readme_header_renew::orphan::readme_header_renew( AbsolutePath::try_from( temp.path() ).unwrap() ).unwrap(); } \ No newline at end of file diff --git a/module/move/willbe/tests/inc/action_tests/readme_health_table_renew.rs b/module/move/willbe/tests/inc/action_tests/readme_health_table_renew.rs index cce1e9065a..909a186116 100644 --- a/module/move/willbe/tests/inc/action_tests/readme_health_table_renew.rs +++ b/module/move/willbe/tests/inc/action_tests/readme_health_table_renew.rs @@ -23,7 +23,7 @@ fn without_any_toml_configurations_test() // Arrange let temp = arrange( "without_any_toml_configurations" ); // Act - _ = action::readme_health_table_renew( &temp ).unwrap(); + _ = action::readme_health_table_renew::orphan::readme_health_table_renew( &temp ).unwrap(); } #[ test ] @@ -33,7 +33,7 @@ fn tags_should_stay() let temp = arrange( "without_module_toml_configurations" ); // Act - _ = action::readme_health_table_renew( &temp ).unwrap(); + _ = action::readme_health_table_renew::orphan::readme_health_table_renew( &temp ).unwrap(); // Assert let mut file = std::fs::File::open( temp.path().join( "readme.md" ) ).unwrap(); @@ -52,7 +52,7 @@ fn stability_experimental_by_default() let temp = arrange( "without_module_toml_configurations" ); // Act - _ = action::readme_health_table_renew( &temp ).unwrap(); + _ = action::readme_health_table_renew::orphan::readme_health_table_renew( &temp ).unwrap(); // Assert let mut file = std::fs::File::open( temp.path().join( "readme.md" ) ).unwrap(); @@ -70,7 +70,7 @@ fn stability_and_repository_from_module_toml() let temp = arrange( "without_workspace_toml_configurations" ); // Act - _ = action::readme_health_table_renew( &temp ).unwrap(); + _ = action::readme_health_table_renew::orphan::readme_health_table_renew( &temp ).unwrap(); // Assert let mut file = std::fs::File::open( temp.path().join( "readme.md" ) ).unwrap(); @@ -101,7 +101,7 @@ fn variadic_tag_configuration_test() let temp = arrange( "variadic_tag_configurations" ); // Act - _ = action::readme_health_table_renew( &temp ).unwrap(); + _ = action::readme_health_table_renew::orphan::readme_health_table_renew( &temp ).unwrap(); // Assert let mut file = std::fs::File::open( temp.path().join( "readme.md" ) ).unwrap(); @@ -121,7 +121,7 @@ fn module_cell() let temp = arrange( "full_config" ); // Act - _ = action::readme_health_table_renew( &temp ).unwrap(); + _ = action::readme_health_table_renew::orphan::readme_health_table_renew( &temp ).unwrap(); // Assert let mut file = std::fs::File::open( temp.path().join( "readme.md" ) ).unwrap(); @@ -139,7 +139,7 @@ fn stability_cell() let temp = arrange( "full_config" ); // Act - _ = action::readme_health_table_renew( &temp ).unwrap(); + _ = action::readme_health_table_renew::orphan::readme_health_table_renew( &temp ).unwrap(); // Assert let mut file = std::fs::File::open( temp.path().join( "readme.md" ) ).unwrap(); @@ -157,7 +157,7 @@ fn branches_cell() let temp = arrange( "full_config" ); // Act - _ = action::readme_health_table_renew( &temp ).unwrap(); + _ = action::readme_health_table_renew::orphan::readme_health_table_renew( &temp ).unwrap(); // Assert let mut file = std::fs::File::open( temp.path().join( "readme.md" ) ).unwrap(); @@ -175,7 +175,7 @@ fn docs_cell() let temp = arrange( "full_config" ); // Act - _ = action::readme_health_table_renew( &temp ).unwrap(); + _ = action::readme_health_table_renew::orphan::readme_health_table_renew( &temp ).unwrap(); // Assert let mut file = std::fs::File::open( temp.path().join( "readme.md" ) ).unwrap(); @@ -193,7 +193,7 @@ fn sample_cell() let temp = arrange( "full_config" ); // Act - _ = action::readme_health_table_renew( &temp ).unwrap(); + _ = action::readme_health_table_renew::orphan::readme_health_table_renew( &temp ).unwrap(); // Assert let mut file = std::fs::File::open( temp.path().join( "readme.md" ) ).unwrap(); diff --git a/module/move/willbe/tests/inc/action_tests/readme_modules_headers_renew.rs b/module/move/willbe/tests/inc/action_tests/readme_modules_headers_renew.rs index db82f365ba..1d4d012d5d 100644 --- a/module/move/willbe/tests/inc/action_tests/readme_modules_headers_renew.rs +++ b/module/move/willbe/tests/inc/action_tests/readme_modules_headers_renew.rs @@ -32,7 +32,9 @@ fn tags_should_stay() let temp = arrange( "single_module" ); // Act - _ = action::readme_modules_headers_renew( CrateDir::try_from( temp.path() ).unwrap() ).unwrap(); + _ = action::main_header::action( CrateDir::try_from( temp.path() ).unwrap() ).unwrap(); + // _ = action::main_header::action( CrateDir::try_from( temp.path() ).unwrap() ).unwrap(); + let mut file = std::fs::File::open( temp.path().join( "test_module" ).join( "Readme.md" ) ).unwrap(); let mut actual = String::new(); @@ -51,7 +53,7 @@ fn default_stability() let temp = arrange( "single_module" ); // Act - _ = action::readme_modules_headers_renew( CrateDir::try_from( temp.path() ).unwrap() ).unwrap(); + _ = action::main_header::action( CrateDir::try_from( temp.path() ).unwrap() ).unwrap(); let mut file = std::fs::File::open( temp.path().join( "test_module" ).join( "Readme.md" ) ).unwrap(); let mut actual = String::new(); @@ -70,7 +72,7 @@ fn docs() let temp = arrange( "single_module" ); // Act - _ = action::readme_modules_headers_renew( CrateDir::try_from( temp.path() ).unwrap() ).unwrap(); + _ = action::main_header::action( CrateDir::try_from( temp.path() ).unwrap() ).unwrap(); let mut file = std::fs::File::open( temp.path().join( "test_module" ).join( "Readme.md" ) ).unwrap(); let mut actual = String::new(); @@ -88,7 +90,7 @@ fn no_gitpod() let temp = arrange("single_module"); // Act - _ = action::readme_modules_headers_renew( CrateDir::try_from( temp.path() ).unwrap() ).unwrap(); + _ = action::main_header::action( CrateDir::try_from( temp.path() ).unwrap() ).unwrap(); let mut file = std::fs::File::open(temp.path().join("test_module").join("Readme.md")).unwrap(); let mut actual = String::new(); @@ -105,7 +107,7 @@ fn with_gitpod() let temp = arrange( "single_module_with_example" ); // Act - _ = action::readme_modules_headers_renew( CrateDir::try_from( temp.path() ).unwrap() ).unwrap(); + _ = action::main_header::action( CrateDir::try_from( temp.path() ).unwrap() ).unwrap(); let mut file = std::fs::File::open( temp.path().join( "module" ).join( "test_module" ).join( "Readme.md" ) ).unwrap(); let mut actual = String::new(); @@ -123,7 +125,7 @@ fn discord() let temp = arrange( "single_module" ); // Act - _ = action::readme_modules_headers_renew( CrateDir::try_from( temp.path() ).unwrap() ).unwrap(); + _ = action::main_header::action( CrateDir::try_from( temp.path() ).unwrap() ).unwrap(); let mut file = std::fs::File::open( temp.path().join( "test_module" ).join( "Readme.md" ) ).unwrap(); let mut actual = String::new(); @@ -141,7 +143,7 @@ fn status() let temp = arrange( "single_module" ); // Act - _ = action::readme_modules_headers_renew( CrateDir::try_from( temp.path() ).unwrap() ).unwrap(); + _ = action::main_header::action( CrateDir::try_from( temp.path() ).unwrap() ).unwrap(); let mut file = std::fs::File::open( temp.path().join( "test_module" ).join( "Readme.md" ) ).unwrap(); let mut actual = String::new(); @@ -159,13 +161,13 @@ fn idempotency() let temp = arrange( "single_module" ); // Act - _ = action::readme_modules_headers_renew( CrateDir::try_from( temp.path() ).unwrap() ).unwrap(); + _ = action::main_header::action( CrateDir::try_from( temp.path() ).unwrap() ).unwrap(); let mut file = std::fs::File::open( temp.path().join( "test_module" ).join( "Readme.md" ) ).unwrap(); let mut actual1 = String::new(); _ = file.read_to_string( &mut actual1 ).unwrap(); drop( file ); - _ = action::readme_modules_headers_renew( CrateDir::try_from( temp.path() ).unwrap() ).unwrap(); + _ = action::main_header::action( CrateDir::try_from( temp.path() ).unwrap() ).unwrap(); let mut file = std::fs::File::open( temp.path().join( "test_module" ).join( "Readme.md" ) ).unwrap(); let mut actual2 = String::new(); _ = file.read_to_string( &mut actual2 ).unwrap(); @@ -180,7 +182,7 @@ fn with_many_members_and_varius_config() { let temp = arrange( "three_packages" ); - _ = action::readme_modules_headers_renew( CrateDir::try_from( temp.path() ).unwrap() ).unwrap(); + _ = action::main_header::action( CrateDir::try_from( temp.path() ).unwrap() ).unwrap(); let mut file_b = std::fs::File::open( temp.path().join( "b" ).join( "Readme.md" ) ).unwrap(); let mut file_c = std::fs::File::open( temp.path().join( "c" ).join( "Readme.md" ) ).unwrap(); @@ -207,5 +209,5 @@ fn without_needed_config() let temp = arrange( "variadic_tag_configurations" ); // Act - _ = action::readme_modules_headers_renew( CrateDir::try_from( temp.path() ).unwrap() ).unwrap(); + _ = action::main_header::action( CrateDir::try_from( temp.path() ).unwrap() ).unwrap(); } diff --git a/module/move/willbe/tests/inc/action_tests/workspace_renew.rs b/module/move/willbe/tests/inc/action_tests/workspace_renew.rs index 9dbfcea23d..4790c39f25 100644 --- a/module/move/willbe/tests/inc/action_tests/workspace_renew.rs +++ b/module/move/willbe/tests/inc/action_tests/workspace_renew.rs @@ -26,7 +26,7 @@ fn default_case() create_dir(temp.join("test_project_name" )).unwrap(); // Act - _ = workspace_renew( &temp.path().join( "test_project_name" ), WorkspaceTemplate::default(), "https://github.con/Username/TestRepository".to_string(), vec![ "master".to_string() ] ).unwrap(); + _ = workspace_renew::action( &temp.path().join( "test_project_name" ), WorkspaceTemplate::default(), "https://github.con/Username/TestRepository".to_string(), vec![ "master".to_string() ] ).unwrap(); // Assets assert!( temp_path.join( "module" ).exists() ); @@ -57,7 +57,7 @@ fn non_empty_dir() let temp = arrange( "single_module" ); // Act - let r = workspace_renew( temp.path(), WorkspaceTemplate::default(), "".to_string(), vec![] ); + let r = workspace_renew::action( temp.path(), WorkspaceTemplate::default(), "".to_string(), vec![] ); // Assert assert!( r.is_err() ); diff --git a/module/move/willbe/tests/inc/tool/graph_test.rs b/module/move/willbe/tests/inc/tool/graph_test.rs index 75a2b29db3..0ad57fa024 100644 --- a/module/move/willbe/tests/inc/tool/graph_test.rs +++ b/module/move/willbe/tests/inc/tool/graph_test.rs @@ -3,7 +3,7 @@ use super::*; // qqq : for Bohdan : bad. don't import the_module::* // use the_module::*; use the_module::graph::toposort; -use collection::HashMap; +use test_tools::collection::HashMap; use petgraph::Graph; use willbe::graph::topological_sort_with_grouping; diff --git a/module/postponed/non_std/Cargo.toml b/module/postponed/non_std/Cargo.toml index 516d197d99..1f684f13cb 100644 --- a/module/postponed/non_std/Cargo.toml +++ b/module/postponed/non_std/Cargo.toml @@ -72,7 +72,7 @@ meta_default = [ "meta_mod_interface", # "meta_former", # "meta_options", - "meta_constructors", + #"meta_constructors", "meta_idents_concat", ] meta_full = [ @@ -82,7 +82,7 @@ meta_full = [ "meta_mod_interface", # "meta_former", # "meta_options", - "meta_constructors", + #"meta_constructors", "meta_idents_concat", ] meta_no_std = [ "wtools/meta_no_std" ] diff --git a/module/postponed/std_tools/Cargo.toml b/module/postponed/std_tools/Cargo.toml index 44d29afa00..928fe93678 100644 --- a/module/postponed/std_tools/Cargo.toml +++ b/module/postponed/std_tools/Cargo.toml @@ -73,7 +73,7 @@ meta_default = [ "meta_mod_interface", # "meta_former", # "meta_options", - "meta_constructors", + #"meta_constructors", "meta_idents_concat", ] meta_full = [ @@ -83,7 +83,7 @@ meta_full = [ "meta_mod_interface", # "meta_former", # "meta_options", - "meta_constructors", + #"meta_constructors", "meta_idents_concat", ] meta_no_std = [ "wtools/meta_no_std" ] diff --git a/module/postponed/std_x/Cargo.toml b/module/postponed/std_x/Cargo.toml index 5693aa40a1..2ee0a6f55c 100644 --- a/module/postponed/std_x/Cargo.toml +++ b/module/postponed/std_x/Cargo.toml @@ -75,7 +75,7 @@ meta_default = [ "meta_mod_interface", # "meta_former", # "meta_options", - "meta_constructors", + #"meta_constructors", "meta_idents_concat", ] meta_full = [ @@ -85,7 +85,7 @@ meta_full = [ "meta_mod_interface", # "meta_former", # "meta_options", - "meta_constructors", + #"meta_constructors", "meta_idents_concat", ] meta_no_std = [ "wtools/meta_no_std" ] diff --git a/module/step/meta/src/module/terminal.rs b/module/step/meta/src/module/terminal.rs index 93289921c5..4a88acf6a9 100644 --- a/module/step/meta/src/module/terminal.rs +++ b/module/step/meta/src/module/terminal.rs @@ -1,4 +1,6 @@ +/// Mechanism to include tests only to terminal crate. +/// It exclude code in terminal module ( crate ), but include for aggregating module ( crate ). #[ macro_export ] macro_rules! only_for_terminal_module { @@ -8,6 +10,8 @@ macro_rules! only_for_terminal_module }; } +/// Mechanism to include tests only to aggregating crate. +/// It exclude code in terminal module ( crate ), but include for aggregating module ( crate ). #[ macro_export ] macro_rules! only_for_aggregating_module { diff --git a/module/step/meta/tests/_conditional/local_module.rs b/module/step/meta/tests/_conditional/local_module.rs index 93289921c5..4a88acf6a9 100644 --- a/module/step/meta/tests/_conditional/local_module.rs +++ b/module/step/meta/tests/_conditional/local_module.rs @@ -1,4 +1,6 @@ +/// Mechanism to include tests only to terminal crate. +/// It exclude code in terminal module ( crate ), but include for aggregating module ( crate ). #[ macro_export ] macro_rules! only_for_terminal_module { @@ -8,6 +10,8 @@ macro_rules! only_for_terminal_module }; } +/// Mechanism to include tests only to aggregating crate. +/// It exclude code in terminal module ( crate ), but include for aggregating module ( crate ). #[ macro_export ] macro_rules! only_for_aggregating_module { diff --git a/module/step/meta/tests/_conditional/wtools.rs b/module/step/meta/tests/_conditional/wtools.rs index e6bb553f35..ba669c790d 100644 --- a/module/step/meta/tests/_conditional/wtools.rs +++ b/module/step/meta/tests/_conditional/wtools.rs @@ -1,4 +1,6 @@ +/// Mechanism to include tests only to terminal crate. +/// It exclude code in terminal module ( crate ), but include for aggregating module ( crate ). #[ macro_export ] macro_rules! only_for_terminal_module { @@ -7,6 +9,8 @@ macro_rules! only_for_terminal_module } } +/// Mechanism to include tests only to aggregating crate. +/// It exclude code in terminal module ( crate ), but include for aggregating module ( crate ). #[ macro_export ] macro_rules! only_for_aggregating_module { From e4299271beddc5dc0dc7a9152069b58e10ba3dbd Mon Sep 17 00:00:00 2001 From: ".Barsik" Date: Wed, 6 Nov 2024 09:48:51 +0200 Subject: [PATCH 38/67] READY: (alpha/willbe): `.publish` and `.publish.diff` new options (#1472) * feat: `exclude_dev_dependencies` flag for `.publish` and `.publish.diff`. Default value is `true` * feat: `commit_changes` flag for `.publish`. Default value is `true` * chore: `commit_changes` flag for `.publish`. Default value is `false` * chore: `exclude_dev_dependencies` Default value is `false` --- module/move/willbe/src/action/publish.rs | 5 + module/move/willbe/src/action/publish_diff.rs | 22 +++-- module/move/willbe/src/command/mod.rs | 15 +++ module/move/willbe/src/command/publish.rs | 12 ++- .../move/willbe/src/command/publish_diff.rs | 13 ++- module/move/willbe/src/entity/publish.rs | 92 +++++++++++++------ module/move/willbe/src/tool/cargo.rs | 75 +++++++++++++++ 7 files changed, 192 insertions(+), 42 deletions(-) diff --git a/module/move/willbe/src/action/publish.rs b/module/move/willbe/src/action/publish.rs index 58412a2d7a..048220bc20 100644 --- a/module/move/willbe/src/action/publish.rs +++ b/module/move/willbe/src/action/publish.rs @@ -109,6 +109,7 @@ mod private /// /// # Arguments /// * `patterns` - A vector of patterns specifying the folders to search for packages. + /// * `exclude_dev_dependencies` - A boolean value indicating whether to exclude dev dependencies from manifest before publish. /// * `dry` - A boolean value indicating whether to perform a dry run. /// * `temp` - A boolean value indicating whether to use a temporary directory. /// @@ -119,6 +120,8 @@ mod private ( patterns : Vec< String >, channel : channel::Channel, + exclude_dev_dependencies : bool, + commit_changes : bool, dry : bool, temp : bool ) @@ -233,6 +236,8 @@ mod private .channel( channel ) .workspace_dir( CrateDir::try_from( workspace_root_dir ).unwrap() ) .option_base_temp_dir( dir.clone() ) + .exclude_dev_dependencies( exclude_dev_dependencies ) + .commit_changes( commit_changes ) .dry( dry ) .roots( roots ) .packages( queue ) diff --git a/module/move/willbe/src/action/publish_diff.rs b/module/move/willbe/src/action/publish_diff.rs index 11b1758ca8..500a73a7f8 100644 --- a/module/move/willbe/src/action/publish_diff.rs +++ b/module/move/willbe/src/action/publish_diff.rs @@ -22,6 +22,7 @@ mod private pub struct PublishDiffOptions { path : PathBuf, + exclude_dev_dependencies : bool, keep_archive : Option< PathBuf >, } @@ -141,16 +142,17 @@ mod private let name = &package.name()?; let version = &package.version()?; - _ = cargo::pack - ( - cargo::PackOptions::former() - .path( dir.as_ref() ) - .allow_dirty( true ) - .checking_consistency( false ) - .dry( false ).form() - )?; - let l = CrateArchive::read( packed_crate::local_path( name, version, dir )? )?; - let r = CrateArchive::download_crates_io( name, version ).unwrap(); + _ = cargo::pack + ( + cargo::PackOptions::former() + .path( dir.as_ref() ) + .allow_dirty( true ) + .checking_consistency( false ) + .exclude_dev_dependencies( o.exclude_dev_dependencies) + .dry( false ).form() + )?; + let l = CrateArchive::read( packed_crate::local_path( name, version, dir )? )?; + let r = CrateArchive::download_crates_io( name, version ).unwrap(); if let Some( out_path ) = &o.keep_archive diff --git a/module/move/willbe/src/command/mod.rs b/module/move/willbe/src/command/mod.rs index bae53834e1..5c81dcf47b 100644 --- a/module/move/willbe/src/command/mod.rs +++ b/module/move/willbe/src/command/mod.rs @@ -25,6 +25,16 @@ mod private .kind( Type::String ) .optional( true ) .end() + .property( "exclude_dev_dependencies" ) + .hint( "Setting this option to true will temporarily remove development dependencies before executing the command, then restore them afterward. Default is `true`." ) + .kind( Type::Bool ) + .optional( true ) + .end() + .property( "commit_changes" ) + .hint( "Indicates whether changes should be committed. Default is `false`." ) + .kind( Type::Bool ) + .optional( true ) + .end() .property( "dry" ) .hint( "Enables 'dry run'. Does not publish, only simulates. Default is `true`." ) .kind( Type::Bool ) @@ -47,6 +57,11 @@ mod private .kind( Type::Path ) .optional( true ) .end() + .property( "exclude_dev_dependencies" ) + .hint( "Setting this option to true will temporarily remove development dependencies before executing the command, then restore them afterward. Default is `true`." ) + .kind( Type::Bool ) + .optional( true ) + .end() .property( "keep_archive" ) .hint( "Save remote package version to the specified path" ) .kind( Type::Path ) diff --git a/module/move/willbe/src/command/publish.rs b/module/move/willbe/src/command/publish.rs index a70af4265d..b9ca441ef5 100644 --- a/module/move/willbe/src/command/publish.rs +++ b/module/move/willbe/src/command/publish.rs @@ -15,6 +15,10 @@ mod private { #[ former( default = Channel::Stable ) ] channel : Channel, + #[ former( default = false ) ] + exclude_dev_dependencies : bool, + #[ former( default = false ) ] + commit_changes : bool, #[ former( default = true ) ] dry : bool, #[ former( default = true ) ] @@ -52,10 +56,12 @@ mod private let PublishProperties { channel, + exclude_dev_dependencies, + commit_changes, dry, temp } = o.props.try_into()?; - let plan = action::publish_plan( patterns, channel, dry, temp ) + let plan = action::publish_plan( patterns, channel, exclude_dev_dependencies, commit_changes, dry, temp ) .context( "Failed to plan the publication process" )?; let mut formatted_plan = String::new(); @@ -110,6 +116,10 @@ mod private else { this }; + this = if let Some( v ) = value + .get_owned( "exclude_dev_dependencies" ) { this.exclude_dev_dependencies::< bool >( v ) } else { this }; + this = if let Some( v ) = value + .get_owned( "commit_changes" ) { this.commit_changes::< bool >( v ) } else { this }; this = if let Some( v ) = value .get_owned( "dry" ) { this.dry::< bool >( v ) } else { this }; this = if let Some( v ) = value diff --git a/module/move/willbe/src/command/publish_diff.rs b/module/move/willbe/src/command/publish_diff.rs index 4691331866..74cdbcbc48 100644 --- a/module/move/willbe/src/command/publish_diff.rs +++ b/module/move/willbe/src/command/publish_diff.rs @@ -13,6 +13,8 @@ mod private #[ derive( former::Former ) ] struct PublishDiffProperties { + #[ former( default = false ) ] + exclude_dev_dependencies : bool, keep_archive : Option< PathBuf >, } @@ -33,10 +35,11 @@ mod private pub fn publish_diff( o : VerifiedCommand ) -> error::untyped::Result< () > // qqq : use typed error { let path : PathBuf = o.args.get_owned( 0 ).unwrap_or( std::env::current_dir()? ); - let PublishDiffProperties { keep_archive } = o.props.try_into()?; + let PublishDiffProperties { keep_archive, exclude_dev_dependencies } = o.props.try_into()?; let mut o = action::PublishDiffOptions::former() - .path( path ); + .path( path ) + .exclude_dev_dependencies( exclude_dev_dependencies ); if let Some( k ) = keep_archive.clone() { o = o.keep_archive( k ); } let o = o.form(); @@ -57,6 +60,12 @@ mod private { let mut this = Self::former(); + this = if let Some( v ) = value + .get_owned( "exclude_dev_dependencies" ) + { this.exclude_dev_dependencies::< bool >( v ) } + else + { this }; + this = if let Some( v ) = value .get_owned( "keep_archive" ) { this.keep_archive::< PathBuf >( v ) } diff --git a/module/move/willbe/src/entity/publish.rs b/module/move/willbe/src/entity/publish.rs index 497ee6c90f..f7cc9965c8 100644 --- a/module/move/willbe/src/entity/publish.rs +++ b/module/move/willbe/src/entity/publish.rs @@ -26,7 +26,7 @@ mod private /// Options for bumping the package version. pub bump : version::BumpOptions, /// Git options related to the package. - pub git_options : entity::git::GitOptions, + pub git_options : Option< entity::git::GitOptions >, /// Options for publishing the package using Cargo. pub publish : cargo::PublishOptions, /// Indicates whether the process should be dry-run (no actual publishing). @@ -42,6 +42,9 @@ mod private package : package::Package< 'a >, channel : channel::Channel, base_temp_dir : Option< path::PathBuf >, + exclude_dev_dependencies : bool, + #[ former( default = true ) ] + commit_changes : bool, #[ former( default = true ) ] dry : bool, } @@ -58,6 +61,7 @@ mod private channel : self.channel, allow_dirty : self.dry, checking_consistency : !self.dry, + exclude_dev_dependencies : self.exclude_dev_dependencies, temp_path : self.base_temp_dir.clone(), dry : self.dry, }; @@ -73,17 +77,21 @@ mod private dependencies : dependencies.clone(), dry : self.dry, }; - let git_options = entity::git::GitOptions + let git_options = if self.commit_changes { - git_root : workspace_root, - items : dependencies.iter().chain([ &crate_dir ]).map( | d | d.clone().absolute_path().join( "Cargo.toml" ) ).collect(), - message : format!( "{}-v{}", self.package.name().unwrap(), new_version ), - dry : self.dry, - }; + Some( entity::git::GitOptions + { + git_root : workspace_root, + items : dependencies.iter().chain([ &crate_dir ]).map( | d | d.clone().absolute_path().join( "Cargo.toml" ) ).collect(), + message : format!( "{}-v{}", self.package.name().unwrap(), new_version ), + dry : self.dry, + }) + } else { None }; let publish = cargo::PublishOptions { path : crate_dir.clone().absolute_path().inner(), temp_path : self.base_temp_dir.clone(), + exclude_dev_dependencies : self.exclude_dev_dependencies, retry_count : 2, dry : self.dry, }; @@ -121,6 +129,14 @@ mod private /// Release channels for rust. pub channel : channel::Channel, + /// Setting this option to true will temporarily remove development dependencies before executing the command, then restore them afterward. + #[ allow( dead_code ) ] // former related + pub exclude_dev_dependencies : bool, + + /// Indicates whether changes should be committed. + #[ former( default = true ) ] + pub commit_changes : bool, + /// `dry` - A boolean value indicating whether to do a dry run. If set to `true`, the application performs /// a simulated run without making any actual changes. If set to `false`, the operations are actually executed. /// This property is optional and defaults to `true`. @@ -246,6 +262,14 @@ mod private { plan = plan.dry( dry ); } + if let Some( exclude_dev_dependencies ) = &self.storage.exclude_dev_dependencies + { + plan = plan.exclude_dev_dependencies( *exclude_dev_dependencies ) + } + if let Some( commit_changes ) = &self.storage.commit_changes + { + plan = plan.commit_changes( *commit_changes ) + } let plan = plan .channel( channel ) .package( package ) @@ -362,45 +386,55 @@ mod private } = instruction; pack.dry = dry; bump.dry = dry; - git_options.dry = dry; + git_options.as_mut().map( | d | d.dry = dry ); publish.dry = dry; report.get_info = Some( cargo::pack( pack ).err_with_report( &report )? ); // aaa : redundant field? // aaa : removed let bump_report = version::bump( bump ).err_with_report( &report )?; report.bump = Some( bump_report.clone() ); - let git_root = git_options.git_root.clone(); - let git = match entity::git::perform_git_commit( git_options ) + + let git_root = git_options.as_ref().map( | g | g.git_root.clone() ); + if let Some( git_options ) = git_options { - Ok( git ) => git, - Err( e ) => + let git = match entity::git::perform_git_commit( git_options ) { - version::revert( &bump_report ) - .map_err( | le | format_err!( "Base error:\n{}\nRevert error:\n{}", e.to_string().replace( '\n', "\n\t" ), le.to_string().replace( '\n', "\n\t" ) ) ) - .err_with_report( &report )?; - return Err(( report, e )); - } - }; - report.add = git.add; - report.commit = git.commit; + Ok( git ) => git, + Err( e ) => + { + version::revert( &bump_report ) + .map_err( | le | format_err!( "Base error:\n{}\nRevert error:\n{}", e.to_string().replace( '\n', "\n\t" ), le.to_string().replace( '\n', "\n\t" ) ) ) + .err_with_report( &report )?; + return Err(( report, e )); + } + }; + report.add = git.add; + report.commit = git.commit; + } report.publish = match cargo::publish( publish ) { Ok( publish ) => Some( publish ), Err( e ) => { - tool::git::reset( git_root.as_ref(), true, 1, false ) - .map_err - ( - | le | - format_err!( "Base error:\n{}\nRevert error:\n{}", e.to_string().replace( '\n', "\n\t" ), le.to_string().replace( '\n', "\n\t" ) ) - ) - .err_with_report( &report )?; + if let Some( git_root ) = git_root.as_ref() + { + tool::git::reset( git_root.as_ref(), true, 1, false ) + .map_err + ( + | le | + format_err!( "Base error:\n{}\nRevert error:\n{}", e.to_string().replace( '\n', "\n\t" ), le.to_string().replace( '\n', "\n\t" ) ) + ) + .err_with_report( &report )?; + } return Err(( report, e )); } }; - let res = tool::git::push( &git_root, dry ).err_with_report( &report )?; - report.push = Some( res ); + if let Some( git_root ) = git_root.as_ref() + { + let res = tool::git::push( &git_root, dry ).err_with_report( &report )?; + report.push = Some( res ); + } Ok( report ) } diff --git a/module/move/willbe/src/tool/cargo.rs b/module/move/willbe/src/tool/cargo.rs index 4e0bd9a330..780defb921 100644 --- a/module/move/willbe/src/tool/cargo.rs +++ b/module/move/willbe/src/tool/cargo.rs @@ -1,6 +1,8 @@ /// Internal namespace. mod private { + use crate::*; + #[ allow( unused_imports ) ] use crate::tool::*; @@ -47,6 +49,9 @@ mod private // aaa : don't abuse negative form, rename to checking_consistency // renamed and changed logic pub( crate ) checking_consistency : bool, + /// Setting this option to true will temporarily remove development dependencies before executing the command, then restore them afterward. + #[ former( default = true ) ] + pub( crate ) exclude_dev_dependencies : bool, /// An optional temporary path to be used during packaging. /// /// This field may contain a path to a temporary directory that will be used during the packaging process. @@ -79,6 +84,53 @@ mod private } } + #[ derive( Debug ) ] + struct TemporaryManifestFile + { + original : PathBuf, + temporary : PathBuf, + } + + impl TemporaryManifestFile + { + /// Creates a backup copy of the original file, allowing the original file location to serve as a temporary workspace. + /// When the object is dropped, the temporary file at the original location is replaced by the backup, restoring the original file. + fn new( path : impl Into< PathBuf > ) -> error::untyped::Result< Self > + { + let path = path.into(); + if !path.ends_with( "Cargo.toml" ) + { + error::untyped::bail!( "Wrong path to temporary manifest" ); + } + + let mut index = 0; + let original = loop + { + let temp_path = PathBuf::from( format!( "{}.temp_{index}", path.display() ) ); + if !temp_path.exists() + { + _ = std::fs::copy( &path, &temp_path )?; + break temp_path; + } + index += 1; + }; + + Ok( Self + { + original, + temporary : path, + }) + } + } + + impl Drop for TemporaryManifestFile + { + fn drop( &mut self ) + { + _ = std::fs::rename( &self.original, &self.temporary ).ok(); + } + } + /// /// Assemble the local package into a distributable tarball. /// @@ -96,6 +148,17 @@ mod private // qqq : use typed error pub fn pack( args : PackOptions ) -> error::untyped::Result< process::Report > { + let _temp = if args.exclude_dev_dependencies + { + let manifest = TemporaryManifestFile::new( args.path.join( "Cargo.toml" ) )?; + let mut file = Manifest::try_from( ManifestFile::try_from( &manifest.temporary )? )?; + let data = file.data(); + + _ = data.remove( "dev-dependencies" ); + file.store()?; + + Some( manifest ) + } else { None }; let ( program, options ) = ( "rustup", args.to_pack_args() ); if args.dry @@ -129,6 +192,7 @@ mod private { pub( crate ) path : PathBuf, pub( crate ) temp_path : Option< PathBuf >, + pub( crate ) exclude_dev_dependencies : bool, #[ former( default = 0usize ) ] pub( crate ) retry_count : usize, pub( crate ) dry : bool, @@ -162,6 +226,17 @@ mod private pub fn publish( args : PublishOptions ) -> error::untyped::Result< process::Report > // qqq : use typed error { + let _temp = if args.exclude_dev_dependencies + { + let manifest = TemporaryManifestFile::new( args.path.join( "Cargo.toml" ) )?; + let mut file = Manifest::try_from( ManifestFile::try_from( &manifest.temporary )? )?; + let data = file.data(); + + _ = data.remove( "dev-dependencies" ); + file.store()?; + + Some( manifest ) + } else { None }; let ( program, arguments) = ( "cargo", args.as_publish_args() ); if args.dry From 4f999160e982238882745530ff20a47274d86afa Mon Sep 17 00:00:00 2001 From: ".Barsik" Date: Wed, 6 Nov 2024 09:49:12 +0200 Subject: [PATCH 39/67] chore: resolve warnings (#1473) --- module/move/wca/src/ca/formatter.rs | 14 +++++++++++++- module/move/wca/src/ca/help.rs | 9 +++++++++ module/move/wca/src/lib.rs | 3 --- 3 files changed, 22 insertions(+), 4 deletions(-) diff --git a/module/move/wca/src/ca/formatter.rs b/module/move/wca/src/ca/formatter.rs index d528ef7f6d..905fed5b2d 100644 --- a/module/move/wca/src/ca/formatter.rs +++ b/module/move/wca/src/ca/formatter.rs @@ -5,14 +5,26 @@ mod private use iter_tools::Itertools; use ca::aggregator::Order; - /// - + /// Enum representing the format options for generating help content. + /// + /// `HelpFormat` defines the output format of help content, enabling the choice + /// between different styles, such as `Markdown` for structured text, or other + /// custom formats. #[ derive( Debug, Clone, PartialEq ) ] pub enum HelpFormat { + /// Generates help content in Markdown format, suitable for environments + /// that support Markdown rendering (e.g., documentation platforms, text editors). Markdown, + /// Represents an alternative format, customizable for different needs. Another, } + /// Generates Markdown-formatted help content based on a dictionary of terms and a specified order. + /// + /// The `md_generator` function takes a reference to a `Dictionary` and an `Order` to produce + /// a help document in Markdown format. This function is useful for generating structured, + /// readable help documentation suitable for Markdown-compatible platforms. pub fn md_generator( grammar : &Dictionary, order: Order ) -> String { let text = grammar.commands() diff --git a/module/move/wca/src/ca/help.rs b/module/move/wca/src/ca/help.rs index bfc8edbfe1..a2a7129f49 100644 --- a/module/move/wca/src/ca/help.rs +++ b/module/move/wca/src/ca/help.rs @@ -21,12 +21,16 @@ mod private // qqq : for Bohdan : it should transparent mechanist which patch list of commands, not a stand-alone mechanism + /// Enum `LevelOfDetail` specifies the granularity of detail for rendering or processing: #[ derive( Debug, Default, Copy, Clone, PartialEq, Eq ) ] pub enum LevelOfDetail { + /// No detail (default). #[ default ] None, + /// Basic level of detail. Simple, + /// High level of detail. Detailed, } @@ -64,6 +68,11 @@ mod private } // qqq : for Barsik : make possible to change properties order + /// Generates help content as a formatted string based on a given dictionary and options. + /// + /// This function takes a `Dictionary` of terms or commands and a `HelpGeneratorOptions` + /// struct to customize the help output, generating a user-friendly help message + /// or guide in `String` format. pub fn generate_help_content( dictionary : &Dictionary, o : HelpGeneratorOptions< '_ > ) -> String { struct Row diff --git a/module/move/wca/src/lib.rs b/module/move/wca/src/lib.rs index 7f42a20e14..2fcb2a8409 100644 --- a/module/move/wca/src/lib.rs +++ b/module/move/wca/src/lib.rs @@ -4,9 +4,6 @@ #![ doc = include_str!( concat!( env!( "CARGO_MANIFEST_DIR" ), "/", "Readme.md" ) ) ] #![ doc = include_str!( concat!( env!( "CARGO_MANIFEST_DIR" ), "/", "doc/", "wca.md" ) ) ] -#![ allow( where_clauses_object_safety ) ] // https://github.com/chris-morgan/anymap/issues/31 -// qqq : xxx : is it neccessary? - use mod_interface::mod_interface; pub mod ca; From 073a878d46e716272b07428c35980a9434b6e866 Mon Sep 17 00:00:00 2001 From: wbot <69343704+wtools-bot@users.noreply.github.com> Date: Thu, 7 Nov 2024 10:05:21 +0200 Subject: [PATCH 40/67] AUTO : Forward from refactoring_3 to alpha (#1480) decycling test_tools wip --- Cargo.toml | 12 ++ .../instance_of/src/typing/implements_lib.rs | 2 +- .../instance_of/src/typing/is_slice_lib.rs | 2 +- .../wtest_basic/src/test/basic/helper.rs | 2 +- .../alias/wtest_basic/src/test/basic/mod.rs | 2 +- module/core/async_from/src/lib.rs | 2 +- module/core/async_tools/src/lib.rs | 2 +- module/core/clone_dyn/src/lib.rs | 2 +- module/core/clone_dyn_types/src/lib.rs | 2 +- module/core/data_type/src/dt.rs | 2 +- module/core/diagnostics_tools/src/diag/rta.rs | 2 +- module/core/error_tools/src/error/assert.rs | 2 +- module/core/error_tools/src/error/mod.rs | 2 +- module/core/error_tools/src/error/typed.rs | 2 +- module/core/error_tools/src/error/untyped.rs | 2 +- module/core/for_each/src/lib.rs | 2 +- module/core/format_tools/src/format.rs | 2 +- .../core/format_tools/src/format/as_table.rs | 2 +- module/core/format_tools/src/format/filter.rs | 2 +- .../core/format_tools/src/format/md_math.rs | 2 +- .../format_tools/src/format/output_format.rs | 2 +- module/core/format_tools/src/format/print.rs | 2 +- module/core/format_tools/src/format/string.rs | 2 +- module/core/format_tools/src/format/table.rs | 2 +- .../core/format_tools/src/format/to_string.rs | 2 +- .../src/format/to_string_with_fallback.rs | 2 +- module/core/former_types/src/collection.rs | 2 +- module/core/fs_tools/src/fs/fs.rs | 2 +- module/core/implements/src/lib.rs | 2 +- module/core/impls_index/Cargo.toml | 4 +- .../core/impls_index/src/impls_index/func.rs | 2 +- .../core/impls_index/src/impls_index/impls.rs | 19 +- .../core/impls_index/src/impls_index/mod.rs | 21 +- module/core/impls_index/src/lib.rs | 5 +- .../core/impls_index/tests/inc/func_test.rs | 2 +- .../core/impls_index/tests/inc/impls1_test.rs | 182 +++++++++--------- .../core/impls_index/tests/inc/impls2_test.rs | 160 ++++++++------- .../core/impls_index/tests/inc/impls3_test.rs | 5 +- .../impls_index/tests/inc/impls_basic_test.rs | 3 +- .../core/impls_index/tests/inc/index_test.rs | 176 +++++++++-------- module/core/impls_index/tests/inc/mod.rs | 7 +- .../impls_index/tests/inc/tests_index_test.rs | 176 +++++++++-------- module/core/impls_index/tests/tests.rs | 1 + module/core/interval_adapter/src/lib.rs | 2 +- module/core/macro_tools/src/attr.rs | 2 +- module/core/macro_tools/src/attr_prop.rs | 2 +- module/core/macro_tools/src/components.rs | 2 +- module/core/macro_tools/src/container_kind.rs | 2 +- module/core/macro_tools/src/ct.rs | 2 +- module/core/macro_tools/src/derive.rs | 2 +- module/core/macro_tools/src/diag.rs | 2 +- module/core/macro_tools/src/equation.rs | 2 +- module/core/macro_tools/src/generic_args.rs | 2 +- module/core/macro_tools/src/generic_params.rs | 2 +- module/core/macro_tools/src/item.rs | 2 +- module/core/macro_tools/src/item_struct.rs | 2 +- module/core/macro_tools/src/iter.rs | 2 +- module/core/macro_tools/src/kw.rs | 2 +- module/core/macro_tools/src/lib.rs | 2 +- module/core/macro_tools/src/name.rs | 2 +- module/core/macro_tools/src/phantom.rs | 2 +- module/core/macro_tools/src/punctuated.rs | 2 +- module/core/macro_tools/src/quantifier.rs | 2 +- module/core/macro_tools/src/struct_like.rs | 2 +- module/core/macro_tools/src/tokens.rs | 2 +- module/core/macro_tools/src/typ.rs | 2 +- module/core/macro_tools/src/typed.rs | 2 +- module/core/mem_tools/src/mem.rs | 2 +- module/core/meta_tools/Cargo.toml | 30 ++- module/core/meta_tools/src/lib.rs | 60 ++++-- module/core/meta_tools/src/meta.rs | 93 ++++++--- module/core/mod_interface/Readme.md | 97 ++++++++-- .../examples/mod_interface_trivial/Readme.md | 8 +- .../inc/derive/micro_modules_glob/mod.rs | 2 +- .../tests/inc/derive/reuse_basic/mod.rs | 2 +- module/core/mod_interface_meta/src/impls.rs | 2 +- module/core/mod_interface_meta/src/record.rs | 2 +- .../core/mod_interface_meta/src/use_tree.rs | 2 +- .../core/mod_interface_meta/src/visibility.rs | 2 +- module/core/process_tools/src/environment.rs | 2 +- module/core/process_tools/src/process.rs | 2 +- module/core/program_tools/src/program.rs | 2 +- module/core/pth/src/as_path.rs | 2 +- module/core/pth/src/path.rs | 2 +- module/core/pth/src/path/absolute_path.rs | 2 +- module/core/pth/src/path/canonical_path.rs | 2 +- module/core/pth/src/path/current_path.rs | 2 +- module/core/pth/src/path/native_path.rs | 2 +- module/core/pth/src/transitive.rs | 2 +- module/core/pth/src/try_into_cow_path.rs | 2 +- module/core/pth/src/try_into_path.rs | 2 +- module/core/reflect_tools/src/reflect.rs | 2 +- .../reflect_tools/src/reflect/axiomatic.rs | 2 +- .../reflect_tools/src/reflect/entity_array.rs | 2 +- .../src/reflect/entity_hashmap.rs | 2 +- .../src/reflect/entity_hashset.rs | 2 +- .../reflect_tools/src/reflect/entity_slice.rs | 2 +- .../reflect_tools/src/reflect/entity_vec.rs | 2 +- .../core/reflect_tools/src/reflect/fields.rs | 2 +- .../reflect_tools/src/reflect/primitive.rs | 2 +- .../core/reflect_tools/src/reflect/wrapper.rs | 2 +- .../core/strs_tools/src/string/indentation.rs | 2 +- module/core/strs_tools/src/string/number.rs | 2 +- .../strs_tools/src/string/parse_request.rs | 2 +- module/core/test_tools/Cargo.toml | 21 +- module/core/test_tools/src/lib.rs | 79 +++++--- module/core/test_tools/src/test/asset.rs | 2 +- .../core/test_tools/src/test/compiletime.rs | 2 +- module/core/test_tools/src/test/helper.rs | 2 +- module/core/test_tools/src/test/mod.rs | 3 +- module/core/test_tools/src/test/smoke_test.rs | 2 +- module/core/test_tools/src/test/version.rs | 2 +- module/core/variadic_from/src/variadic.rs | 2 +- module/move/assistant/src/client.rs | 2 +- module/move/assistant/src/debug.rs | 2 +- module/move/assistant/src/lib.rs | 2 +- module/move/crates_tools/src/lib.rs | 2 +- .../src/hrng_deterministic.rs | 2 +- .../src/hrng_non_deterministic.rs | 2 +- module/move/deterministic_rand/src/iter.rs | 2 +- module/move/deterministic_rand/src/seed.rs | 2 +- module/move/graphs_tools/src/abs/edge.rs | 2 +- module/move/graphs_tools/src/abs/factory.rs | 2 +- .../move/graphs_tools/src/abs/id_generator.rs | 2 +- module/move/graphs_tools/src/abs/identity.rs | 2 +- module/move/graphs_tools/src/abs/node.rs | 2 +- module/move/graphs_tools/src/algo/dfs.rs | 2 +- .../move/graphs_tools/src/canonical/edge.rs | 2 +- .../src/canonical/factory_generative.rs | 2 +- .../src/canonical/factory_readable.rs | 2 +- .../graphs_tools/src/canonical/identity.rs | 2 +- .../move/graphs_tools/src/canonical/node.rs | 2 +- .../plot_interface/src/plot/abs/change.rs | 2 +- .../plot_interface/src/plot/abs/changer.rs | 2 +- .../plot_interface/src/plot/abs/context.rs | 2 +- .../plot_interface/src/plot/abs/identity.rs | 2 +- .../plot_interface/src/plot/abs/registry.rs | 2 +- module/move/plot_interface/src/plot/color.rs | 2 +- .../plot_interface/src/plot/sys/context.rs | 2 +- .../src/plot/sys/context_changer.rs | 2 +- .../plot_interface/src/plot/sys/drawing.rs | 2 +- .../src/plot/sys/drawing/change_new.rs | 2 +- .../src/plot/sys/drawing/changer.rs | 2 +- .../src/plot/sys/drawing/command.rs | 2 +- .../src/plot/sys/drawing/queue.rs | 2 +- .../src/plot/sys/drawing/rect_change_new.rs | 2 +- .../plot/sys/drawing/rect_change_region.rs | 2 +- .../src/plot/sys/drawing/rect_changer.rs | 2 +- .../src/plot/sys/stroke_brush.rs | 2 +- .../src/plot/sys/stroke_brush/change_color.rs | 2 +- .../src/plot/sys/stroke_brush/change_new.rs | 2 +- .../src/plot/sys/stroke_brush/change_width.rs | 2 +- .../src/plot/sys/stroke_brush/changer.rs | 2 +- .../plot_interface/src/plot/sys/target.rs | 2 +- module/move/sqlx_query/src/lib.rs | 2 +- module/move/willbe/src/action/list.rs | 2 +- module/move/willbe/src/action/publish.rs | 2 +- module/move/willbe/src/action/publish_diff.rs | 2 +- module/move/willbe/src/action/test.rs | 2 +- module/move/willbe/src/command/list.rs | 2 +- module/move/willbe/src/command/mod.rs | 2 +- module/move/willbe/src/command/publish.rs | 2 +- module/move/willbe/src/command/test.rs | 2 +- module/move/willbe/src/entity/files.rs | 2 +- module/move/willbe/src/entity/manifest.rs | 2 +- .../willbe/src/entity/package_md_extension.rs | 2 +- module/move/willbe/src/entity/version.rs | 2 +- .../src/entity/workspace_md_extension.rs | 2 +- module/move/willbe/src/lib.rs | 2 +- module/move/willbe/src/tool/cargo.rs | 2 +- module/move/willbe/src/tool/files.rs | 2 +- module/move/willbe/src/tool/git.rs | 2 +- module/move/willbe/src/tool/graph.rs | 2 +- module/move/willbe/src/tool/http.rs | 2 +- module/move/willbe/src/tool/iter.rs | 2 +- module/move/willbe/src/tool/macros.rs | 2 +- module/move/willbe/src/tool/path.rs | 2 +- module/move/willbe/src/tool/query.rs | 2 +- module/move/willbe/src/tool/repository.rs | 2 +- module/move/willbe/src/tool/template.rs | 4 +- module/move/willbe/src/tool/url.rs | 2 +- module/move/wplot/src/plot/abs/change.rs | 2 +- module/move/wplot/src/plot/abs/changer.rs | 2 +- module/move/wplot/src/plot/abs/context.rs | 2 +- module/move/wplot/src/plot/abs/identity.rs | 2 +- module/move/wplot/src/plot/abs/registry.rs | 2 +- module/move/wplot/src/plot/color.rs | 2 +- module/move/wplot/src/plot/sys/context.rs | 2 +- .../wplot/src/plot/sys/context_changer.rs | 2 +- module/move/wplot/src/plot/sys/drawing.rs | 2 +- .../wplot/src/plot/sys/drawing/change_new.rs | 2 +- .../wplot/src/plot/sys/drawing/changer.rs | 2 +- .../wplot/src/plot/sys/drawing/command.rs | 2 +- .../move/wplot/src/plot/sys/drawing/queue.rs | 2 +- .../src/plot/sys/drawing/rect_change_new.rs | 2 +- .../plot/sys/drawing/rect_change_region.rs | 2 +- .../src/plot/sys/drawing/rect_changer.rs | 2 +- .../move/wplot/src/plot/sys/stroke_brush.rs | 2 +- .../src/plot/sys/stroke_brush/change_color.rs | 2 +- .../src/plot/sys/stroke_brush/change_new.rs | 2 +- .../src/plot/sys/stroke_brush/change_width.rs | 2 +- .../src/plot/sys/stroke_brush/changer.rs | 2 +- module/move/wplot/src/plot/sys/target.rs | 2 +- .../src/type_constuctor/enumerable.rs | 2 +- .../src/type_constuctor/helper.rs | 2 +- .../src/type_constuctor/make.rs | 2 +- .../src/type_constuctor/many.rs | 2 +- .../src/type_constuctor/no_many.rs | 2 +- .../src/type_constuctor/pair.rs | 2 +- .../src/type_constuctor/single.rs | 2 +- .../src/type_constuctor/traits.rs | 2 +- .../src/type_constuctor/types.rs | 2 +- .../src/type_constuctor/vectorized_from.rs | 2 +- .../postponed/wautomata/src/graph/abs/edge.rs | 2 +- .../wautomata/src/graph/abs/factory.rs | 2 +- .../wautomata/src/graph/abs/id_generator.rs | 2 +- .../wautomata/src/graph/abs/identity.rs | 2 +- .../postponed/wautomata/src/graph/abs/node.rs | 2 +- .../postponed/wautomata/src/graph/algo/dfs.rs | 2 +- .../wautomata/src/graph/canonical/edge.rs | 2 +- .../src/graph/canonical/factory_generative.rs | 2 +- .../src/graph/canonical/factory_readable.rs | 2 +- .../wautomata/src/graph/canonical/identity.rs | 2 +- .../wautomata/src/graph/canonical/node.rs | 2 +- module/template/layer/layer.rs | 2 +- 225 files changed, 868 insertions(+), 704 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 95a8901253..666e522707 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -557,3 +557,15 @@ default-features = false [workspace.dependencies.paste] version = "~1.0.14" default-features = false + +[workspace.dependencies.tempdir] +version = "~0.3.7" + +[workspace.dependencies.rustversion] +version = "~1.0" + +[workspace.dependencies.num-traits] +version = "~0.2" + +[workspace.dependencies.rand] +version = "0.8.5" diff --git a/module/alias/instance_of/src/typing/implements_lib.rs b/module/alias/instance_of/src/typing/implements_lib.rs index 1a3f76aa7e..4129608ed8 100644 --- a/module/alias/instance_of/src/typing/implements_lib.rs +++ b/module/alias/instance_of/src/typing/implements_lib.rs @@ -15,7 +15,7 @@ // #[ macro_use ] mod implements_impl; -/// Internal namespace. +/// Define a private namespace for all its items. mod private { diff --git a/module/alias/instance_of/src/typing/is_slice_lib.rs b/module/alias/instance_of/src/typing/is_slice_lib.rs index 0f4a45cbc4..f3479787d1 100644 --- a/module/alias/instance_of/src/typing/is_slice_lib.rs +++ b/module/alias/instance_of/src/typing/is_slice_lib.rs @@ -12,7 +12,7 @@ #![ doc = include_str!( concat!( env!( "CARGO_MANIFEST_DIR" ), "/", "Readme.md" ) ) ] -/// Internal namespace. +/// Define a private namespace for all its items. mod private { diff --git a/module/alias/wtest_basic/src/test/basic/helper.rs b/module/alias/wtest_basic/src/test/basic/helper.rs index fd3f8907d2..dc9ed8372b 100644 --- a/module/alias/wtest_basic/src/test/basic/helper.rs +++ b/module/alias/wtest_basic/src/test/basic/helper.rs @@ -3,7 +3,7 @@ //! Helpers. //! -/// Internal namespace. +/// Define a private namespace for all its items. mod private { diff --git a/module/alias/wtest_basic/src/test/basic/mod.rs b/module/alias/wtest_basic/src/test/basic/mod.rs index 9e9e011623..034ebb427a 100644 --- a/module/alias/wtest_basic/src/test/basic/mod.rs +++ b/module/alias/wtest_basic/src/test/basic/mod.rs @@ -3,7 +3,7 @@ //! Basic tools for testing. //! -/// Internal namespace. +/// Define a private namespace for all its items. mod private { } diff --git a/module/core/async_from/src/lib.rs b/module/core/async_from/src/lib.rs index b2419ae521..669dcf988e 100644 --- a/module/core/async_from/src/lib.rs +++ b/module/core/async_from/src/lib.rs @@ -11,7 +11,7 @@ pub mod dependency pub use ::async_trait; } -/// Internal namespace. +/// Define a private namespace for all its items. #[ cfg( feature = "enabled" ) ] mod private { diff --git a/module/core/async_tools/src/lib.rs b/module/core/async_tools/src/lib.rs index ab0bcbf7e8..0390e0dbe2 100644 --- a/module/core/async_tools/src/lib.rs +++ b/module/core/async_tools/src/lib.rs @@ -12,7 +12,7 @@ pub mod dependency pub use ::async_from; } -/// Internal namespace. +/// Define a private namespace for all its items. #[ cfg( feature = "enabled" ) ] mod private { diff --git a/module/core/clone_dyn/src/lib.rs b/module/core/clone_dyn/src/lib.rs index 18e0150163..57ae64c7f8 100644 --- a/module/core/clone_dyn/src/lib.rs +++ b/module/core/clone_dyn/src/lib.rs @@ -14,7 +14,7 @@ pub mod dependency pub use ::clone_dyn_types; } -/// Internal namespace. +/// Define a private namespace for all its items. #[ cfg( feature = "enabled" ) ] mod private { diff --git a/module/core/clone_dyn_types/src/lib.rs b/module/core/clone_dyn_types/src/lib.rs index 3fc94133df..cc55badce4 100644 --- a/module/core/clone_dyn_types/src/lib.rs +++ b/module/core/clone_dyn_types/src/lib.rs @@ -10,7 +10,7 @@ pub mod dependency { } -/// Internal namespace. +/// Define a private namespace for all its items. // #[ cfg( any( not( feature = "no_std" ), feature = "use_alloc" ) ) ] #[ cfg( feature = "enabled" ) ] mod private diff --git a/module/core/data_type/src/dt.rs b/module/core/data_type/src/dt.rs index 69c9e80518..f384f56072 100644 --- a/module/core/data_type/src/dt.rs +++ b/module/core/data_type/src/dt.rs @@ -1,4 +1,4 @@ -/// Internal namespace. +/// Define a private namespace for all its items. mod private { } diff --git a/module/core/diagnostics_tools/src/diag/rta.rs b/module/core/diagnostics_tools/src/diag/rta.rs index 27f8d991ec..46b21050a2 100644 --- a/module/core/diagnostics_tools/src/diag/rta.rs +++ b/module/core/diagnostics_tools/src/diag/rta.rs @@ -1,4 +1,4 @@ -/// Internal namespace. +/// Define a private namespace for all its items. mod private { diff --git a/module/core/error_tools/src/error/assert.rs b/module/core/error_tools/src/error/assert.rs index 50c72b0bdf..52f45d29bf 100644 --- a/module/core/error_tools/src/error/assert.rs +++ b/module/core/error_tools/src/error/assert.rs @@ -1,4 +1,4 @@ -/// Internal namespace. +/// Define a private namespace for all its items. mod private { /// diff --git a/module/core/error_tools/src/error/mod.rs b/module/core/error_tools/src/error/mod.rs index cd999e016d..4d9c79ddaa 100644 --- a/module/core/error_tools/src/error/mod.rs +++ b/module/core/error_tools/src/error/mod.rs @@ -1,4 +1,4 @@ -/// Internal namespace. +/// Define a private namespace for all its items. mod private { pub use std::error::Error as ErrorTrait; diff --git a/module/core/error_tools/src/error/typed.rs b/module/core/error_tools/src/error/typed.rs index e4e341a586..074fb5b61a 100644 --- a/module/core/error_tools/src/error/typed.rs +++ b/module/core/error_tools/src/error/typed.rs @@ -1,4 +1,4 @@ -/// Internal namespace. +/// Define a private namespace for all its items. mod private { diff --git a/module/core/error_tools/src/error/untyped.rs b/module/core/error_tools/src/error/untyped.rs index 17e335473a..8288f32866 100644 --- a/module/core/error_tools/src/error/untyped.rs +++ b/module/core/error_tools/src/error/untyped.rs @@ -1,4 +1,4 @@ -/// Internal namespace. +/// Define a private namespace for all its items. mod private { diff --git a/module/core/for_each/src/lib.rs b/module/core/for_each/src/lib.rs index b106a5110b..00825e5b96 100644 --- a/module/core/for_each/src/lib.rs +++ b/module/core/for_each/src/lib.rs @@ -4,7 +4,7 @@ #![ doc( html_root_url = "https://docs.rs/for_each/latest/for_each/" ) ] #![ doc = include_str!( concat!( env!( "CARGO_MANIFEST_DIR" ), "/", "Readme.md" ) ) ] -/// Internal namespace. +/// Define a private namespace for all its items. #[ cfg( feature = "enabled" ) ] mod private { diff --git a/module/core/format_tools/src/format.rs b/module/core/format_tools/src/format.rs index 2abf7f18a4..fb207181b3 100644 --- a/module/core/format_tools/src/format.rs +++ b/module/core/format_tools/src/format.rs @@ -2,7 +2,7 @@ //! Collection of mechanisms for formatting and serialization into string. //! -/// Internal namespace. +/// Define a private namespace for all its items. mod private { diff --git a/module/core/format_tools/src/format/as_table.rs b/module/core/format_tools/src/format/as_table.rs index b1c48c159f..7409b42952 100644 --- a/module/core/format_tools/src/format/as_table.rs +++ b/module/core/format_tools/src/format/as_table.rs @@ -2,7 +2,7 @@ //! Nice print's wrapper. //! -/// Internal namespace. +/// Define a private namespace for all its items. mod private { diff --git a/module/core/format_tools/src/format/filter.rs b/module/core/format_tools/src/format/filter.rs index 191522e138..1551721570 100644 --- a/module/core/format_tools/src/format/filter.rs +++ b/module/core/format_tools/src/format/filter.rs @@ -2,7 +2,7 @@ //! Print data as table. //! -/// Internal namespace. +/// Define a private namespace for all its items. mod private { diff --git a/module/core/format_tools/src/format/md_math.rs b/module/core/format_tools/src/format/md_math.rs index 196b0ee811..9aa70022d0 100644 --- a/module/core/format_tools/src/format/md_math.rs +++ b/module/core/format_tools/src/format/md_math.rs @@ -5,7 +5,7 @@ // xxx : use crate mdmath -/// Internal namespace. +/// Define a private namespace for all its items. mod private { use core:: diff --git a/module/core/format_tools/src/format/output_format.rs b/module/core/format_tools/src/format/output_format.rs index 69acca8515..1b3e13c5b9 100644 --- a/module/core/format_tools/src/format/output_format.rs +++ b/module/core/format_tools/src/format/output_format.rs @@ -28,7 +28,7 @@ //! ``` //! -/// Internal namespace. +/// Define a private namespace for all its items. mod private { diff --git a/module/core/format_tools/src/format/print.rs b/module/core/format_tools/src/format/print.rs index 858bd7dd57..bc49db448d 100644 --- a/module/core/format_tools/src/format/print.rs +++ b/module/core/format_tools/src/format/print.rs @@ -2,7 +2,7 @@ //! Print data as table. //! -/// Internal namespace. +/// Define a private namespace for all its items. mod private { diff --git a/module/core/format_tools/src/format/string.rs b/module/core/format_tools/src/format/string.rs index 511f44c473..619d1690c2 100644 --- a/module/core/format_tools/src/format/string.rs +++ b/module/core/format_tools/src/format/string.rs @@ -4,7 +4,7 @@ // xxx : move to crate string_tools -/// Internal namespace. +/// Define a private namespace for all its items. mod private { diff --git a/module/core/format_tools/src/format/table.rs b/module/core/format_tools/src/format/table.rs index d3dc8bd71c..27e44ca0e5 100644 --- a/module/core/format_tools/src/format/table.rs +++ b/module/core/format_tools/src/format/table.rs @@ -2,7 +2,7 @@ //! Table interface. //! -/// Internal namespace. +/// Define a private namespace for all its items. mod private { diff --git a/module/core/format_tools/src/format/to_string.rs b/module/core/format_tools/src/format/to_string.rs index 6446e90fa2..8bc9bb538f 100644 --- a/module/core/format_tools/src/format/to_string.rs +++ b/module/core/format_tools/src/format/to_string.rs @@ -2,7 +2,7 @@ //! Flexible ToString augmentation. //! -/// Internal namespace. +/// Define a private namespace for all its items. mod private { diff --git a/module/core/format_tools/src/format/to_string_with_fallback.rs b/module/core/format_tools/src/format/to_string_with_fallback.rs index e79b827896..fb5966bf38 100644 --- a/module/core/format_tools/src/format/to_string_with_fallback.rs +++ b/module/core/format_tools/src/format/to_string_with_fallback.rs @@ -2,7 +2,7 @@ //! Flexible ToString augmentation. //! -/// Internal namespace. +/// Define a private namespace for all its items. mod private { use crate::*; diff --git a/module/core/former_types/src/collection.rs b/module/core/former_types/src/collection.rs index c740510fb3..c7b25889b7 100644 --- a/module/core/former_types/src/collection.rs +++ b/module/core/former_types/src/collection.rs @@ -5,7 +5,7 @@ //! such as vectors, hash maps, and custom collection implementations. //! -/// Internal namespace. +/// Define a private namespace for all its items. mod private { diff --git a/module/core/fs_tools/src/fs/fs.rs b/module/core/fs_tools/src/fs/fs.rs index 25f60b2592..a10288843c 100644 --- a/module/core/fs_tools/src/fs/fs.rs +++ b/module/core/fs_tools/src/fs/fs.rs @@ -1,4 +1,4 @@ -/// Internal namespace. +/// Define a private namespace for all its items. mod private { diff --git a/module/core/implements/src/lib.rs b/module/core/implements/src/lib.rs index 7bdfba2035..989d5e528e 100644 --- a/module/core/implements/src/lib.rs +++ b/module/core/implements/src/lib.rs @@ -16,7 +16,7 @@ #[ cfg( feature = "enabled" ) ] mod implements_impl; -/// Internal namespace. +/// Define a private namespace for all its items. #[ cfg( feature = "enabled" ) ] mod private { diff --git a/module/core/impls_index/Cargo.toml b/module/core/impls_index/Cargo.toml index 1845411740..392f5bf2f0 100644 --- a/module/core/impls_index/Cargo.toml +++ b/module/core/impls_index/Cargo.toml @@ -27,8 +27,6 @@ all-features = false [features] default = [ "enabled" ] full = [ "enabled" ] -no_std = [] -use_alloc = [ "no_std" ] enabled = [ "impls_index_meta/enabled" ] [dependencies] @@ -36,4 +34,4 @@ impls_index_meta = { workspace = true } [dev-dependencies] test_tools = { workspace = true } -tempdir = { version = "0.3.7" } +tempdir = { workspace = true } diff --git a/module/core/impls_index/src/impls_index/func.rs b/module/core/impls_index/src/impls_index/func.rs index 21448b2ef8..da33da0127 100644 --- a/module/core/impls_index/src/impls_index/func.rs +++ b/module/core/impls_index/src/impls_index/func.rs @@ -1,4 +1,4 @@ -/// Internal namespace. +/// Define a private namespace for all its items. mod private { diff --git a/module/core/impls_index/src/impls_index/impls.rs b/module/core/impls_index/src/impls_index/impls.rs index 18d81346a8..b10b4498e3 100644 --- a/module/core/impls_index/src/impls_index/impls.rs +++ b/module/core/impls_index/src/impls_index/impls.rs @@ -1,4 +1,4 @@ -/// Internal namespace. +/// Define a private namespace for all its items. mod private { @@ -382,14 +382,7 @@ pub mod exposed use super::*; #[ doc( inline ) ] pub use prelude::*; -} - -/// Prelude to use essentials: `use my_module::prelude::*`. -#[ allow( unused_imports ) ] -pub mod prelude -{ - use super::*; #[ doc( inline ) ] pub use private:: { @@ -403,9 +396,15 @@ pub mod prelude _impls_callback, }; #[ doc( inline ) ] - #[ allow( unused_imports ) ] pub use ::impls_index_meta::impls3; #[ doc( inline ) ] - #[ allow( unused_imports ) ] pub use impls3 as impls; + +} + +/// Prelude to use essentials: `use my_module::prelude::*`. +#[ allow( unused_imports ) ] +pub mod prelude +{ + use super::*; } diff --git a/module/core/impls_index/src/impls_index/mod.rs b/module/core/impls_index/src/impls_index/mod.rs index f0d3a5f74f..5b54558d8d 100644 --- a/module/core/impls_index/src/impls_index/mod.rs +++ b/module/core/impls_index/src/impls_index/mod.rs @@ -1,4 +1,4 @@ -/// Internal namespace. +/// Define a private namespace for all its items. mod private { @@ -31,6 +31,8 @@ pub mod own use super::*; #[ doc( inline ) ] pub use orphan::*; + #[ doc( inline ) ] + pub use ::impls_index_meta::*; } /// Shared with parent namespace of the module @@ -40,7 +42,6 @@ pub mod orphan use super::*; #[ doc( inline ) ] pub use exposed::*; - // pub use super::dependency; } /// Exposed namespace of the module. @@ -51,11 +52,9 @@ pub mod exposed #[ doc( inline ) ] pub use prelude::*; #[ doc( inline ) ] - #[ allow( unused_imports ) ] - pub use super::impls::exposed::*; + pub use impls::exposed::*; #[ doc( inline ) ] - #[ allow( unused_imports ) ] - pub use super::func::exposed::*; + pub use func::exposed::*; } /// Prelude to use essentials: `use my_module::prelude::*`. @@ -64,13 +63,7 @@ pub mod prelude { use super::*; #[ doc( inline ) ] - #[ allow( unused_imports ) ] - pub use super::impls::prelude::*; + pub use impls::prelude::*; #[ doc( inline ) ] - #[ allow( unused_imports ) ] - pub use super::func::prelude::*; - // #[ cfg( any( feature = "meta", feature = "impls_index_meta" ) ) ] - #[ doc( inline ) ] - #[ allow( unused_imports ) ] - pub use ::impls_index_meta::*; + pub use func::prelude::*; } diff --git a/module/core/impls_index/src/lib.rs b/module/core/impls_index/src/lib.rs index ec229443d8..ac0f33af0a 100644 --- a/module/core/impls_index/src/lib.rs +++ b/module/core/impls_index/src/lib.rs @@ -1,4 +1,4 @@ -#![ cfg_attr( feature = "no_std", no_std ) ] +#![ no_std ] #![ doc( html_logo_url = "https://raw.githubusercontent.com/Wandalen/wTools/master/asset/img/logo_v3_trans_square.png" ) ] #![ doc( html_favicon_url = "https://raw.githubusercontent.com/Wandalen/wTools/alpha/asset/img/logo_v3_trans_square_icon_small_v2.ico" ) ] #![ doc( html_root_url = "https://docs.rs/impls_index/latest/impls_index/" ) ] @@ -30,7 +30,6 @@ pub mod own #[ doc( inline ) ] pub use orphan::*; #[ doc( inline ) ] - #[ allow( unused_imports ) ] pub use super::impls_index::orphan::*; } @@ -53,7 +52,6 @@ pub mod exposed #[ doc( inline ) ] pub use prelude::*; #[ doc( inline ) ] - #[ allow( unused_imports ) ] pub use super::impls_index::exposed::*; } @@ -64,6 +62,5 @@ pub mod prelude { use super::*; #[ doc( inline ) ] - #[ allow( unused_imports ) ] pub use super::impls_index::prelude::*; } diff --git a/module/core/impls_index/tests/inc/func_test.rs b/module/core/impls_index/tests/inc/func_test.rs index 7408b5b3ff..7b5d74bbcc 100644 --- a/module/core/impls_index/tests/inc/func_test.rs +++ b/module/core/impls_index/tests/inc/func_test.rs @@ -2,7 +2,7 @@ use super::*; #[ allow ( unused_imports ) ] -use the_module::prelude::*; +use the_module::exposed::*; // use test_tools::exposed::*; // diff --git a/module/core/impls_index/tests/inc/impls1_test.rs b/module/core/impls_index/tests/inc/impls1_test.rs index c8df2ca220..b49e010b01 100644 --- a/module/core/impls_index/tests/inc/impls1_test.rs +++ b/module/core/impls_index/tests/inc/impls1_test.rs @@ -1,120 +1,118 @@ // use test_tools::exposed::*; use super::*; -use the_module::prelude::impls1; +use the_module::exposed::impls1; +// use the_module::exposed::{ index }; // -tests_impls! +#[ test ] +fn impls_basic() { - fn impls_basic() + // test.case( "impls1 basic" ); { - // test.case( "impls1 basic" ); + impls1! { - - impls1! + fn f1() { - fn f1() - { - println!( "f1" ); - } - pub fn f2() - { - println!( "f2" ); - } - }; + println!( "f1" ); + } + pub fn f2() + { + println!( "f2" ); + } + }; - // trace_macros!( true ); - f1!(); - f2!(); - // trace_macros!( false ); + // trace_macros!( true ); + f1!(); + f2!(); + // trace_macros!( false ); - f1(); - f2(); + f1(); + f2(); - } + } + +// // test.case( "impls1 as" ); +// { +// +// impls1! +// { +// fn f1() +// { +// println!( "f1" ); +// } +// pub fn f2() +// { +// println!( "f2" ); +// } +// }; +// +// // trace_macros!( true ); +// f1!( as f1b ); +// f2!( as f2b ); +// // trace_macros!( false ); +// +// f1b(); +// f2b(); +// +// } +// +// // test.case( "impls1 as index" ); +// { +// +// impls1! +// { +// fn f1() +// { +// println!( "f1" ); +// } +// pub fn f2() +// { +// println!( "f2" ); +// } +// }; +// +// // trace_macros!( true ); +// index! +// { +// f1, +// f2 as f2b, +// } +// // trace_macros!( false ); +// +// f1(); +// f2b(); +// +// } - // // test.case( "impls1 as" ); - // { - // - // impls1! - // { - // fn f1() - // { - // println!( "f1" ); - // } - // pub fn f2() - // { - // println!( "f2" ); - // } - // }; - // - // // trace_macros!( true ); - // f1!( as f1b ); - // f2!( as f2b ); - // // trace_macros!( false ); - // - // f1b(); - // f2b(); - // - // } - // - // // test.case( "impls1 as index" ); - // { - // - // impls1! - // { - // fn f1() - // { - // println!( "f1" ); - // } - // pub fn f2() - // { - // println!( "f2" ); - // } - // }; - // - // // trace_macros!( true ); - // index! - // { - // f1, - // f2 as f2b, - // } - // // trace_macros!( false ); - // - // f1(); - // f2b(); - // - // } + // test.case( "macro" ); + { - // test.case( "macro" ); + impls1! { - - impls1! + fn f1() { - fn f1() + macro_rules! macro1 { - macro_rules! macro1 - { - ( $( $Arg : tt )* ) => { }; - } - macro1!(); + ( $( $Arg : tt )* ) => { }; } + macro1!(); } - - // trace_macros!( true ); - f1!(); - // trace_macros!( false ); - } + // trace_macros!( true ); + f1!(); + // trace_macros!( false ); + } + } // -tests_index! -{ - impls_basic, -} +// tests_index! +// { +// impls_basic, +// } diff --git a/module/core/impls_index/tests/inc/impls2_test.rs b/module/core/impls_index/tests/inc/impls2_test.rs index bb5d16eaab..20b83f0731 100644 --- a/module/core/impls_index/tests/inc/impls2_test.rs +++ b/module/core/impls_index/tests/inc/impls2_test.rs @@ -1,121 +1,119 @@ // use test_tools::exposed::*; use super::*; -use the_module::prelude::impls2; +use the_module::exposed::impls2; +use the_module::exposed::{ index }; // -tests_impls! +#[ test ] +fn impls_basic() { - fn impls_basic() + // test.case( "impls2 basic" ); { - // test.case( "impls2 basic" ); + impls2! { - - impls2! + fn f1() { - fn f1() - { - println!( "f1" ); - } - pub fn f2() - { - println!( "f2" ); - } - }; + println!( "f1" ); + } + pub fn f2() + { + println!( "f2" ); + } + }; - // trace_macros!( true ); - f1!(); - f2!(); - // trace_macros!( false ); + // trace_macros!( true ); + f1!(); + f2!(); + // trace_macros!( false ); - f1(); - f2(); + f1(); + f2(); - } + } - // test.case( "impls2 as" ); - { + // test.case( "impls2 as" ); + { - impls2! + impls2! + { + fn f1() { - fn f1() - { - println!( "f1" ); - } - pub fn f2() - { - println!( "f2" ); - } - }; + println!( "f1" ); + } + pub fn f2() + { + println!( "f2" ); + } + }; - // trace_macros!( true ); - f1!( as f1b ); - f2!( as f2b ); - // trace_macros!( false ); + // trace_macros!( true ); + f1!( as f1b ); + f2!( as f2b ); + // trace_macros!( false ); - f1b(); - f2b(); + f1b(); + f2b(); - } + } - // test.case( "impls2 as index" ); - { + // test.case( "impls2 as index" ); + { - impls2! + impls2! + { + fn f1() { - fn f1() - { - println!( "f1" ); - } - pub fn f2() - { - println!( "f2" ); - } - }; - - // trace_macros!( true ); - index! + println!( "f1" ); + } + pub fn f2() { - f1, - f2 as f2b, + println!( "f2" ); } - // trace_macros!( false ); - - f1(); - f2b(); + }; + // trace_macros!( true ); + index! + { + f1, + f2 as f2b, } + // trace_macros!( false ); - // test.case( "macro" ); - { + f1(); + f2b(); - impls2! + } + + // test.case( "macro" ); + { + + impls2! + { + fn f1() { - fn f1() + macro_rules! macro1 { - macro_rules! macro1 - { - ( $( $Arg : tt )* ) => { }; - } - macro1!(); + ( $( $Arg : tt )* ) => { }; } + macro1!(); } - - // trace_macros!( true ); - f1!(); - // trace_macros!( false ); - } + // trace_macros!( true ); + f1!(); + // trace_macros!( false ); + } + } // -tests_index! -{ - // fns, - impls_basic, -} +// tests_index! +// { +// // fns, +// impls_basic, +// } diff --git a/module/core/impls_index/tests/inc/impls3_test.rs b/module/core/impls_index/tests/inc/impls3_test.rs index 860acd126a..1fe454dfc7 100644 --- a/module/core/impls_index/tests/inc/impls3_test.rs +++ b/module/core/impls_index/tests/inc/impls3_test.rs @@ -1,5 +1,6 @@ use super::*; -use the_module::prelude::impls3; +use the_module::exposed::impls3; +use the_module::exposed::{ index }; // @@ -7,7 +8,7 @@ use the_module::prelude::impls3; fn basic() { - impls! + impls3! { fn f1() { diff --git a/module/core/impls_index/tests/inc/impls_basic_test.rs b/module/core/impls_index/tests/inc/impls_basic_test.rs index c488aec5a2..8a9ef8df85 100644 --- a/module/core/impls_index/tests/inc/impls_basic_test.rs +++ b/module/core/impls_index/tests/inc/impls_basic_test.rs @@ -1,6 +1,5 @@ use super::*; -#[ allow( unused_imports ) ] -use the_module::prelude::*; +use the_module::exposed::*; // trace_macros!( true ); tests_impls! diff --git a/module/core/impls_index/tests/inc/index_test.rs b/module/core/impls_index/tests/inc/index_test.rs index de1ed0d9be..561e1ba8ac 100644 --- a/module/core/impls_index/tests/inc/index_test.rs +++ b/module/core/impls_index/tests/inc/index_test.rs @@ -1,155 +1,151 @@ // use test_tools::exposed::*; use super::*; -use the_module::prelude::impls1; +use the_module::exposed::impls1; +use the_module::exposed::{ index }; // -tests_impls! +#[ test ] +fn empty_with_comma() { - - fn empty_with_comma() + // test.case( "impls1 basic" ); { - // test.case( "impls1 basic" ); - { - - impls1!(); - index!(); - - } + impls1!(); + index!(); } +} + +#[ test ] +fn empty_without_comma() +{ - fn empty_without_comma() + // test.case( "impls1 basic" ); { - // test.case( "impls1 basic" ); + impls1! { + }; - impls1! - { - }; - - index! - { - } - + index! + { } } +} + +#[ test ] +fn with_comma() +{ - fn with_comma() + // test.case( "impls1 basic" ); { - // test.case( "impls1 basic" ); + impls1! { - - impls1! + fn f1() -> i32 { - fn f1() -> i32 - { - println!( "f1" ); - 13 - } - }; - - index! - { - f1, + println!( "f1" ); + 13 } + }; - a_id!( f1(), 13 ); + index! + { + f1, } + a_id!( f1(), 13 ); } +} + +#[ test ] +fn without_comma() +{ - fn without_comma() + // test.case( "impls1 basic" ); { - // test.case( "impls1 basic" ); + impls1! { - - impls1! - { - fn f1() -> i32 - { - println!( "f1" ); - 13 - } - }; - - index! + fn f1() -> i32 { - f1 + println!( "f1" ); + 13 } + }; - a_id!( f1(), 13 ); + index! + { + f1 } + a_id!( f1(), 13 ); } +} + +#[ test ] +fn parentheses_with_comma() +{ - fn parentheses_with_comma() + // test.case( "impls1 basic" ); { - // test.case( "impls1 basic" ); + impls1! { - - impls1! + fn f1() -> i32 { - fn f1() -> i32 - { - println!( "f1" ); - 13 - } - }; - - index!( f1, ); + println!( "f1" ); + 13 + } + }; - a_id!( f1(), 13 ); - } + index!( f1, ); + a_id!( f1(), 13 ); } +} - fn parentheses_without_comma() +#[ test ] +fn parentheses_without_comma() +{ + + // test.case( "impls1 basic" ); { - // test.case( "impls1 basic" ); + impls1! { - - impls1! + fn f1() -> i32 { - fn f1() -> i32 - { - println!( "f1" ); - 13 - } - }; - - index!( f1 ); + println!( "f1" ); + 13 + } + }; - a_id!( f1(), 13 ); - } + index!( f1 ); + a_id!( f1(), 13 ); } } // -tests_index! -{ - - empty_with_comma, - empty_without_comma, - with_comma, - without_comma, - parentheses_with_comma, - parentheses_without_comma, - -} +// tests_index! +// { +// +// empty_with_comma, +// empty_without_comma, +// with_comma, +// without_comma, +// parentheses_with_comma, +// parentheses_without_comma, +// +// } diff --git a/module/core/impls_index/tests/inc/mod.rs b/module/core/impls_index/tests/inc/mod.rs index d7b9687e2f..105f2928cd 100644 --- a/module/core/impls_index/tests/inc/mod.rs +++ b/module/core/impls_index/tests/inc/mod.rs @@ -1,5 +1,10 @@ -use super::*; +use super:: +{ + the_module, + only_for_terminal_module, + a_id, +}; mod func_test; mod impls_basic_test; diff --git a/module/core/impls_index/tests/inc/tests_index_test.rs b/module/core/impls_index/tests/inc/tests_index_test.rs index 9c684d5a68..d6cbf4e3c6 100644 --- a/module/core/impls_index/tests/inc/tests_index_test.rs +++ b/module/core/impls_index/tests/inc/tests_index_test.rs @@ -1,155 +1,151 @@ // use test_tools::exposed::*; use super::*; -use the_module::prelude::impls1; +use the_module::exposed::impls1; +use the_module::exposed::{ tests_index }; // -tests_impls! +#[ test ] +fn empty_with_comma() { - - fn empty_with_comma() + // test.case( "impls1 basic" ); { - // test.case( "impls1 basic" ); - { - - impls1!(); - tests_index!(); - - } + impls1!(); + tests_index!(); } +} + +#[ test ] +fn empty_without_comma() +{ - fn empty_without_comma() + // test.case( "impls1 basic" ); { - // test.case( "impls1 basic" ); + impls1! { + }; - impls1! - { - }; - - tests_index! - { - } - + tests_index! + { } } +} + +#[ test ] +fn with_comma() +{ - fn with_comma() + // test.case( "impls1 basic" ); { - // test.case( "impls1 basic" ); + impls1! { - - impls1! + fn f1() -> i32 { - fn f1() -> i32 - { - println!( "f1" ); - 13 - } - }; - - tests_index! - { - f1, + println!( "f1" ); + 13 } + }; - a_id!( f1(), 13 ); + tests_index! + { + f1, } + a_id!( f1(), 13 ); } +} + +#[ test ] +fn without_comma() +{ - fn without_comma() + // test.case( "impls1 basic" ); { - // test.case( "impls1 basic" ); + impls1! { - - impls1! - { - fn f1() -> i32 - { - println!( "f1" ); - 13 - } - }; - - tests_index! + fn f1() -> i32 { - f1 + println!( "f1" ); + 13 } + }; - a_id!( f1(), 13 ); + tests_index! + { + f1 } + a_id!( f1(), 13 ); } +} + +#[ test ] +fn parentheses_with_comma() +{ - fn parentheses_with_comma() + // test.case( "impls1 basic" ); { - // test.case( "impls1 basic" ); + impls1! { - - impls1! + fn f1() -> i32 { - fn f1() -> i32 - { - println!( "f1" ); - 13 - } - }; - - tests_index!( f1, ); + println!( "f1" ); + 13 + } + }; - a_id!( f1(), 13 ); - } + tests_index!( f1, ); + a_id!( f1(), 13 ); } +} - fn parentheses_without_comma() +#[ test ] +fn parentheses_without_comma() +{ + + // test.case( "impls1 basic" ); { - // test.case( "impls1 basic" ); + impls1! { - - impls1! + fn f1() -> i32 { - fn f1() -> i32 - { - println!( "f1" ); - 13 - } - }; - - tests_index!( f1 ); + println!( "f1" ); + 13 + } + }; - a_id!( f1(), 13 ); - } + tests_index!( f1 ); + a_id!( f1(), 13 ); } } // -tests_index! -{ - - empty_with_comma, - empty_without_comma, - with_comma, - without_comma, - parentheses_with_comma, - parentheses_without_comma, - -} +// tests_index! +// { +// +// empty_with_comma, +// empty_without_comma, +// with_comma, +// without_comma, +// parentheses_with_comma, +// parentheses_without_comma, +// +// } diff --git a/module/core/impls_index/tests/tests.rs b/module/core/impls_index/tests/tests.rs index 7d4038e715..b96684ac4a 100644 --- a/module/core/impls_index/tests/tests.rs +++ b/module/core/impls_index/tests/tests.rs @@ -1,3 +1,4 @@ +#![ allow( unused_imports ) ] include!( "../../../../module/step/meta/src/module/terminal.rs" ); diff --git a/module/core/interval_adapter/src/lib.rs b/module/core/interval_adapter/src/lib.rs index 4684d69850..3fcc39e3f1 100644 --- a/module/core/interval_adapter/src/lib.rs +++ b/module/core/interval_adapter/src/lib.rs @@ -4,7 +4,7 @@ #![ doc( html_root_url = "https://docs.rs/winterval/latest/winterval/" ) ] #![ doc = include_str!( concat!( env!( "CARGO_MANIFEST_DIR" ), "/", "Readme.md" ) ) ] -/// Internal namespace. +/// Define a private namespace for all its items. #[ cfg( feature = "enabled" ) ] mod private { diff --git a/module/core/macro_tools/src/attr.rs b/module/core/macro_tools/src/attr.rs index d87c7865b2..15c0d1e69d 100644 --- a/module/core/macro_tools/src/attr.rs +++ b/module/core/macro_tools/src/attr.rs @@ -2,7 +2,7 @@ //! Attributes analyzys and manipulation. //! -/// Internal namespace. +/// Define a private namespace for all its items. mod private { use crate::*; diff --git a/module/core/macro_tools/src/attr_prop.rs b/module/core/macro_tools/src/attr_prop.rs index a5e3aeaecd..8ffa3d9cab 100644 --- a/module/core/macro_tools/src/attr_prop.rs +++ b/module/core/macro_tools/src/attr_prop.rs @@ -102,7 +102,7 @@ mod boolean_optional; mod syn; mod syn_optional; -/// Internal namespace. +/// Define a private namespace for all its items. mod private { // use crate::*; diff --git a/module/core/macro_tools/src/components.rs b/module/core/macro_tools/src/components.rs index 5ff8c6fbe5..4e8b2ccf20 100644 --- a/module/core/macro_tools/src/components.rs +++ b/module/core/macro_tools/src/components.rs @@ -2,7 +2,7 @@ //! Type-based assigning. //! -/// Internal namespace. +/// Define a private namespace for all its items. mod private { } diff --git a/module/core/macro_tools/src/container_kind.rs b/module/core/macro_tools/src/container_kind.rs index b7cedc6149..6dc2719925 100644 --- a/module/core/macro_tools/src/container_kind.rs +++ b/module/core/macro_tools/src/container_kind.rs @@ -2,7 +2,7 @@ //! Determine kind of a container. //! -/// Internal namespace. +/// Define a private namespace for all its items. mod private { use crate::*; diff --git a/module/core/macro_tools/src/ct.rs b/module/core/macro_tools/src/ct.rs index dd3778e29b..1264aea393 100644 --- a/module/core/macro_tools/src/ct.rs +++ b/module/core/macro_tools/src/ct.rs @@ -2,7 +2,7 @@ //! Compile-time tools. //! -/// Internal namespace. +/// Define a private namespace for all its items. mod private { } diff --git a/module/core/macro_tools/src/derive.rs b/module/core/macro_tools/src/derive.rs index 84ab933fb4..dacf84f8e7 100644 --- a/module/core/macro_tools/src/derive.rs +++ b/module/core/macro_tools/src/derive.rs @@ -2,7 +2,7 @@ //! Macro helpers around derive macro and structure [`syn::DeriveInput`]. //! -/// Internal namespace. +/// Define a private namespace for all its items. mod private { use crate::*; diff --git a/module/core/macro_tools/src/diag.rs b/module/core/macro_tools/src/diag.rs index 0f8fa6f6e8..81138cd382 100644 --- a/module/core/macro_tools/src/diag.rs +++ b/module/core/macro_tools/src/diag.rs @@ -2,7 +2,7 @@ //! Macro helpers. //! -/// Internal namespace. +/// Define a private namespace for all its items. mod private { use crate::*; diff --git a/module/core/macro_tools/src/equation.rs b/module/core/macro_tools/src/equation.rs index 6a419dfe07..3d89aa4ba4 100644 --- a/module/core/macro_tools/src/equation.rs +++ b/module/core/macro_tools/src/equation.rs @@ -2,7 +2,7 @@ //! Attributes analyzys and manipulation. //! -/// Internal namespace. +/// Define a private namespace for all its items. mod private { use crate::*; diff --git a/module/core/macro_tools/src/generic_args.rs b/module/core/macro_tools/src/generic_args.rs index 16636e8ac0..1cd40e8f7a 100644 --- a/module/core/macro_tools/src/generic_args.rs +++ b/module/core/macro_tools/src/generic_args.rs @@ -2,7 +2,7 @@ //! This module provides utilities to handle and manipulate generic arguments using the `syn` crate. It includes traits and functions for transforming, merging, and managing generic parameters within procedural macros, enabling seamless syntactic analysis and code generation. //! -/// Internal namespace. +/// Define a private namespace for all its items. mod private { diff --git a/module/core/macro_tools/src/generic_params.rs b/module/core/macro_tools/src/generic_params.rs index 599b5ca7cb..fac0859aa1 100644 --- a/module/core/macro_tools/src/generic_params.rs +++ b/module/core/macro_tools/src/generic_params.rs @@ -2,7 +2,7 @@ //! Functions and structures to handle and manipulate generic parameters using the `syn` crate. It's designed to support macro-driven code generation by simplifying, merging, extracting, and decomposing `syn::Generics`. //! -/// Internal namespace. +/// Define a private namespace for all its items. mod private { use crate::*; diff --git a/module/core/macro_tools/src/item.rs b/module/core/macro_tools/src/item.rs index 605124e8a5..d82f484847 100644 --- a/module/core/macro_tools/src/item.rs +++ b/module/core/macro_tools/src/item.rs @@ -3,7 +3,7 @@ //! to manipulate the structure of items, handle different kinds of fields, and provide a structured approach to //! organizing the codebase into different access levels. -/// Internal namespace. +/// Define a private namespace for all its items. mod private { use crate::*; diff --git a/module/core/macro_tools/src/item_struct.rs b/module/core/macro_tools/src/item_struct.rs index 1855fa67b3..dd8f31f739 100644 --- a/module/core/macro_tools/src/item_struct.rs +++ b/module/core/macro_tools/src/item_struct.rs @@ -2,7 +2,7 @@ //! Parse structures, like `struct { a : i32 }`. //! -/// Internal namespace. +/// Define a private namespace for all its items. mod private { use crate::*; diff --git a/module/core/macro_tools/src/iter.rs b/module/core/macro_tools/src/iter.rs index 6ad9773801..71bf438658 100644 --- a/module/core/macro_tools/src/iter.rs +++ b/module/core/macro_tools/src/iter.rs @@ -2,7 +2,7 @@ //! Tailored iterator. //! -/// Internal namespace. +/// Define a private namespace for all its items. mod private { } diff --git a/module/core/macro_tools/src/kw.rs b/module/core/macro_tools/src/kw.rs index c7910e7571..4a29a6879d 100644 --- a/module/core/macro_tools/src/kw.rs +++ b/module/core/macro_tools/src/kw.rs @@ -2,7 +2,7 @@ //! Keywords //! -/// Internal namespace. +/// Define a private namespace for all its items. mod private { // use crate::*; diff --git a/module/core/macro_tools/src/lib.rs b/module/core/macro_tools/src/lib.rs index a11fdf7f69..8f20b5d77b 100644 --- a/module/core/macro_tools/src/lib.rs +++ b/module/core/macro_tools/src/lib.rs @@ -4,7 +4,7 @@ #![ doc = include_str!( concat!( env!( "CARGO_MANIFEST_DIR" ), "/", "Readme.md" ) ) ] -/// Internal namespace. +/// Define a private namespace for all its items. #[ cfg( feature = "enabled" ) ] mod private { diff --git a/module/core/macro_tools/src/name.rs b/module/core/macro_tools/src/name.rs index a9f53887b0..a5185ea8c4 100644 --- a/module/core/macro_tools/src/name.rs +++ b/module/core/macro_tools/src/name.rs @@ -2,7 +2,7 @@ //! Tait to getn name of an Item. //! -/// Internal namespace. +/// Define a private namespace for all its items. mod private { diff --git a/module/core/macro_tools/src/phantom.rs b/module/core/macro_tools/src/phantom.rs index f4bc1ec350..507a7fe0d1 100644 --- a/module/core/macro_tools/src/phantom.rs +++ b/module/core/macro_tools/src/phantom.rs @@ -4,7 +4,7 @@ //! Functions and structures to handle and manipulate `PhantomData` fields in structs using the `syn` crate. These utilities ensure that generic parameters are correctly accounted for in type checking, even if they are not directly used in the struct's fields. //! -/// Internal namespace. +/// Define a private namespace for all its items. mod private { use crate::*; diff --git a/module/core/macro_tools/src/punctuated.rs b/module/core/macro_tools/src/punctuated.rs index 2257904a81..893e8459fb 100644 --- a/module/core/macro_tools/src/punctuated.rs +++ b/module/core/macro_tools/src/punctuated.rs @@ -4,7 +4,7 @@ //! This module provides functionality to manipulate and ensure correct punctuation in `syn::punctuated::Punctuated` collections, commonly used in procedural macros to represent sequences of elements separated by punctuation marks, such as commas. //! -/// Internal namespace. +/// Define a private namespace for all its items. mod private { diff --git a/module/core/macro_tools/src/quantifier.rs b/module/core/macro_tools/src/quantifier.rs index 379c38e9a4..a1c3cb7833 100644 --- a/module/core/macro_tools/src/quantifier.rs +++ b/module/core/macro_tools/src/quantifier.rs @@ -2,7 +2,7 @@ //! Quantifiers like Pair and Many. //! -/// Internal namespace. +/// Define a private namespace for all its items. mod private { use crate::*; diff --git a/module/core/macro_tools/src/struct_like.rs b/module/core/macro_tools/src/struct_like.rs index 1ec494be89..5b9652b506 100644 --- a/module/core/macro_tools/src/struct_like.rs +++ b/module/core/macro_tools/src/struct_like.rs @@ -2,7 +2,7 @@ //! Parse structures, like `struct { a : i32 }`. //! -/// Internal namespace. +/// Define a private namespace for all its items. mod private { use crate::*; diff --git a/module/core/macro_tools/src/tokens.rs b/module/core/macro_tools/src/tokens.rs index 0d7fd568e8..4785c8dfc8 100644 --- a/module/core/macro_tools/src/tokens.rs +++ b/module/core/macro_tools/src/tokens.rs @@ -2,7 +2,7 @@ //! Attributes analyzys and manipulation. //! -/// Internal namespace. +/// Define a private namespace for all its items. mod private { use crate::*; diff --git a/module/core/macro_tools/src/typ.rs b/module/core/macro_tools/src/typ.rs index 03b535081e..1c1f0e39e5 100644 --- a/module/core/macro_tools/src/typ.rs +++ b/module/core/macro_tools/src/typ.rs @@ -2,7 +2,7 @@ //! Advanced syntax elements. //! -/// Internal namespace. +/// Define a private namespace for all its items. mod private { use crate::*; diff --git a/module/core/macro_tools/src/typed.rs b/module/core/macro_tools/src/typed.rs index 3eeeba271f..5e75476fb1 100644 --- a/module/core/macro_tools/src/typed.rs +++ b/module/core/macro_tools/src/typed.rs @@ -2,7 +2,7 @@ //! Typed parsing. //! -/// Internal namespace. +/// Define a private namespace for all its items. mod private { // use crate::*; diff --git a/module/core/mem_tools/src/mem.rs b/module/core/mem_tools/src/mem.rs index e35cb611cd..8a540d97e2 100644 --- a/module/core/mem_tools/src/mem.rs +++ b/module/core/mem_tools/src/mem.rs @@ -1,4 +1,4 @@ -/// Internal namespace. +/// Define a private namespace for all its items. mod private { // use crate::own::*; diff --git a/module/core/meta_tools/Cargo.toml b/module/core/meta_tools/Cargo.toml index 842fa3143e..ec2054076a 100644 --- a/module/core/meta_tools/Cargo.toml +++ b/module/core/meta_tools/Cargo.toml @@ -24,47 +24,45 @@ workspace = true features = [ "full" ] all-features = false - - [features] default = [ "enabled", "meta_for_each", "meta_impls_index", - # "meta_mod_interface", - # "meta_constructors", + "mod_interface", "meta_idents_concat", ] full = [ "enabled", "meta_for_each", "meta_impls_index", - # "meta_mod_interface", - # "meta_constructors", + "mod_interface", "meta_idents_concat", ] no_std = [] use_alloc = [ "no_std" ] -enabled = [] +enabled = [ + "for_each/enabled", + "impls_index/enabled", + "mod_interface/enabled", +] -meta_for_each = [ "for_each/enabled" ] -meta_impls_index = [ "impls_index/enabled" ] -meta_mod_interface = [ "mod_interface/enabled" ] -# xxx : qqq : make mod_interface optional maybe +meta_for_each = [ "for_each/enabled", "dep:for_each" ] +meta_impls_index = [ "impls_index/enabled", "dep:impls_index" ] +mod_interface = [ "mod_interface/enabled", "dep:mod_interface" ] # meta_constructors = [ "literally" ] -meta_idents_concat = [ "paste" ] +meta_idents_concat = [ "dep:paste" ] [dependencies] # ## external -# literally = { version = "~0.1.3", optional = true, default-features = false } paste = { workspace = true, optional = true, default-features = false } ## internal -impls_index = { workspace = true } -for_each = { workspace = true } -mod_interface = { workspace = true, features = [ "default" ] } +for_each = { workspace = true, optional = true } +impls_index = { workspace = true, optional = true } +mod_interface = { workspace = true, optional = true } [dev-dependencies] test_tools = { workspace = true } diff --git a/module/core/meta_tools/src/lib.rs b/module/core/meta_tools/src/lib.rs index 391eaf5050..e31d911ffe 100644 --- a/module/core/meta_tools/src/lib.rs +++ b/module/core/meta_tools/src/lib.rs @@ -10,35 +10,69 @@ pub mod dependency { - // #[ cfg( feature = "meta_mod_interface" ) ] pub use ::mod_interface; #[ cfg( feature = "meta_for_each" ) ] pub use ::for_each; #[ cfg( feature = "meta_impls_index" ) ] pub use ::impls_index; - - // #[ cfg( feature = "meta_constructors" ) ] - // pub use ::literally; #[ cfg( feature = "meta_idents_concat" ) ] pub use ::paste; - // #[ cfg( feature = "former" ) ] - // pub use ::former; - // #[ cfg( feature = "options" ) ] - // pub use ::woptions; - } mod private {} // -// qqq : meta interface should be optional dependancy. please fix writing equivalent code manually +// // qqq : meta interface should be optional dependancy. please fix writing equivalent code manually +// #[ cfg( feature = "enabled" ) ] +// mod_interface::mod_interface! +// { +// // #![ debug ] +// +// layer meta; +// +// } + +pub mod meta; + +#[ cfg( feature = "enabled" ) ] +#[ allow( unused_imports ) ] +pub use own::*; + +/// Own namespace of the module. +#[ cfg( feature = "enabled" ) ] +#[ allow( unused_imports ) ] +pub mod own +{ + use super::*; + pub use meta::orphan::*; +} + +/// Orphan namespace of the module. #[ cfg( feature = "enabled" ) ] -mod_interface::mod_interface! +#[ allow( unused_imports ) ] +pub mod orphan { - // #![ debug ] + use super::*; + pub use exposed::*; +} - layer meta; +/// Exposed namespace of the module. +#[ cfg( feature = "enabled" ) ] +#[ allow( unused_imports ) ] +pub mod exposed +{ + use super::*; + pub use prelude::*; + pub use meta::exposed::*; +} +/// Prelude to use essentials: `use my_module::prelude::*`. +#[ cfg( feature = "enabled" ) ] +#[ allow( unused_imports ) ] +pub mod prelude +{ + use super::*; + pub use meta::prelude::*; } diff --git a/module/core/meta_tools/src/meta.rs b/module/core/meta_tools/src/meta.rs index 1cda0b024d..1047857beb 100644 --- a/module/core/meta_tools/src/meta.rs +++ b/module/core/meta_tools/src/meta.rs @@ -2,39 +2,82 @@ //! Collection of general purpose meta tools. //! -/// Internal namespace. +/// Define a private namespace for all its items. mod private { } // +// #[ cfg( feature = "enabled" ) ] +// mod_interface::mod_interface! +// { +// #![ debug ] +// +// #[ cfg( feature = "meta_impls_index" ) ] +// use ::impls_index; +// #[ cfg( feature = "meta_for_each" ) ] +// use ::for_each; +// // #[ cfg( feature = "meta_mod_interface" ) ] +// use ::mod_interface; +// // #[ cfg( feature = "meta_mod_interface" ) ] +// prelude use ::mod_interface::mod_interface; +// +// #[ cfg( feature = "meta_idents_concat" ) ] +// prelude use ::paste::paste as meta_idents_concat; +// +// } + +#[ cfg( feature = "enabled" ) ] +#[ allow( unused_imports ) ] +pub use own::*; + +/// Own namespace of the module. +#[ cfg( feature = "enabled" ) ] +pub mod own +{ + use super::*; + pub use ::impls_index::orphan::*; + pub use ::for_each::orphan::*; + pub use ::mod_interface::orphan::*; + pub use orphan::*; +} + +/// Orphan namespace of the module. #[ cfg( feature = "enabled" ) ] -mod_interface::mod_interface! +#[ allow( unused_imports ) ] +pub mod orphan { + use super::*; + + // pub use ::impls_index; + // pub use ::for_each; + // pub use ::mod_interface; - #[ cfg( feature = "meta_impls_index" ) ] - use ::impls_index; - #[ cfg( feature = "meta_for_each" ) ] - use ::for_each; - // #[ cfg( feature = "meta_mod_interface" ) ] - use ::mod_interface; - // #[ cfg( feature = "meta_mod_interface" ) ] - prelude use ::mod_interface::mod_interface; - - // #[ cfg( feature = "meta_constructors" ) ] - // prelude use ::literally::*; - #[ cfg( feature = "meta_idents_concat" ) ] - prelude use ::paste::paste as meta_idents_concat; - - // #[ cfg( feature = "options" ) ] - // use ::woptions; - // #[ cfg( feature = "options" ) ] - // prelude use ::woptions as options; - - // #[ cfg( feature = "former" ) ] - // use ::former; - // #[ cfg( feature = "former" ) ] - // prelude use ::former as former; + pub use exposed::*; +} +/// Exposed namespace of the module. +#[ cfg( feature = "enabled" ) ] +#[ allow( unused_imports ) ] +pub mod exposed +{ + use super::*; + pub use prelude::*; + pub use super::super::meta; + pub use ::impls_index::exposed::*; + pub use ::for_each::exposed::*; + pub use ::mod_interface::exposed::*; + pub use ::paste::paste as meta_idents_concat; +} + +/// Prelude to use essentials: `use my_module::prelude::*`. +#[ cfg( feature = "enabled" ) ] +#[ allow( unused_imports ) ] +pub mod prelude +{ + use super::*; + pub use ::impls_index::prelude::*; + pub use ::for_each::prelude::*; + pub use ::mod_interface::prelude::*; } diff --git a/module/core/mod_interface/Readme.md b/module/core/mod_interface/Readme.md index d9b6f668c5..0bb7e9b255 100644 --- a/module/core/mod_interface/Readme.md +++ b/module/core/mod_interface/Readme.md @@ -11,15 +11,45 @@ Protocol of modularity unifying interface. The `mod_interface` crate provides a structured approach to modularity, addressing two key challenges in software development: -1. **Meaningful Namespace Structuring**: The crate enables developers to organize program entities into meaningful namespaces ( read modules ) without additional development overhead. This is achieved through a set of auto-importing rules and a flexible inversion of control mechanism, allowing parent namespaces to delegate control over its items to child namespaces. This approach ensures that each namespace is self-contained and meaningful, promoting better organization and modularity. +1. **Meaningful Namespace Structuring**: The crate enables developers to organize program entities into meaningful namespaces ( read modules ) without additional development overhead. This is achieved through a set of auto-importing rules and a flexible inversion of control mechanism, allowing parent layers ( namespaces or modules ) to delegate control over its items to child layers. This approach ensures that each namespace is self-contained and meaningful, promoting better organization and modularity. 2. **Enhanced Readability and Tooling Independence**: By requiring a `mod private` section that lists all items ( read functions, structures, traits, types ) the `mod_interface` macro encourages developers to create a concise list of items at the beginning or end of a file. This improves readability, encourages refactoring, and reduces cognitive load by providing a clear, high-level grouping of items. Code tooling is not always reliable and can sometimes be counterproductive by automating tasks that should be done manually to achieve more concise code. While code tooling like `rust_analyzer` are useful, this approach minimizes reliance on them, making the program's structure easier to understand and manage. While some may argue that inversion of control over namespaces may not always achieve the desired outcome, and code tooling can be sufficient, the `mod_interface` crate offers a cathartic solution for designing complex systems where tooling and triditional structuring often fall short. By promoting a clear and organized structure, it helps developers grasp the semantics of their programs more holistically. -### Example : Trivial +### Basic Concepts -This example demonstrates how to use the `mod_interface` crate to organize a Rust program into structured namespaces. The code is divided into a library file (`child.rs`) and a main function. The library file defines a module with private functions and uses the `mod_interface` macro to specify which functions should be exposed in different namespaces. The main function then tests the visibility and accessibility of these functions. +In the `mod_interface` crate, the concepts of layers and namespaces are central to its modularity approach. Here's a refined explanation: + +- **Namespaces**: These are standard Rust modules that help organize code into logical groups. +- **Layers**: A layer is a specialized module that contains a set of predefined submodules, referred to as chapters. These chapters dictate how the contents of the module are propagated to parent layers. + +The chapters within a layer are: + +- **Private Chapter (`private`)**: This is where all the code and entities are initially defined. It is not accessible outside the module. +- **Public Chapter (`own`)**: Contains items that are not propagated to any parent layers. They remain within the module. +- **Public Chapter (`orphan`)**: Shares its contents with the immediate parent layer only. +- **Public Chapter (`exposed`)**: Propagates its contents to all parent layers, making them accessible throughout the hierarchy. +- **Public Chapter (`prelude`)**: Similar to `exposed`, but also serves as a recommendation for other crates to implicitly import its contents, akin to the prelude in the [Rust standard library](https://doc.rust-lang.org/std/prelude/index.html). + +Developers should define all entities within the `private` chapter and then re-export them through the other four chapters based on the desired propagation strategy. + +### Syntax of `mod_interface` Macro + +The `mod_interface` macro provides several directives to manage the relationships between layers and entities: + +- **`layer `**: Establishes a relationship where the current layer uses a child layer. +- **`use `**: Allows the current layer to use another layer defined elsewhere. +- **`reuse `**: Enables the current layer to reuse a layer defined anywhere, promoting code reuse. +- **` use `**: Allows the current layer to use an entity defined anywhere, with the specified promotion stategy (``). + +These directives provide flexibility in organizing and managing the modular structure of a Rust program, enhancing both readability and maintainability. + +### Example: Using Layers and Entities + +In this example, we demonstrate the basic use case of one layer utilizing another layer. For a module to be used as a layer, it must contain all the necessary chapters: `orphan`, `exposed`, and `prelude`. Generally, a layer should also have the `own` and `private` chapters, but these are typically not modified directly by the user unless explicitly defined, with the `private` chapter remaining inaccessible from outside the module. + +Below is a simple example where a parent layer imports a `child` layer. The `child` layer defines several functions, each with a different propagation strategy, resulting in each function being placed in a different chapter of the parent layer, while some functions do not reach the parent layer at all. ```rust use mod_interface::mod_interface; @@ -62,7 +92,6 @@ crate::mod_interface! use super::child; } - fn main() { @@ -125,29 +154,34 @@ pub mod child /// Own namespace of the module. pub mod own { - pub use super::orphan::*; - pub use super::private::my_thing; + use super::*; + pub use orphan::*; + pub use private::my_thing; } /// Orphan namespace of the module. pub mod orphan { - pub use super::exposed::*; - pub use super::private::orphan_thing; + use super::*; + pub use exposed::*; + pub use private::orphan_thing; } /// Exposed namespace of the module. pub mod exposed { - pub use super::prelude::*; - pub use super::private::exposed_thing; + use super::*; + pub use prelude::*; + pub use private::exposed_thing; } /// Prelude to use essentials: `use my_module::prelude::*`. pub mod prelude { - pub use super::private::prelude_thing; + use super::*; + pub use private::prelude_thing; } + } // Priave namespaces is necessary. @@ -161,7 +195,7 @@ pub mod own { use super::*; pub use orphan::*; - pub use super::child::orphan::*; + pub use child::orphan::*; pub use super::child; } @@ -179,7 +213,7 @@ pub mod exposed { use super::*; pub use prelude::*; - pub use super::child::exposed::*; + pub use child::exposed::*; } /// Prelude to use essentials: `use my_module::prelude::*`. @@ -187,7 +221,7 @@ pub mod exposed pub mod prelude { use super::*; - pub use super::child::prelude::*; + pub use child::prelude::*; } // @@ -229,6 +263,41 @@ fn main() +As you can see: + +- The content of the `prelude` chapter is automatically propagated into the `exposed` chapter of the same layer. +- The content of the `exposed` chapter is automatically propagated into the `orphan` chapter of the same layer. +- The content of the `orphan` chapter is automatically propagated into the `own` chapter of the same layer. +- The content of the `own` chapter is not automatically propagated anywhere. + +### `layer ` vs `use ` + +The `use ;` syntax is used to import a layer from anywhere within the project or even from external crates. This provides flexibility in how layers are organized, as the layer being used does not need to be a direct submodule of the current module. It allows you to bring any accessible layer into the current scope without enforcing a specific file structure. The visibility of the imported layer remains as it is defined in its original location, and this syntax does not inherently change that visibility. + +In contrast, the `layer ` syntax is used to establish a hierarchical relationship where the current module uses a child layer. This requires the child layer to be a direct submodule, meaning it must be physically present in the file structure as a submodule. The `layer ` syntax implies `pub mod layer1`, making the child layer publicly accessible as a submodule. This enforces a specific organizational structure, where the child layer is part of the current module's hierarchy, and its contents are directly accessible according to the defined propagation strategies. + +Thus, `layer ` acts as a shortcut, combining the definition of a reference to a module file and using it, while `use ` uses a module that is already defined somewhere, not necessarily in the same crate. + +### `reuse ` vs `use ` + +The `reuse ` syntax treats the child layer as an integral part of the parent layer, so the normal rules of propagation do not apply to the content of the child layer. Specifically, the `own` chapter of the child layer is imported into the `own` chapter of the parent layer, and the `orphan` chapter of the child layer is imported into the `orphan` chapter of the parent layer. + +In contrast, `use ` follows the standard propagation rules: + +- `child::own` is not propagated. +- `child::orphan` is imported into `parent::own`. +- `child::exposed` is imported into `parent::exposed`. +- `child::prelude` is imported into `parent::prelude`. + +For `reuse `, the propagation is as follows: + +- `child::own` is imported into `parent::own`. +- `child::orphan` is imported into `parent::orphan`. +- `child::exposed` is imported into `parent::exposed`. +- `child::prelude` is imported into `parent::prelude`. + +`reusing` does not impact parent of parent or child of child. + ### Debugging To debug module interface use directive `#![ debug ]` in macro `mod_interface`. Let's update the main file of the example : diff --git a/module/core/mod_interface/examples/mod_interface_trivial/Readme.md b/module/core/mod_interface/examples/mod_interface_trivial/Readme.md index 343322a31c..e7b67c25d1 100644 --- a/module/core/mod_interface/examples/mod_interface_trivial/Readme.md +++ b/module/core/mod_interface/examples/mod_interface_trivial/Readme.md @@ -1,11 +1,9 @@ -# Sample +### Example: Using Layers and Entities [![discord](https://img.shields.io/discord/872391416519737405?color=eee&logo=discord&logoColor=eee&label=ask)](https://discord.gg/m3YfbXpUUY) [![Open in Gitpod](https://raster.shields.io/static/v1?label=try&message=online&color=eee&logo=gitpod&logoColor=eee)](https://gitpod.io/#RUN_PATH=sample%2Frust%2Fmod_interface_trivial,SAMPLE_FILE=.%2Fsrc%2Fmain.rs/https://github.com/Wandalen/wTools) [![docs.rs](https://raster.shields.io/static/v1?label=docs&message=online&color=eee&logo=docsdotrs&logoColor=eee)](https://docs.rs/mod_interface) -A sample demonstrates basic usage of macro `mod_interface`. +In this example, we demonstrate the basic use case of one layer utilizing another layer. For a module to be used as a layer, it must contain all the necessary chapters: `orphan`, `exposed`, and `prelude`. Generally, a layer should also have the `own` and `private` chapters, but these are typically not modified directly by the user unless explicitly defined, with the `private` chapter remaining inaccessible from outside the module. -In file `inner.rs` demonstrated how to generate module interface from namespace `private` and its public routine. - -In file `main.rs` demonstrated how to generate module interface from layer ( file with full module interface ). +Below is a simple example where a parent layer imports a `child` layer. The `child` layer defines several functions, each with a different propagation strategy, resulting in each function being placed in a different chapter of the parent layer, while some functions do not reach the parent layer at all. diff --git a/module/core/mod_interface/tests/inc/derive/micro_modules_glob/mod.rs b/module/core/mod_interface/tests/inc/derive/micro_modules_glob/mod.rs index b9c78c9198..f567778739 100644 --- a/module/core/mod_interface/tests/inc/derive/micro_modules_glob/mod.rs +++ b/module/core/mod_interface/tests/inc/derive/micro_modules_glob/mod.rs @@ -1,7 +1,7 @@ // use super::*; -/// Internal namespace. +/// Define a private namespace for all its items. mod private { pub struct Struct1; diff --git a/module/core/mod_interface/tests/inc/derive/reuse_basic/mod.rs b/module/core/mod_interface/tests/inc/derive/reuse_basic/mod.rs index 89ae6afbe8..8ee5259142 100644 --- a/module/core/mod_interface/tests/inc/derive/reuse_basic/mod.rs +++ b/module/core/mod_interface/tests/inc/derive/reuse_basic/mod.rs @@ -1,7 +1,7 @@ // use super::*; -/// Internal namespace. +/// Define a private namespace for all its items. mod private { } diff --git a/module/core/mod_interface_meta/src/impls.rs b/module/core/mod_interface_meta/src/impls.rs index 84fd04d881..81c6cec066 100644 --- a/module/core/mod_interface_meta/src/impls.rs +++ b/module/core/mod_interface_meta/src/impls.rs @@ -1,4 +1,4 @@ -/// Internal namespace. +/// Define a private namespace for all its items. mod private { use crate::*; diff --git a/module/core/mod_interface_meta/src/record.rs b/module/core/mod_interface_meta/src/record.rs index 70e8f289ec..aeb6a696eb 100644 --- a/module/core/mod_interface_meta/src/record.rs +++ b/module/core/mod_interface_meta/src/record.rs @@ -1,4 +1,4 @@ -/// Internal namespace. +/// Define a private namespace for all its items. mod private { use crate::*; diff --git a/module/core/mod_interface_meta/src/use_tree.rs b/module/core/mod_interface_meta/src/use_tree.rs index 2191171530..3f51fccc22 100644 --- a/module/core/mod_interface_meta/src/use_tree.rs +++ b/module/core/mod_interface_meta/src/use_tree.rs @@ -1,4 +1,4 @@ -/// Internal namespace. +/// Define a private namespace for all its items. mod private { use macro_tools::prelude::*; diff --git a/module/core/mod_interface_meta/src/visibility.rs b/module/core/mod_interface_meta/src/visibility.rs index acece0cb4f..4e229ed21c 100644 --- a/module/core/mod_interface_meta/src/visibility.rs +++ b/module/core/mod_interface_meta/src/visibility.rs @@ -1,4 +1,4 @@ -/// Internal namespace. +/// Define a private namespace for all its items. mod private { use macro_tools::prelude::*; diff --git a/module/core/process_tools/src/environment.rs b/module/core/process_tools/src/environment.rs index 6ba4ba20fd..31d20aa9f0 100644 --- a/module/core/process_tools/src/environment.rs +++ b/module/core/process_tools/src/environment.rs @@ -1,4 +1,4 @@ -/// Internal namespace. +/// Define a private namespace for all its items. mod private { diff --git a/module/core/process_tools/src/process.rs b/module/core/process_tools/src/process.rs index 8636c628b5..d58e95455a 100644 --- a/module/core/process_tools/src/process.rs +++ b/module/core/process_tools/src/process.rs @@ -1,4 +1,4 @@ -/// Internal namespace. +/// Define a private namespace for all its items. mod private { // use crate::*; diff --git a/module/core/program_tools/src/program.rs b/module/core/program_tools/src/program.rs index 70d66a7ead..90737df823 100644 --- a/module/core/program_tools/src/program.rs +++ b/module/core/program_tools/src/program.rs @@ -1,4 +1,4 @@ -/// Internal namespace. +/// Define a private namespace for all its items. mod private { diff --git a/module/core/pth/src/as_path.rs b/module/core/pth/src/as_path.rs index b94a4cf4d4..693532a081 100644 --- a/module/core/pth/src/as_path.rs +++ b/module/core/pth/src/as_path.rs @@ -1,4 +1,4 @@ -/// Internal namespace. +/// Define a private namespace for all its items. mod private { #[ allow( unused_imports ) ] diff --git a/module/core/pth/src/path.rs b/module/core/pth/src/path.rs index 7907a3268a..504dcdd25f 100644 --- a/module/core/pth/src/path.rs +++ b/module/core/pth/src/path.rs @@ -1,4 +1,4 @@ -/// Internal namespace. +/// Define a private namespace for all its items. mod private { diff --git a/module/core/pth/src/path/absolute_path.rs b/module/core/pth/src/path/absolute_path.rs index 13a9686207..c80f0ce2c1 100644 --- a/module/core/pth/src/path/absolute_path.rs +++ b/module/core/pth/src/path/absolute_path.rs @@ -1,4 +1,4 @@ -/// Internal namespace. +/// Define a private namespace for all its items. mod private { use crate::*; diff --git a/module/core/pth/src/path/canonical_path.rs b/module/core/pth/src/path/canonical_path.rs index b7a871af4d..dd8a6957c6 100644 --- a/module/core/pth/src/path/canonical_path.rs +++ b/module/core/pth/src/path/canonical_path.rs @@ -1,4 +1,4 @@ -/// Internal namespace. +/// Define a private namespace for all its items. mod private { diff --git a/module/core/pth/src/path/current_path.rs b/module/core/pth/src/path/current_path.rs index cc5fe4aaaa..8ddf322b2a 100644 --- a/module/core/pth/src/path/current_path.rs +++ b/module/core/pth/src/path/current_path.rs @@ -1,4 +1,4 @@ -/// Internal namespace. +/// Define a private namespace for all its items. mod private { diff --git a/module/core/pth/src/path/native_path.rs b/module/core/pth/src/path/native_path.rs index 09dfaaed62..80b75715b2 100644 --- a/module/core/pth/src/path/native_path.rs +++ b/module/core/pth/src/path/native_path.rs @@ -1,4 +1,4 @@ -/// Internal namespace. +/// Define a private namespace for all its items. mod private { diff --git a/module/core/pth/src/transitive.rs b/module/core/pth/src/transitive.rs index 93bbcd3e10..ce1f366765 100644 --- a/module/core/pth/src/transitive.rs +++ b/module/core/pth/src/transitive.rs @@ -1,4 +1,4 @@ -/// Internal namespace. +/// Define a private namespace for all its items. mod private { // xxx : move to derive_tools diff --git a/module/core/pth/src/try_into_cow_path.rs b/module/core/pth/src/try_into_cow_path.rs index b9f04524ce..af03344f74 100644 --- a/module/core/pth/src/try_into_cow_path.rs +++ b/module/core/pth/src/try_into_cow_path.rs @@ -1,4 +1,4 @@ -/// Internal namespace. +/// Define a private namespace for all its items. mod private { use crate::*; diff --git a/module/core/pth/src/try_into_path.rs b/module/core/pth/src/try_into_path.rs index 29f508ec1b..979ee754f9 100644 --- a/module/core/pth/src/try_into_path.rs +++ b/module/core/pth/src/try_into_path.rs @@ -1,4 +1,4 @@ -/// Internal namespace. +/// Define a private namespace for all its items. mod private { #[ allow( unused_imports ) ] diff --git a/module/core/reflect_tools/src/reflect.rs b/module/core/reflect_tools/src/reflect.rs index 0cde174ac9..3d363b1c09 100644 --- a/module/core/reflect_tools/src/reflect.rs +++ b/module/core/reflect_tools/src/reflect.rs @@ -52,7 +52,7 @@ // qqq : make the example working. use tests for inpsisrations -/// Internal namespace. +/// Define a private namespace for all its items. mod private { } diff --git a/module/core/reflect_tools/src/reflect/axiomatic.rs b/module/core/reflect_tools/src/reflect/axiomatic.rs index dcc6f044c8..2a092dfd0b 100644 --- a/module/core/reflect_tools/src/reflect/axiomatic.rs +++ b/module/core/reflect_tools/src/reflect/axiomatic.rs @@ -4,7 +4,7 @@ use super::*; -/// Internal namespace. +/// Define a private namespace for all its items. mod private { use super::*; diff --git a/module/core/reflect_tools/src/reflect/entity_array.rs b/module/core/reflect_tools/src/reflect/entity_array.rs index afe3363b7c..3a9e592116 100644 --- a/module/core/reflect_tools/src/reflect/entity_array.rs +++ b/module/core/reflect_tools/src/reflect/entity_array.rs @@ -4,7 +4,7 @@ use super::*; -/// Internal namespace. +/// Define a private namespace for all its items. pub mod private { use super::*; diff --git a/module/core/reflect_tools/src/reflect/entity_hashmap.rs b/module/core/reflect_tools/src/reflect/entity_hashmap.rs index 97d9a821c9..21f7a04f35 100644 --- a/module/core/reflect_tools/src/reflect/entity_hashmap.rs +++ b/module/core/reflect_tools/src/reflect/entity_hashmap.rs @@ -4,7 +4,7 @@ use super::*; -/// Internal namespace. +/// Define a private namespace for all its items. pub mod private { use super::*; diff --git a/module/core/reflect_tools/src/reflect/entity_hashset.rs b/module/core/reflect_tools/src/reflect/entity_hashset.rs index ba48a7a189..84803f0c77 100644 --- a/module/core/reflect_tools/src/reflect/entity_hashset.rs +++ b/module/core/reflect_tools/src/reflect/entity_hashset.rs @@ -4,7 +4,7 @@ use super::*; -/// Internal namespace. +/// Define a private namespace for all its items. pub mod private { use super::*; diff --git a/module/core/reflect_tools/src/reflect/entity_slice.rs b/module/core/reflect_tools/src/reflect/entity_slice.rs index e396f8bcf9..1584c874f2 100644 --- a/module/core/reflect_tools/src/reflect/entity_slice.rs +++ b/module/core/reflect_tools/src/reflect/entity_slice.rs @@ -4,7 +4,7 @@ use super::*; -/// Internal namespace. +/// Define a private namespace for all its items. pub mod private { use super::*; diff --git a/module/core/reflect_tools/src/reflect/entity_vec.rs b/module/core/reflect_tools/src/reflect/entity_vec.rs index c559136729..ec74a41b00 100644 --- a/module/core/reflect_tools/src/reflect/entity_vec.rs +++ b/module/core/reflect_tools/src/reflect/entity_vec.rs @@ -4,7 +4,7 @@ use super::*; -/// Internal namespace. +/// Define a private namespace for all its items. pub mod private { use super::*; diff --git a/module/core/reflect_tools/src/reflect/fields.rs b/module/core/reflect_tools/src/reflect/fields.rs index edbdfbc9b4..811b9835d2 100644 --- a/module/core/reflect_tools/src/reflect/fields.rs +++ b/module/core/reflect_tools/src/reflect/fields.rs @@ -2,7 +2,7 @@ //! Iterator over fields. //! -/// Internal namespace. +/// Define a private namespace for all its items. mod private { diff --git a/module/core/reflect_tools/src/reflect/primitive.rs b/module/core/reflect_tools/src/reflect/primitive.rs index 986291afb8..23ce9a125e 100644 --- a/module/core/reflect_tools/src/reflect/primitive.rs +++ b/module/core/reflect_tools/src/reflect/primitive.rs @@ -2,7 +2,7 @@ //! Define primitive and data types. //! -/// Internal namespace. +/// Define a private namespace for all its items. mod private { diff --git a/module/core/reflect_tools/src/reflect/wrapper.rs b/module/core/reflect_tools/src/reflect/wrapper.rs index 31ad09a99a..8481bce1c7 100644 --- a/module/core/reflect_tools/src/reflect/wrapper.rs +++ b/module/core/reflect_tools/src/reflect/wrapper.rs @@ -2,7 +2,7 @@ //! Collection of wrappers. //! -/// Internal namespace. +/// Define a private namespace for all its items. mod private { } diff --git a/module/core/strs_tools/src/string/indentation.rs b/module/core/strs_tools/src/string/indentation.rs index b4574f3fbc..59c29951eb 100644 --- a/module/core/strs_tools/src/string/indentation.rs +++ b/module/core/strs_tools/src/string/indentation.rs @@ -1,4 +1,4 @@ -/// Internal namespace. +/// Define a private namespace for all its items. mod private { diff --git a/module/core/strs_tools/src/string/number.rs b/module/core/strs_tools/src/string/number.rs index 69f8b2c0d1..1a0df1c3ad 100644 --- a/module/core/strs_tools/src/string/number.rs +++ b/module/core/strs_tools/src/string/number.rs @@ -1,4 +1,4 @@ -/// Internal namespace. +/// Define a private namespace for all its items. mod private { } diff --git a/module/core/strs_tools/src/string/parse_request.rs b/module/core/strs_tools/src/string/parse_request.rs index 432b2098b6..44e215d15d 100644 --- a/module/core/strs_tools/src/string/parse_request.rs +++ b/module/core/strs_tools/src/string/parse_request.rs @@ -1,4 +1,4 @@ -/// Internal namespace. +/// Define a private namespace for all its items. mod private { use crate::*; diff --git a/module/core/test_tools/Cargo.toml b/module/core/test_tools/Cargo.toml index f1e7576436..01ee46f740 100644 --- a/module/core/test_tools/Cargo.toml +++ b/module/core/test_tools/Cargo.toml @@ -69,7 +69,7 @@ enabled = [ normal = [ "dep:error_tools", "dep:collection_tools", - "dep:meta_tools", + "dep:impls_index", "dep:mem_tools", "dep:typing_tools", "dep:diagnostics_tools", @@ -78,8 +78,8 @@ normal = [ standalone = [ "standalone_error_tools", "standalone_collection_tools", - - "dep:meta_tools", + "standalone_impls_index", + "standalone_mem_tools", "dep:mem_tools", "dep:typing_tools", "dep:diagnostics_tools", @@ -87,6 +87,8 @@ standalone = [ ] standalone_error_tools = [ "dep:anyhow", "dep:thiserror", "error_typed", "error_untyped" ] standalone_collection_tools = [ "dep:hashbrown", "collection_constructors", "collection_into_constructors" ] +standalone_impls_index = [ "dep:impls_index_meta" ] +standalone_mem_tools = [] # error_tools error_typed = [] @@ -99,18 +101,18 @@ collection_into_constructors = [] ## external -# paste = "~1.0" # zzz : remove later -rustversion = "~1.0" -num-traits = "~0.2" trybuild = { version = "1.0.85", features = [ "diff" ] } -rand = "0.8.5" +rustversion = { workspace = true } +num-traits = { workspace = true } +rand = { workspace = true } +# tempdir = { workspace = true } ## internal error_tools = { workspace = true, features = [ "full" ], optional = true } collection_tools = { workspace = true, features = [ "full" ], optional = true } +impls_index = { workspace = true, features = [ "full" ], optional = true } -meta_tools = { workspace = true, features = [ "full" ], optional = true } mem_tools = { workspace = true, features = [ "full" ], optional = true } typing_tools = { workspace = true, features = [ "full" ], optional = true } diagnostics_tools = { workspace = true, features = [ "full" ], optional = true } @@ -125,6 +127,9 @@ anyhow = { workspace = true, optional = true } thiserror = { workspace = true, optional = true } # collection_tools hashbrown = { workspace = true, optional = true } +# impls_index +impls_index_meta = { workspace = true, optional = true } +# mem_tools [build-dependencies] rustc_version = "0.4" diff --git a/module/core/test_tools/src/lib.rs b/module/core/test_tools/src/lib.rs index ced13dc4d8..cb0d390392 100644 --- a/module/core/test_tools/src/lib.rs +++ b/module/core/test_tools/src/lib.rs @@ -18,26 +18,20 @@ pub mod dependency pub use ::trybuild; #[ doc( inline ) ] pub use ::rustversion; + #[ doc( inline ) ] + pub use ::num_traits; - // #[ doc( inline ) ] - // pub use ::error_tools; -// #[ doc( inline ) ] -// pub use ::meta_tools; -// #[ doc( inline ) ] -// pub use ::mem_tools; -// #[ doc( inline ) ] -// pub use ::typing_tools; -// #[ doc( inline ) ] -// pub use ::num_traits; -// #[ doc( inline ) ] -// pub use ::diagnostics_tools; -// -// #[ doc( inline ) ] -// pub use ::process_tools; - - // #[ doc( inline ) ] - // #[ allow( unused_imports ) ] - // pub use ::process_tools as process_tools; + #[ doc( inline ) ] + pub use super:: + { + error_tools, + collection_tools, + impls_index, + mem_tools, + typing_tools, + diagnostics_tools, + process_tools, + }; } @@ -103,18 +97,47 @@ mod private {} pub mod test; /// Error tools. +#[ cfg( feature = "enabled" ) ] #[ cfg( feature = "standalone" ) ] -#[ path = "../../../core/error_tools/src/error/mod.rs" ] -pub mod error; -#[ cfg( feature = "standalone" ) ] -pub use error as error_tools; +mod standalone +{ -/// Collection tools. -#[ cfg( feature = "standalone" ) ] -#[ path = "../../../core/collection_tools/src/collection/mod.rs" ] -pub mod collection; + /// Error tools. + #[ path = "../../../../core/error_tools/src/error/mod.rs" ] + pub mod error; + pub use error as error_tools; + + /// Collection tools. + #[ path = "../../../../core/collection_tools/src/collection/mod.rs" ] + pub mod collection; + pub use collection as collection_tools; + + /// impl index macroc. + #[ path = "../../../../core/impls_index/src/impls_index/mod.rs" ] + pub mod impls_index; + +} +#[ cfg( feature = "enabled" ) ] #[ cfg( feature = "standalone" ) ] -pub use collection as collection_tools; +pub use standalone::*; + +#[ cfg( feature = "enabled" ) ] +#[ cfg( not( feature = "standalone" ) ) ] +pub use :: +{ + error_tools, + collection_tools, + impls_index, +}; + +#[ cfg( feature = "enabled" ) ] +pub use :: +{ + mem_tools, + typing_tools, + diagnostics_tools, + process_tools, +}; #[ cfg( feature = "enabled" ) ] #[ doc( inline ) ] diff --git a/module/core/test_tools/src/test/asset.rs b/module/core/test_tools/src/test/asset.rs index e206fc8c0f..c32cf9cb91 100644 --- a/module/core/test_tools/src/test/asset.rs +++ b/module/core/test_tools/src/test/asset.rs @@ -3,7 +3,7 @@ //! Test asset helper. //! -/// Internal namespace. +/// Define a private namespace for all its items. // #[ cfg( not( feature = "no_std" ) ) ] mod private { diff --git a/module/core/test_tools/src/test/compiletime.rs b/module/core/test_tools/src/test/compiletime.rs index 7ea5edf31f..9792d17e3d 100644 --- a/module/core/test_tools/src/test/compiletime.rs +++ b/module/core/test_tools/src/test/compiletime.rs @@ -3,7 +3,7 @@ //! Try building a program for negative testing. //! -/// Internal namespace. +/// Define a private namespace for all its items. mod private { #[ doc( inline ) ] diff --git a/module/core/test_tools/src/test/helper.rs b/module/core/test_tools/src/test/helper.rs index 4a97b134da..16100d426a 100644 --- a/module/core/test_tools/src/test/helper.rs +++ b/module/core/test_tools/src/test/helper.rs @@ -5,7 +5,7 @@ // use super::*; -/// Internal namespace. +/// Define a private namespace for all its items. mod private { diff --git a/module/core/test_tools/src/test/mod.rs b/module/core/test_tools/src/test/mod.rs index 0d62f6e9d2..c13f70ef67 100644 --- a/module/core/test_tools/src/test/mod.rs +++ b/module/core/test_tools/src/test/mod.rs @@ -80,7 +80,8 @@ pub mod exposed version::exposed::*, }; - pub use meta_tools:: + #[ doc( inline ) ] + pub use crate::impls_index:: { impls, index, diff --git a/module/core/test_tools/src/test/smoke_test.rs b/module/core/test_tools/src/test/smoke_test.rs index 0074d4f090..a99b4ee1a0 100644 --- a/module/core/test_tools/src/test/smoke_test.rs +++ b/module/core/test_tools/src/test/smoke_test.rs @@ -8,7 +8,7 @@ // xxx2 : use process_tools to build and run rust programs, introduce program_ -/// Internal namespace. +/// Define a private namespace for all its items. mod private { #[ allow( unused_imports ) ] diff --git a/module/core/test_tools/src/test/version.rs b/module/core/test_tools/src/test/version.rs index 8cdc5cbbc8..f20821e54e 100644 --- a/module/core/test_tools/src/test/version.rs +++ b/module/core/test_tools/src/test/version.rs @@ -3,7 +3,7 @@ //! Version of Rust compiler //! -/// Internal namespace. +/// Define a private namespace for all its items. // #[ cfg( not( feature = "no_std" ) ) ] mod private { diff --git a/module/core/variadic_from/src/variadic.rs b/module/core/variadic_from/src/variadic.rs index 715a135960..1297cb443c 100644 --- a/module/core/variadic_from/src/variadic.rs +++ b/module/core/variadic_from/src/variadic.rs @@ -2,7 +2,7 @@ //! Variadic constructor. Constructor with n arguments. Like Default, but with arguments. //! -/// Internal namespace. +/// Define a private namespace for all its items. mod private { diff --git a/module/move/assistant/src/client.rs b/module/move/assistant/src/client.rs index 51a00cb368..3276edc7a9 100644 --- a/module/move/assistant/src/client.rs +++ b/module/move/assistant/src/client.rs @@ -2,7 +2,7 @@ //! Client of API. //! -/// Internal namespace. +/// Define a private namespace for all its items. mod private { diff --git a/module/move/assistant/src/debug.rs b/module/move/assistant/src/debug.rs index e5966e4f2c..d35c02699a 100644 --- a/module/move/assistant/src/debug.rs +++ b/module/move/assistant/src/debug.rs @@ -2,7 +2,7 @@ //! Client of API. //! -/// Internal namespace. +/// Define a private namespace for all its items. mod private { diff --git a/module/move/assistant/src/lib.rs b/module/move/assistant/src/lib.rs index 2c0c103663..dd2ca047ba 100644 --- a/module/move/assistant/src/lib.rs +++ b/module/move/assistant/src/lib.rs @@ -5,7 +5,7 @@ use mod_interface::mod_interface; -/// Internal namespace. +/// Define a private namespace for all its items. mod private { } diff --git a/module/move/crates_tools/src/lib.rs b/module/move/crates_tools/src/lib.rs index 92dc8c0048..f43c975b7f 100644 --- a/module/move/crates_tools/src/lib.rs +++ b/module/move/crates_tools/src/lib.rs @@ -3,7 +3,7 @@ #![ doc( html_root_url = "https://docs.rs/crates_tools/latest/crates_tools/" ) ] #![ doc = include_str!( concat!( env!( "CARGO_MANIFEST_DIR" ), "/", "Readme.md" ) ) ] -/// Internal namespace. +/// Define a private namespace for all its items. #[ cfg( feature = "enabled" ) ] mod private { diff --git a/module/move/deterministic_rand/src/hrng_deterministic.rs b/module/move/deterministic_rand/src/hrng_deterministic.rs index ceb64b06c0..e489d8522e 100644 --- a/module/move/deterministic_rand/src/hrng_deterministic.rs +++ b/module/move/deterministic_rand/src/hrng_deterministic.rs @@ -6,7 +6,7 @@ //! Both have the same interface and are interchengable by switching on/off a feature `determinsim`. //! -/// Internal namespace. +/// Define a private namespace for all its items. mod private { diff --git a/module/move/deterministic_rand/src/hrng_non_deterministic.rs b/module/move/deterministic_rand/src/hrng_non_deterministic.rs index 1ab19d55d2..57db16656b 100644 --- a/module/move/deterministic_rand/src/hrng_non_deterministic.rs +++ b/module/move/deterministic_rand/src/hrng_non_deterministic.rs @@ -6,7 +6,7 @@ //! Both have the same interface and are interchengable by switching on/off a feature `determinsim`. //! -/// Internal namespace. +/// Define a private namespace for all its items. mod private { diff --git a/module/move/deterministic_rand/src/iter.rs b/module/move/deterministic_rand/src/iter.rs index caab96a148..cdfb83e100 100644 --- a/module/move/deterministic_rand/src/iter.rs +++ b/module/move/deterministic_rand/src/iter.rs @@ -3,7 +3,7 @@ //! Extensions of iterator for determinism. //! -/// Internal namespace. +/// Define a private namespace for all its items. mod private { diff --git a/module/move/deterministic_rand/src/seed.rs b/module/move/deterministic_rand/src/seed.rs index c7f1e078a9..fc68cc4cdf 100644 --- a/module/move/deterministic_rand/src/seed.rs +++ b/module/move/deterministic_rand/src/seed.rs @@ -3,7 +3,7 @@ //! Master seed. //! -/// Internal namespace. +/// Define a private namespace for all its items. mod private { #[ cfg( feature = "no_std" ) ] diff --git a/module/move/graphs_tools/src/abs/edge.rs b/module/move/graphs_tools/src/abs/edge.rs index 550a350efb..214f8f10d9 100644 --- a/module/move/graphs_tools/src/abs/edge.rs +++ b/module/move/graphs_tools/src/abs/edge.rs @@ -1,4 +1,4 @@ -/// Internal namespace. +/// Define a private namespace for all its items. mod private { use crate::prelude::*; diff --git a/module/move/graphs_tools/src/abs/factory.rs b/module/move/graphs_tools/src/abs/factory.rs index 0f6d19e324..ad6bc76c8b 100644 --- a/module/move/graphs_tools/src/abs/factory.rs +++ b/module/move/graphs_tools/src/abs/factory.rs @@ -1,4 +1,4 @@ -/// Internal namespace. +/// Define a private namespace for all its items. mod private { use crate::prelude::*; diff --git a/module/move/graphs_tools/src/abs/id_generator.rs b/module/move/graphs_tools/src/abs/id_generator.rs index 943315c041..2090439804 100644 --- a/module/move/graphs_tools/src/abs/id_generator.rs +++ b/module/move/graphs_tools/src/abs/id_generator.rs @@ -1,4 +1,4 @@ -/// Internal namespace. +/// Define a private namespace for all its items. mod private { // use crate::prelude::*; diff --git a/module/move/graphs_tools/src/abs/identity.rs b/module/move/graphs_tools/src/abs/identity.rs index 412b759d73..287e69f4b1 100644 --- a/module/move/graphs_tools/src/abs/identity.rs +++ b/module/move/graphs_tools/src/abs/identity.rs @@ -1,4 +1,4 @@ -/// Internal namespace. +/// Define a private namespace for all its items. mod private { // use crate::prelude::*; diff --git a/module/move/graphs_tools/src/abs/node.rs b/module/move/graphs_tools/src/abs/node.rs index b227581718..703bd0893d 100644 --- a/module/move/graphs_tools/src/abs/node.rs +++ b/module/move/graphs_tools/src/abs/node.rs @@ -1,4 +1,4 @@ -/// Internal namespace. +/// Define a private namespace for all its items. mod private { use crate::prelude::*; diff --git a/module/move/graphs_tools/src/algo/dfs.rs b/module/move/graphs_tools/src/algo/dfs.rs index 0a75884e2c..13e7c81e84 100644 --- a/module/move/graphs_tools/src/algo/dfs.rs +++ b/module/move/graphs_tools/src/algo/dfs.rs @@ -1,4 +1,4 @@ -/// Internal namespace. +/// Define a private namespace for all its items. mod private { use crate::prelude::*; diff --git a/module/move/graphs_tools/src/canonical/edge.rs b/module/move/graphs_tools/src/canonical/edge.rs index 3bf782aaee..4d02b207d4 100644 --- a/module/move/graphs_tools/src/canonical/edge.rs +++ b/module/move/graphs_tools/src/canonical/edge.rs @@ -1,4 +1,4 @@ -/// Internal namespace. +/// Define a private namespace for all its items. mod private { use crate::prelude::*; diff --git a/module/move/graphs_tools/src/canonical/factory_generative.rs b/module/move/graphs_tools/src/canonical/factory_generative.rs index ba735895c4..1e2e838081 100644 --- a/module/move/graphs_tools/src/canonical/factory_generative.rs +++ b/module/move/graphs_tools/src/canonical/factory_generative.rs @@ -1,4 +1,4 @@ -/// Internal namespace. +/// Define a private namespace for all its items. mod private { use crate::prelude::*; diff --git a/module/move/graphs_tools/src/canonical/factory_readable.rs b/module/move/graphs_tools/src/canonical/factory_readable.rs index 9ec9bf6012..d545d38d89 100644 --- a/module/move/graphs_tools/src/canonical/factory_readable.rs +++ b/module/move/graphs_tools/src/canonical/factory_readable.rs @@ -1,4 +1,4 @@ -/// Internal namespace. +/// Define a private namespace for all its items. mod private { use crate::prelude::*; diff --git a/module/move/graphs_tools/src/canonical/identity.rs b/module/move/graphs_tools/src/canonical/identity.rs index 90b53e8879..f4c1a38eba 100644 --- a/module/move/graphs_tools/src/canonical/identity.rs +++ b/module/move/graphs_tools/src/canonical/identity.rs @@ -1,4 +1,4 @@ -/// Internal namespace. +/// Define a private namespace for all its items. mod private { use crate::prelude::*; diff --git a/module/move/graphs_tools/src/canonical/node.rs b/module/move/graphs_tools/src/canonical/node.rs index 94d7f7d313..ce0aa547bd 100644 --- a/module/move/graphs_tools/src/canonical/node.rs +++ b/module/move/graphs_tools/src/canonical/node.rs @@ -1,4 +1,4 @@ -/// Internal namespace. +/// Define a private namespace for all its items. mod private { use crate::prelude::*; diff --git a/module/move/plot_interface/src/plot/abs/change.rs b/module/move/plot_interface/src/plot/abs/change.rs index b6ba9fc235..fc14b77ec9 100644 --- a/module/move/plot_interface/src/plot/abs/change.rs +++ b/module/move/plot_interface/src/plot/abs/change.rs @@ -1,4 +1,4 @@ -/// Internal namespace. +/// Define a private namespace for all its items. mod private { use crate::own::*; diff --git a/module/move/plot_interface/src/plot/abs/changer.rs b/module/move/plot_interface/src/plot/abs/changer.rs index 99e39449e0..9e09820670 100644 --- a/module/move/plot_interface/src/plot/abs/changer.rs +++ b/module/move/plot_interface/src/plot/abs/changer.rs @@ -1,4 +1,4 @@ -/// Internal namespace. +/// Define a private namespace for all its items. mod private { use crate::own::*; diff --git a/module/move/plot_interface/src/plot/abs/context.rs b/module/move/plot_interface/src/plot/abs/context.rs index 526b5bf488..c9f844e802 100644 --- a/module/move/plot_interface/src/plot/abs/context.rs +++ b/module/move/plot_interface/src/plot/abs/context.rs @@ -1,4 +1,4 @@ -/// Internal namespace. +/// Define a private namespace for all its items. mod private { use crate::own::*; diff --git a/module/move/plot_interface/src/plot/abs/identity.rs b/module/move/plot_interface/src/plot/abs/identity.rs index d8be2ccff7..1fe2b0e613 100644 --- a/module/move/plot_interface/src/plot/abs/identity.rs +++ b/module/move/plot_interface/src/plot/abs/identity.rs @@ -1,4 +1,4 @@ -/// Internal namespace. +/// Define a private namespace for all its items. mod private { use crate::own::*; diff --git a/module/move/plot_interface/src/plot/abs/registry.rs b/module/move/plot_interface/src/plot/abs/registry.rs index b6d662b429..21a2cd6be7 100644 --- a/module/move/plot_interface/src/plot/abs/registry.rs +++ b/module/move/plot_interface/src/plot/abs/registry.rs @@ -1,4 +1,4 @@ -/// Internal namespace. +/// Define a private namespace for all its items. mod private { use crate::own::*; diff --git a/module/move/plot_interface/src/plot/color.rs b/module/move/plot_interface/src/plot/color.rs index b14a3e268e..fc2b94c17f 100644 --- a/module/move/plot_interface/src/plot/color.rs +++ b/module/move/plot_interface/src/plot/color.rs @@ -1,4 +1,4 @@ -/// Internal namespace. +/// Define a private namespace for all its items. mod private { use crate::own::*; diff --git a/module/move/plot_interface/src/plot/sys/context.rs b/module/move/plot_interface/src/plot/sys/context.rs index e5c23e71f6..ee2f95fbf3 100644 --- a/module/move/plot_interface/src/plot/sys/context.rs +++ b/module/move/plot_interface/src/plot/sys/context.rs @@ -1,4 +1,4 @@ -/// Internal namespace. +/// Define a private namespace for all its items. mod private { use crate::own::*; diff --git a/module/move/plot_interface/src/plot/sys/context_changer.rs b/module/move/plot_interface/src/plot/sys/context_changer.rs index 2f87310469..fa33094931 100644 --- a/module/move/plot_interface/src/plot/sys/context_changer.rs +++ b/module/move/plot_interface/src/plot/sys/context_changer.rs @@ -1,4 +1,4 @@ -/// Internal namespace. +/// Define a private namespace for all its items. mod private { use crate::own::*; diff --git a/module/move/plot_interface/src/plot/sys/drawing.rs b/module/move/plot_interface/src/plot/sys/drawing.rs index 7fdca77c2d..1ec732286b 100644 --- a/module/move/plot_interface/src/plot/sys/drawing.rs +++ b/module/move/plot_interface/src/plot/sys/drawing.rs @@ -1,4 +1,4 @@ -/// Internal namespace. +/// Define a private namespace for all its items. mod private { use crate::own::*; diff --git a/module/move/plot_interface/src/plot/sys/drawing/change_new.rs b/module/move/plot_interface/src/plot/sys/drawing/change_new.rs index 914678a907..4661f9587b 100644 --- a/module/move/plot_interface/src/plot/sys/drawing/change_new.rs +++ b/module/move/plot_interface/src/plot/sys/drawing/change_new.rs @@ -1,4 +1,4 @@ -/// Internal namespace. +/// Define a private namespace for all its items. mod private { use crate::own::*; diff --git a/module/move/plot_interface/src/plot/sys/drawing/changer.rs b/module/move/plot_interface/src/plot/sys/drawing/changer.rs index bfe7cf170f..7fd62e8e44 100644 --- a/module/move/plot_interface/src/plot/sys/drawing/changer.rs +++ b/module/move/plot_interface/src/plot/sys/drawing/changer.rs @@ -1,4 +1,4 @@ -/// Internal namespace. +/// Define a private namespace for all its items. mod private { use crate::own::*; diff --git a/module/move/plot_interface/src/plot/sys/drawing/command.rs b/module/move/plot_interface/src/plot/sys/drawing/command.rs index f98cedfd22..998272ee16 100644 --- a/module/move/plot_interface/src/plot/sys/drawing/command.rs +++ b/module/move/plot_interface/src/plot/sys/drawing/command.rs @@ -1,4 +1,4 @@ -/// Internal namespace. +/// Define a private namespace for all its items. mod private { // use crate::own::*; diff --git a/module/move/plot_interface/src/plot/sys/drawing/queue.rs b/module/move/plot_interface/src/plot/sys/drawing/queue.rs index c68de594ba..c3148011bb 100644 --- a/module/move/plot_interface/src/plot/sys/drawing/queue.rs +++ b/module/move/plot_interface/src/plot/sys/drawing/queue.rs @@ -1,4 +1,4 @@ -/// Internal namespace. +/// Define a private namespace for all its items. mod private { // use crate::own::*; diff --git a/module/move/plot_interface/src/plot/sys/drawing/rect_change_new.rs b/module/move/plot_interface/src/plot/sys/drawing/rect_change_new.rs index 7b1a3acfc7..57fe8b5898 100644 --- a/module/move/plot_interface/src/plot/sys/drawing/rect_change_new.rs +++ b/module/move/plot_interface/src/plot/sys/drawing/rect_change_new.rs @@ -1,4 +1,4 @@ -/// Internal namespace. +/// Define a private namespace for all its items. mod private { use crate::own::*; diff --git a/module/move/plot_interface/src/plot/sys/drawing/rect_change_region.rs b/module/move/plot_interface/src/plot/sys/drawing/rect_change_region.rs index bdbb18321d..84c1634301 100644 --- a/module/move/plot_interface/src/plot/sys/drawing/rect_change_region.rs +++ b/module/move/plot_interface/src/plot/sys/drawing/rect_change_region.rs @@ -1,4 +1,4 @@ -/// Internal namespace. +/// Define a private namespace for all its items. mod private { use crate::own::*; diff --git a/module/move/plot_interface/src/plot/sys/drawing/rect_changer.rs b/module/move/plot_interface/src/plot/sys/drawing/rect_changer.rs index 85d56d9b48..cb5ddf757f 100644 --- a/module/move/plot_interface/src/plot/sys/drawing/rect_changer.rs +++ b/module/move/plot_interface/src/plot/sys/drawing/rect_changer.rs @@ -1,4 +1,4 @@ -/// Internal namespace. +/// Define a private namespace for all its items. mod private { use crate::own::*; diff --git a/module/move/plot_interface/src/plot/sys/stroke_brush.rs b/module/move/plot_interface/src/plot/sys/stroke_brush.rs index 08c73b350b..edfbfc4878 100644 --- a/module/move/plot_interface/src/plot/sys/stroke_brush.rs +++ b/module/move/plot_interface/src/plot/sys/stroke_brush.rs @@ -1,4 +1,4 @@ -/// Internal namespace. +/// Define a private namespace for all its items. mod private { use crate::own::*; diff --git a/module/move/plot_interface/src/plot/sys/stroke_brush/change_color.rs b/module/move/plot_interface/src/plot/sys/stroke_brush/change_color.rs index ae615f89a4..76bd951613 100644 --- a/module/move/plot_interface/src/plot/sys/stroke_brush/change_color.rs +++ b/module/move/plot_interface/src/plot/sys/stroke_brush/change_color.rs @@ -1,4 +1,4 @@ -/// Internal namespace. +/// Define a private namespace for all its items. mod private { use crate::own::*; diff --git a/module/move/plot_interface/src/plot/sys/stroke_brush/change_new.rs b/module/move/plot_interface/src/plot/sys/stroke_brush/change_new.rs index d147f3241b..caa1c2f75c 100644 --- a/module/move/plot_interface/src/plot/sys/stroke_brush/change_new.rs +++ b/module/move/plot_interface/src/plot/sys/stroke_brush/change_new.rs @@ -1,4 +1,4 @@ -/// Internal namespace. +/// Define a private namespace for all its items. mod private { use crate::own::*; diff --git a/module/move/plot_interface/src/plot/sys/stroke_brush/change_width.rs b/module/move/plot_interface/src/plot/sys/stroke_brush/change_width.rs index 192b42e8ad..758fbe75a7 100644 --- a/module/move/plot_interface/src/plot/sys/stroke_brush/change_width.rs +++ b/module/move/plot_interface/src/plot/sys/stroke_brush/change_width.rs @@ -1,4 +1,4 @@ -/// Internal namespace. +/// Define a private namespace for all its items. mod private { use crate::own::*; diff --git a/module/move/plot_interface/src/plot/sys/stroke_brush/changer.rs b/module/move/plot_interface/src/plot/sys/stroke_brush/changer.rs index c6f8ab0f5f..d6208455a0 100644 --- a/module/move/plot_interface/src/plot/sys/stroke_brush/changer.rs +++ b/module/move/plot_interface/src/plot/sys/stroke_brush/changer.rs @@ -1,4 +1,4 @@ -/// Internal namespace. +/// Define a private namespace for all its items. mod private { use crate::own::*; diff --git a/module/move/plot_interface/src/plot/sys/target.rs b/module/move/plot_interface/src/plot/sys/target.rs index 96f38bfe51..820f3a3b97 100644 --- a/module/move/plot_interface/src/plot/sys/target.rs +++ b/module/move/plot_interface/src/plot/sys/target.rs @@ -1,4 +1,4 @@ -/// Internal namespace. +/// Define a private namespace for all its items. mod private { // use crate::prelude::*; diff --git a/module/move/sqlx_query/src/lib.rs b/module/move/sqlx_query/src/lib.rs index b0855a6219..53d4a4043e 100644 --- a/module/move/sqlx_query/src/lib.rs +++ b/module/move/sqlx_query/src/lib.rs @@ -17,7 +17,7 @@ #![ doc = include_str!( concat!( env!( "CARGO_MANIFEST_DIR" ), "/../../../", "Readme.md" ) ) ] -/// Internal namespace. +/// Define a private namespace for all its items. #[ cfg( feature = "enabled" ) ] mod private { diff --git a/module/move/willbe/src/action/list.rs b/module/move/willbe/src/action/list.rs index 074189cd7f..040c8ba12c 100644 --- a/module/move/willbe/src/action/list.rs +++ b/module/move/willbe/src/action/list.rs @@ -1,4 +1,4 @@ -/// Internal namespace. +/// Define a private namespace for all its items. mod private { use crate::*; diff --git a/module/move/willbe/src/action/publish.rs b/module/move/willbe/src/action/publish.rs index 048220bc20..6b8f4dc657 100644 --- a/module/move/willbe/src/action/publish.rs +++ b/module/move/willbe/src/action/publish.rs @@ -1,4 +1,4 @@ -/// Internal namespace. +/// Define a private namespace for all its items. mod private { use crate::*; diff --git a/module/move/willbe/src/action/publish_diff.rs b/module/move/willbe/src/action/publish_diff.rs index 500a73a7f8..a6b76e6528 100644 --- a/module/move/willbe/src/action/publish_diff.rs +++ b/module/move/willbe/src/action/publish_diff.rs @@ -1,4 +1,4 @@ -/// Internal namespace. +/// Define a private namespace for all its items. mod private { use crate::*; diff --git a/module/move/willbe/src/action/test.rs b/module/move/willbe/src/action/test.rs index be0b90405c..8efcad2f5f 100644 --- a/module/move/willbe/src/action/test.rs +++ b/module/move/willbe/src/action/test.rs @@ -1,4 +1,4 @@ -/// Internal namespace. +/// Define a private namespace for all its items. mod private { use crate::*; diff --git a/module/move/willbe/src/command/list.rs b/module/move/willbe/src/command/list.rs index 83e252fc65..98e8b2d2be 100644 --- a/module/move/willbe/src/command/list.rs +++ b/module/move/willbe/src/command/list.rs @@ -1,4 +1,4 @@ -/// Internal namespace. +/// Define a private namespace for all its items. mod private { use crate::*; diff --git a/module/move/willbe/src/command/mod.rs b/module/move/willbe/src/command/mod.rs index 5c81dcf47b..d54c8f2df5 100644 --- a/module/move/willbe/src/command/mod.rs +++ b/module/move/willbe/src/command/mod.rs @@ -1,4 +1,4 @@ -/// Internal namespace. +/// Define a private namespace for all its items. mod private { use crate::*; diff --git a/module/move/willbe/src/command/publish.rs b/module/move/willbe/src/command/publish.rs index b9ca441ef5..3af3fad60d 100644 --- a/module/move/willbe/src/command/publish.rs +++ b/module/move/willbe/src/command/publish.rs @@ -1,4 +1,4 @@ -/// Internal namespace. +/// Define a private namespace for all its items. mod private { use crate::*; diff --git a/module/move/willbe/src/command/test.rs b/module/move/willbe/src/command/test.rs index 9a05c92c89..b56c84a79f 100644 --- a/module/move/willbe/src/command/test.rs +++ b/module/move/willbe/src/command/test.rs @@ -1,4 +1,4 @@ -/// Internal namespace. +/// Define a private namespace for all its items. mod private { use crate::*; diff --git a/module/move/willbe/src/entity/files.rs b/module/move/willbe/src/entity/files.rs index 8385e87167..d8941dfa27 100644 --- a/module/move/willbe/src/entity/files.rs +++ b/module/move/willbe/src/entity/files.rs @@ -1,4 +1,4 @@ -/// Internal namespace. +/// Define a private namespace for all its items. mod private { use crate::*; diff --git a/module/move/willbe/src/entity/manifest.rs b/module/move/willbe/src/entity/manifest.rs index 4df6ead08d..5a0f7a58aa 100644 --- a/module/move/willbe/src/entity/manifest.rs +++ b/module/move/willbe/src/entity/manifest.rs @@ -1,4 +1,4 @@ -/// Internal namespace. +/// Define a private namespace for all its items. mod private { use crate::*; diff --git a/module/move/willbe/src/entity/package_md_extension.rs b/module/move/willbe/src/entity/package_md_extension.rs index 76ffdbac88..29fe95b645 100644 --- a/module/move/willbe/src/entity/package_md_extension.rs +++ b/module/move/willbe/src/entity/package_md_extension.rs @@ -1,4 +1,4 @@ -/// Internal namespace. +/// Define a private namespace for all its items. mod private { use crate::*; diff --git a/module/move/willbe/src/entity/version.rs b/module/move/willbe/src/entity/version.rs index 0722b8a59c..7018f00481 100644 --- a/module/move/willbe/src/entity/version.rs +++ b/module/move/willbe/src/entity/version.rs @@ -1,4 +1,4 @@ -/// Internal namespace. +/// Define a private namespace for all its items. mod private { use crate::*; diff --git a/module/move/willbe/src/entity/workspace_md_extension.rs b/module/move/willbe/src/entity/workspace_md_extension.rs index f463d4cf60..a6d10dd9cb 100644 --- a/module/move/willbe/src/entity/workspace_md_extension.rs +++ b/module/move/willbe/src/entity/workspace_md_extension.rs @@ -1,4 +1,4 @@ -/// Internal namespace. +/// Define a private namespace for all its items. mod private { use crate::*; diff --git a/module/move/willbe/src/lib.rs b/module/move/willbe/src/lib.rs index 87149b74fa..27cf2d036a 100644 --- a/module/move/willbe/src/lib.rs +++ b/module/move/willbe/src/lib.rs @@ -5,7 +5,7 @@ pub use mod_interface::mod_interface; -/// Internal namespace. +/// Define a private namespace for all its items. mod private { use crate::*; diff --git a/module/move/willbe/src/tool/cargo.rs b/module/move/willbe/src/tool/cargo.rs index 780defb921..5a3974ddd9 100644 --- a/module/move/willbe/src/tool/cargo.rs +++ b/module/move/willbe/src/tool/cargo.rs @@ -1,4 +1,4 @@ -/// Internal namespace. +/// Define a private namespace for all its items. mod private { use crate::*; diff --git a/module/move/willbe/src/tool/files.rs b/module/move/willbe/src/tool/files.rs index 38878c477d..95f19de887 100644 --- a/module/move/willbe/src/tool/files.rs +++ b/module/move/willbe/src/tool/files.rs @@ -1,5 +1,5 @@ -/// Internal namespace. +/// Define a private namespace for all its items. mod private { #[ allow( unused_imports ) ] diff --git a/module/move/willbe/src/tool/git.rs b/module/move/willbe/src/tool/git.rs index 05b582cd8a..9e240c804b 100644 --- a/module/move/willbe/src/tool/git.rs +++ b/module/move/willbe/src/tool/git.rs @@ -1,4 +1,4 @@ -/// Internal namespace. +/// Define a private namespace for all its items. mod private { #[ allow( unused_imports ) ] diff --git a/module/move/willbe/src/tool/graph.rs b/module/move/willbe/src/tool/graph.rs index 296547ac82..dec62fdc9e 100644 --- a/module/move/willbe/src/tool/graph.rs +++ b/module/move/willbe/src/tool/graph.rs @@ -1,4 +1,4 @@ -/// Internal namespace. +/// Define a private namespace for all its items. mod private { #[ allow( unused_imports ) ] diff --git a/module/move/willbe/src/tool/http.rs b/module/move/willbe/src/tool/http.rs index d682a79d69..81e13a2d74 100644 --- a/module/move/willbe/src/tool/http.rs +++ b/module/move/willbe/src/tool/http.rs @@ -1,4 +1,4 @@ -/// Internal namespace. +/// Define a private namespace for all its items. mod private { #[ allow( unused_imports ) ] diff --git a/module/move/willbe/src/tool/iter.rs b/module/move/willbe/src/tool/iter.rs index a7b82abd7a..855c492006 100644 --- a/module/move/willbe/src/tool/iter.rs +++ b/module/move/willbe/src/tool/iter.rs @@ -1,4 +1,4 @@ -/// Internal namespace. +/// Define a private namespace for all its items. mod private { } diff --git a/module/move/willbe/src/tool/macros.rs b/module/move/willbe/src/tool/macros.rs index 81861cb3de..96f7a3b06d 100644 --- a/module/move/willbe/src/tool/macros.rs +++ b/module/move/willbe/src/tool/macros.rs @@ -1,4 +1,4 @@ -/// Internal namespace. +/// Define a private namespace for all its items. mod private { } diff --git a/module/move/willbe/src/tool/path.rs b/module/move/willbe/src/tool/path.rs index 028bbd4189..611a9c21ed 100644 --- a/module/move/willbe/src/tool/path.rs +++ b/module/move/willbe/src/tool/path.rs @@ -1,4 +1,4 @@ -/// Internal namespace. +/// Define a private namespace for all its items. mod private { } diff --git a/module/move/willbe/src/tool/query.rs b/module/move/willbe/src/tool/query.rs index 3528d887ae..4b8a14d10e 100644 --- a/module/move/willbe/src/tool/query.rs +++ b/module/move/willbe/src/tool/query.rs @@ -1,4 +1,4 @@ -/// Internal namespace. +/// Define a private namespace for all its items. mod private { #[ allow( unused_imports ) ] diff --git a/module/move/willbe/src/tool/repository.rs b/module/move/willbe/src/tool/repository.rs index 66474d906d..c78d304661 100644 --- a/module/move/willbe/src/tool/repository.rs +++ b/module/move/willbe/src/tool/repository.rs @@ -1,4 +1,4 @@ -/// Internal namespace. +/// Define a private namespace for all its items. mod private { #[ allow( unused_imports ) ] diff --git a/module/move/willbe/src/tool/template.rs b/module/move/willbe/src/tool/template.rs index bd90b0b5d7..59040dca01 100644 --- a/module/move/willbe/src/tool/template.rs +++ b/module/move/willbe/src/tool/template.rs @@ -1,4 +1,4 @@ -/// Internal namespace. +/// Define a private namespace for all its items. mod private { #[ allow( unused_imports ) ] @@ -29,7 +29,7 @@ mod private /// The values associated with the template. pub values : TemplateValues, /// Path to the parameter storage for recovering values - /// for already generated templated files. + /// for already generated templated files. pub parameter_storage : &'static Path, /// Name of the template to generate pub template_name : &'static str, diff --git a/module/move/willbe/src/tool/url.rs b/module/move/willbe/src/tool/url.rs index f1ab5b8f9c..58c2792e4c 100644 --- a/module/move/willbe/src/tool/url.rs +++ b/module/move/willbe/src/tool/url.rs @@ -1,4 +1,4 @@ -/// Internal namespace. +/// Define a private namespace for all its items. mod private { #[ allow( unused_imports ) ] diff --git a/module/move/wplot/src/plot/abs/change.rs b/module/move/wplot/src/plot/abs/change.rs index 660a54e108..ad2a0219a2 100644 --- a/module/move/wplot/src/plot/abs/change.rs +++ b/module/move/wplot/src/plot/abs/change.rs @@ -1,4 +1,4 @@ -/// Internal namespace. +/// Define a private namespace for all its items. mod private { // use crate::own::*; diff --git a/module/move/wplot/src/plot/abs/changer.rs b/module/move/wplot/src/plot/abs/changer.rs index 3315e82b38..9c91ad95c2 100644 --- a/module/move/wplot/src/plot/abs/changer.rs +++ b/module/move/wplot/src/plot/abs/changer.rs @@ -1,4 +1,4 @@ -/// Internal namespace. +/// Define a private namespace for all its items. mod private { // use crate::own::*; diff --git a/module/move/wplot/src/plot/abs/context.rs b/module/move/wplot/src/plot/abs/context.rs index c666e3edca..a27efc6748 100644 --- a/module/move/wplot/src/plot/abs/context.rs +++ b/module/move/wplot/src/plot/abs/context.rs @@ -1,4 +1,4 @@ -/// Internal namespace. +/// Define a private namespace for all its items. #[ cfg( not( feature = "no_std" ) ) ] mod private { diff --git a/module/move/wplot/src/plot/abs/identity.rs b/module/move/wplot/src/plot/abs/identity.rs index 48d9c0426a..9abecc7727 100644 --- a/module/move/wplot/src/plot/abs/identity.rs +++ b/module/move/wplot/src/plot/abs/identity.rs @@ -1,4 +1,4 @@ -/// Internal namespace. +/// Define a private namespace for all its items. #[ cfg( not( feature = "no_std" ) ) ] mod private { diff --git a/module/move/wplot/src/plot/abs/registry.rs b/module/move/wplot/src/plot/abs/registry.rs index 026c0f5c20..c69f775dca 100644 --- a/module/move/wplot/src/plot/abs/registry.rs +++ b/module/move/wplot/src/plot/abs/registry.rs @@ -1,4 +1,4 @@ -/// Internal namespace. +/// Define a private namespace for all its items. #[ cfg( not( feature = "no_std" ) ) ] mod private { diff --git a/module/move/wplot/src/plot/color.rs b/module/move/wplot/src/plot/color.rs index 5cf94b95c8..3ae327c824 100644 --- a/module/move/wplot/src/plot/color.rs +++ b/module/move/wplot/src/plot/color.rs @@ -1,4 +1,4 @@ -/// Internal namespace. +/// Define a private namespace for all its items. mod private { // use crate::own::*; diff --git a/module/move/wplot/src/plot/sys/context.rs b/module/move/wplot/src/plot/sys/context.rs index a59ae6343d..19bd3ce2a9 100644 --- a/module/move/wplot/src/plot/sys/context.rs +++ b/module/move/wplot/src/plot/sys/context.rs @@ -1,4 +1,4 @@ -/// Internal namespace. +/// Define a private namespace for all its items. mod private { use crate::abs::registry::private::Registry; diff --git a/module/move/wplot/src/plot/sys/context_changer.rs b/module/move/wplot/src/plot/sys/context_changer.rs index e6a91ca8e5..c0f1df3442 100644 --- a/module/move/wplot/src/plot/sys/context_changer.rs +++ b/module/move/wplot/src/plot/sys/context_changer.rs @@ -1,4 +1,4 @@ -/// Internal namespace. +/// Define a private namespace for all its items. mod private { // use crate::own::*; diff --git a/module/move/wplot/src/plot/sys/drawing.rs b/module/move/wplot/src/plot/sys/drawing.rs index 673fd1fa74..9e668966be 100644 --- a/module/move/wplot/src/plot/sys/drawing.rs +++ b/module/move/wplot/src/plot/sys/drawing.rs @@ -1,6 +1,6 @@ pub(crate) mod changer; -/// Internal namespace. +/// Define a private namespace for all its items. mod private { // use crate::own::*; diff --git a/module/move/wplot/src/plot/sys/drawing/change_new.rs b/module/move/wplot/src/plot/sys/drawing/change_new.rs index ab075de7fa..f7628c2566 100644 --- a/module/move/wplot/src/plot/sys/drawing/change_new.rs +++ b/module/move/wplot/src/plot/sys/drawing/change_new.rs @@ -1,4 +1,4 @@ -/// Internal namespace. +/// Define a private namespace for all its items. mod private { // use crate::own::*; diff --git a/module/move/wplot/src/plot/sys/drawing/changer.rs b/module/move/wplot/src/plot/sys/drawing/changer.rs index a7ba4c1b67..84c69db2c3 100644 --- a/module/move/wplot/src/plot/sys/drawing/changer.rs +++ b/module/move/wplot/src/plot/sys/drawing/changer.rs @@ -1,4 +1,4 @@ -/// Internal namespace. +/// Define a private namespace for all its items. mod private { // use crate::own::*; diff --git a/module/move/wplot/src/plot/sys/drawing/command.rs b/module/move/wplot/src/plot/sys/drawing/command.rs index f98cedfd22..998272ee16 100644 --- a/module/move/wplot/src/plot/sys/drawing/command.rs +++ b/module/move/wplot/src/plot/sys/drawing/command.rs @@ -1,4 +1,4 @@ -/// Internal namespace. +/// Define a private namespace for all its items. mod private { // use crate::own::*; diff --git a/module/move/wplot/src/plot/sys/drawing/queue.rs b/module/move/wplot/src/plot/sys/drawing/queue.rs index c68de594ba..c3148011bb 100644 --- a/module/move/wplot/src/plot/sys/drawing/queue.rs +++ b/module/move/wplot/src/plot/sys/drawing/queue.rs @@ -1,4 +1,4 @@ -/// Internal namespace. +/// Define a private namespace for all its items. mod private { // use crate::own::*; diff --git a/module/move/wplot/src/plot/sys/drawing/rect_change_new.rs b/module/move/wplot/src/plot/sys/drawing/rect_change_new.rs index 212ffb82c1..b682c0ead8 100644 --- a/module/move/wplot/src/plot/sys/drawing/rect_change_new.rs +++ b/module/move/wplot/src/plot/sys/drawing/rect_change_new.rs @@ -1,4 +1,4 @@ -/// Internal namespace. +/// Define a private namespace for all its items. mod private { // use crate::own::*; diff --git a/module/move/wplot/src/plot/sys/drawing/rect_change_region.rs b/module/move/wplot/src/plot/sys/drawing/rect_change_region.rs index 463259b6cf..29b6885e63 100644 --- a/module/move/wplot/src/plot/sys/drawing/rect_change_region.rs +++ b/module/move/wplot/src/plot/sys/drawing/rect_change_region.rs @@ -1,4 +1,4 @@ -/// Internal namespace. +/// Define a private namespace for all its items. mod private { use crate::own::*; diff --git a/module/move/wplot/src/plot/sys/drawing/rect_changer.rs b/module/move/wplot/src/plot/sys/drawing/rect_changer.rs index bb25c465aa..7e39fb06fc 100644 --- a/module/move/wplot/src/plot/sys/drawing/rect_changer.rs +++ b/module/move/wplot/src/plot/sys/drawing/rect_changer.rs @@ -1,4 +1,4 @@ -/// Internal namespace. +/// Define a private namespace for all its items. mod private { use crate::own::*; diff --git a/module/move/wplot/src/plot/sys/stroke_brush.rs b/module/move/wplot/src/plot/sys/stroke_brush.rs index 78ad289dc7..9f52539630 100644 --- a/module/move/wplot/src/plot/sys/stroke_brush.rs +++ b/module/move/wplot/src/plot/sys/stroke_brush.rs @@ -1,7 +1,7 @@ mod change_width; mod change_new; -/// Internal namespace. +/// Define a private namespace for all its items. mod private { use crate::own::*; diff --git a/module/move/wplot/src/plot/sys/stroke_brush/change_color.rs b/module/move/wplot/src/plot/sys/stroke_brush/change_color.rs index ae615f89a4..76bd951613 100644 --- a/module/move/wplot/src/plot/sys/stroke_brush/change_color.rs +++ b/module/move/wplot/src/plot/sys/stroke_brush/change_color.rs @@ -1,4 +1,4 @@ -/// Internal namespace. +/// Define a private namespace for all its items. mod private { use crate::own::*; diff --git a/module/move/wplot/src/plot/sys/stroke_brush/change_new.rs b/module/move/wplot/src/plot/sys/stroke_brush/change_new.rs index 077f20f6ba..4e70ba7ee7 100644 --- a/module/move/wplot/src/plot/sys/stroke_brush/change_new.rs +++ b/module/move/wplot/src/plot/sys/stroke_brush/change_new.rs @@ -1,4 +1,4 @@ -/// Internal namespace. +/// Define a private namespace for all its items. mod private { // use crate::own::*; diff --git a/module/move/wplot/src/plot/sys/stroke_brush/change_width.rs b/module/move/wplot/src/plot/sys/stroke_brush/change_width.rs index cf5a548778..a7fcecdcb8 100644 --- a/module/move/wplot/src/plot/sys/stroke_brush/change_width.rs +++ b/module/move/wplot/src/plot/sys/stroke_brush/change_width.rs @@ -1,4 +1,4 @@ -/// Internal namespace. +/// Define a private namespace for all its items. mod private { // use crate::own::*; diff --git a/module/move/wplot/src/plot/sys/stroke_brush/changer.rs b/module/move/wplot/src/plot/sys/stroke_brush/changer.rs index 407b234fac..152dfebaab 100644 --- a/module/move/wplot/src/plot/sys/stroke_brush/changer.rs +++ b/module/move/wplot/src/plot/sys/stroke_brush/changer.rs @@ -1,4 +1,4 @@ -/// Internal namespace. +/// Define a private namespace for all its items. mod private { use crate::own::*; diff --git a/module/move/wplot/src/plot/sys/target.rs b/module/move/wplot/src/plot/sys/target.rs index 58634c4e36..95d123186b 100644 --- a/module/move/wplot/src/plot/sys/target.rs +++ b/module/move/wplot/src/plot/sys/target.rs @@ -1,4 +1,4 @@ -/// Internal namespace. +/// Define a private namespace for all its items. mod private { // use crate::prelude::*; diff --git a/module/postponed/type_constructor/src/type_constuctor/enumerable.rs b/module/postponed/type_constructor/src/type_constuctor/enumerable.rs index e17553d21c..fdfa45fb97 100644 --- a/module/postponed/type_constructor/src/type_constuctor/enumerable.rs +++ b/module/postponed/type_constructor/src/type_constuctor/enumerable.rs @@ -1,4 +1,4 @@ -/// Internal namespace. +/// Define a private namespace for all its items. mod private { diff --git a/module/postponed/type_constructor/src/type_constuctor/helper.rs b/module/postponed/type_constructor/src/type_constuctor/helper.rs index 57c9986a69..a4dcf9011f 100644 --- a/module/postponed/type_constructor/src/type_constuctor/helper.rs +++ b/module/postponed/type_constructor/src/type_constuctor/helper.rs @@ -1,4 +1,4 @@ -/// Internal namespace. +/// Define a private namespace for all its items. mod private { use crate::exposed::*; diff --git a/module/postponed/type_constructor/src/type_constuctor/make.rs b/module/postponed/type_constructor/src/type_constuctor/make.rs index 807974a4ca..2cdb6d6973 100644 --- a/module/postponed/type_constructor/src/type_constuctor/make.rs +++ b/module/postponed/type_constructor/src/type_constuctor/make.rs @@ -1,4 +1,4 @@ -// /// Internal namespace. +// /// Define a private namespace for all its items. // #[ cfg( feature = "make" ) ] // mod private // { diff --git a/module/postponed/type_constructor/src/type_constuctor/many.rs b/module/postponed/type_constructor/src/type_constuctor/many.rs index 0c81d87180..3ded63125c 100644 --- a/module/postponed/type_constructor/src/type_constuctor/many.rs +++ b/module/postponed/type_constructor/src/type_constuctor/many.rs @@ -1,4 +1,4 @@ -/// Internal namespace. +/// Define a private namespace for all its items. mod private { use crate::exposed::*; diff --git a/module/postponed/type_constructor/src/type_constuctor/no_many.rs b/module/postponed/type_constructor/src/type_constuctor/no_many.rs index a36e9829ef..d810f74d08 100644 --- a/module/postponed/type_constructor/src/type_constuctor/no_many.rs +++ b/module/postponed/type_constructor/src/type_constuctor/no_many.rs @@ -1,4 +1,4 @@ -/// Internal namespace. +/// Define a private namespace for all its items. mod private { diff --git a/module/postponed/type_constructor/src/type_constuctor/pair.rs b/module/postponed/type_constructor/src/type_constuctor/pair.rs index 090428e500..56b71bc2ff 100644 --- a/module/postponed/type_constructor/src/type_constuctor/pair.rs +++ b/module/postponed/type_constructor/src/type_constuctor/pair.rs @@ -1,4 +1,4 @@ -/// Internal namespace. +/// Define a private namespace for all its items. mod private { use crate::exposed::*; diff --git a/module/postponed/type_constructor/src/type_constuctor/single.rs b/module/postponed/type_constructor/src/type_constuctor/single.rs index 7fcf8642f4..2fd3637235 100644 --- a/module/postponed/type_constructor/src/type_constuctor/single.rs +++ b/module/postponed/type_constructor/src/type_constuctor/single.rs @@ -1,4 +1,4 @@ -/// Internal namespace. +/// Define a private namespace for all its items. mod private { use crate::exposed::*; diff --git a/module/postponed/type_constructor/src/type_constuctor/traits.rs b/module/postponed/type_constructor/src/type_constuctor/traits.rs index cd11c438ee..cf4838bee3 100644 --- a/module/postponed/type_constructor/src/type_constuctor/traits.rs +++ b/module/postponed/type_constructor/src/type_constuctor/traits.rs @@ -1,4 +1,4 @@ -/// Internal namespace. +/// Define a private namespace for all its items. mod private { diff --git a/module/postponed/type_constructor/src/type_constuctor/types.rs b/module/postponed/type_constructor/src/type_constuctor/types.rs index 151b33ae42..d9d1de235a 100644 --- a/module/postponed/type_constructor/src/type_constuctor/types.rs +++ b/module/postponed/type_constructor/src/type_constuctor/types.rs @@ -1,4 +1,4 @@ -/// Internal namespace. +/// Define a private namespace for all its items. mod private { use crate::exposed::*; diff --git a/module/postponed/type_constructor/src/type_constuctor/vectorized_from.rs b/module/postponed/type_constructor/src/type_constuctor/vectorized_from.rs index c7e366142a..c145e31404 100644 --- a/module/postponed/type_constructor/src/type_constuctor/vectorized_from.rs +++ b/module/postponed/type_constructor/src/type_constuctor/vectorized_from.rs @@ -1,4 +1,4 @@ -/// Internal namespace. +/// Define a private namespace for all its items. mod private { diff --git a/module/postponed/wautomata/src/graph/abs/edge.rs b/module/postponed/wautomata/src/graph/abs/edge.rs index 550a350efb..214f8f10d9 100644 --- a/module/postponed/wautomata/src/graph/abs/edge.rs +++ b/module/postponed/wautomata/src/graph/abs/edge.rs @@ -1,4 +1,4 @@ -/// Internal namespace. +/// Define a private namespace for all its items. mod private { use crate::prelude::*; diff --git a/module/postponed/wautomata/src/graph/abs/factory.rs b/module/postponed/wautomata/src/graph/abs/factory.rs index 737cbfdf5c..ddf6012168 100644 --- a/module/postponed/wautomata/src/graph/abs/factory.rs +++ b/module/postponed/wautomata/src/graph/abs/factory.rs @@ -1,4 +1,4 @@ -/// Internal namespace. +/// Define a private namespace for all its items. mod private { use crate::prelude::*; diff --git a/module/postponed/wautomata/src/graph/abs/id_generator.rs b/module/postponed/wautomata/src/graph/abs/id_generator.rs index 943315c041..2090439804 100644 --- a/module/postponed/wautomata/src/graph/abs/id_generator.rs +++ b/module/postponed/wautomata/src/graph/abs/id_generator.rs @@ -1,4 +1,4 @@ -/// Internal namespace. +/// Define a private namespace for all its items. mod private { // use crate::prelude::*; diff --git a/module/postponed/wautomata/src/graph/abs/identity.rs b/module/postponed/wautomata/src/graph/abs/identity.rs index c7fdcb3797..1e9c21d2f9 100644 --- a/module/postponed/wautomata/src/graph/abs/identity.rs +++ b/module/postponed/wautomata/src/graph/abs/identity.rs @@ -1,4 +1,4 @@ -/// Internal namespace. +/// Define a private namespace for all its items. mod private { // use crate::prelude::*; diff --git a/module/postponed/wautomata/src/graph/abs/node.rs b/module/postponed/wautomata/src/graph/abs/node.rs index b227581718..703bd0893d 100644 --- a/module/postponed/wautomata/src/graph/abs/node.rs +++ b/module/postponed/wautomata/src/graph/abs/node.rs @@ -1,4 +1,4 @@ -/// Internal namespace. +/// Define a private namespace for all its items. mod private { use crate::prelude::*; diff --git a/module/postponed/wautomata/src/graph/algo/dfs.rs b/module/postponed/wautomata/src/graph/algo/dfs.rs index 0a75884e2c..13e7c81e84 100644 --- a/module/postponed/wautomata/src/graph/algo/dfs.rs +++ b/module/postponed/wautomata/src/graph/algo/dfs.rs @@ -1,4 +1,4 @@ -/// Internal namespace. +/// Define a private namespace for all its items. mod private { use crate::prelude::*; diff --git a/module/postponed/wautomata/src/graph/canonical/edge.rs b/module/postponed/wautomata/src/graph/canonical/edge.rs index 3bf782aaee..4d02b207d4 100644 --- a/module/postponed/wautomata/src/graph/canonical/edge.rs +++ b/module/postponed/wautomata/src/graph/canonical/edge.rs @@ -1,4 +1,4 @@ -/// Internal namespace. +/// Define a private namespace for all its items. mod private { use crate::prelude::*; diff --git a/module/postponed/wautomata/src/graph/canonical/factory_generative.rs b/module/postponed/wautomata/src/graph/canonical/factory_generative.rs index 766002bfb3..0548aa26c5 100644 --- a/module/postponed/wautomata/src/graph/canonical/factory_generative.rs +++ b/module/postponed/wautomata/src/graph/canonical/factory_generative.rs @@ -1,4 +1,4 @@ -/// Internal namespace. +/// Define a private namespace for all its items. mod private { use crate::prelude::*; diff --git a/module/postponed/wautomata/src/graph/canonical/factory_readable.rs b/module/postponed/wautomata/src/graph/canonical/factory_readable.rs index d9505b7819..1cad2804dd 100644 --- a/module/postponed/wautomata/src/graph/canonical/factory_readable.rs +++ b/module/postponed/wautomata/src/graph/canonical/factory_readable.rs @@ -1,4 +1,4 @@ -/// Internal namespace. +/// Define a private namespace for all its items. mod private { use crate::prelude::*; diff --git a/module/postponed/wautomata/src/graph/canonical/identity.rs b/module/postponed/wautomata/src/graph/canonical/identity.rs index 497da5ff54..6680ead861 100644 --- a/module/postponed/wautomata/src/graph/canonical/identity.rs +++ b/module/postponed/wautomata/src/graph/canonical/identity.rs @@ -1,4 +1,4 @@ -/// Internal namespace. +/// Define a private namespace for all its items. mod private { use crate::prelude::*; diff --git a/module/postponed/wautomata/src/graph/canonical/node.rs b/module/postponed/wautomata/src/graph/canonical/node.rs index 94d7f7d313..ce0aa547bd 100644 --- a/module/postponed/wautomata/src/graph/canonical/node.rs +++ b/module/postponed/wautomata/src/graph/canonical/node.rs @@ -1,4 +1,4 @@ -/// Internal namespace. +/// Define a private namespace for all its items. mod private { use crate::prelude::*; diff --git a/module/template/layer/layer.rs b/module/template/layer/layer.rs index 45d766e897..b4b8322d92 100644 --- a/module/template/layer/layer.rs +++ b/module/template/layer/layer.rs @@ -1,4 +1,4 @@ -/// Internal namespace. +/// Define a private namespace for all its items. mod private { use super::super::*; From 857016fd6280db2feb1765b069fbb03a1209cb63 Mon Sep 17 00:00:00 2001 From: ".Barsik" Date: Fri, 8 Nov 2024 11:41:07 +0200 Subject: [PATCH 41/67] READY: (willbe): Add Test for Emulation of Publication Sequence (#1481) willbe test --- module/move/willbe/tests/inc/mod.rs | 2 + module/move/willbe/tests/inc/package.rs | 307 ++++++++++++++++++++++++ 2 files changed, 309 insertions(+) diff --git a/module/move/willbe/tests/inc/mod.rs b/module/move/willbe/tests/inc/mod.rs index ca9dbda05d..0b456a9b87 100644 --- a/module/move/willbe/tests/inc/mod.rs +++ b/module/move/willbe/tests/inc/mod.rs @@ -14,6 +14,8 @@ mod action_tests; mod helper; +mod package; + // aaa : for Petro : for Bohdan : for Nikita : sort out test files to be consistent with src files // sorted diff --git a/module/move/willbe/tests/inc/package.rs b/module/move/willbe/tests/inc/package.rs index 8a5fb2a2f0..bc83b38d89 100644 --- a/module/move/willbe/tests/inc/package.rs +++ b/module/move/willbe/tests/inc/package.rs @@ -1,3 +1,310 @@ +use std::*; +use std::io::Write; + +use crate::the_module::{ action, channel, package }; + +enum Dependency +{ + Normal { name: String, path: Option< path::PathBuf >, is_macro: bool }, + Dev { name: String, path: Option< path::PathBuf >, is_macro: bool }, +} + +impl Dependency +{ + fn as_toml( &self ) -> String + { + match self + { + Dependency::Normal { name, path, is_macro } if !is_macro => + if let Some( path ) = path + { + format!( "[dependencies.{name}]\npath = \"../{}\"", path.display().to_string().replace( "\\", "/" ) ) + } + else + { + format!( "[dependencies.{name}]\nversion = \"*\"" ) + } + Dependency::Normal { name, .. } => format!( "[dependencies.{name}]\nworkspace = true" ), + Dependency::Dev { name, path, is_macro } if !is_macro => + if let Some( path ) = path + { + format!( "[dev-dependencies.{name}]\npath = \"../{}\"", path.display().to_string().replace( "\\", "/" ) ) + } + else + { + format!( "[dev-dependencies.{name}]\nversion = \"*\"" ) + } + Dependency::Dev { name, .. } => format!( "[dev-dependencies.{name}]\nworkspace = true" ), + } + } +} + +struct TestPackage +{ + name: String, + dependencies: Vec< Dependency >, + path: Option< path::PathBuf >, +} + +impl TestPackage +{ + pub fn new( name: impl Into< String > ) -> Self + { + Self { name: name.into(), dependencies: vec![], path: None } + } + + pub fn dependency( mut self, name: impl Into< String > ) -> Self + { + self.dependencies.push( Dependency::Normal { name: name.into(), path: None, is_macro: false } ); + self + } + + pub fn macro_dependency( mut self, name: impl Into< String > ) -> Self + { + self.dependencies.push( Dependency::Normal { name: name.into(), path: None, is_macro: true } ); + self + } + + pub fn dev_dependency( mut self, name: impl Into< String > ) -> Self + { + self.dependencies.push( Dependency::Dev { name: name.into(), path: None, is_macro: false } ); + self + } + + pub fn macro_dev_dependency( mut self, name: impl Into< String > ) -> Self + { + self.dependencies.push( Dependency::Dev { name: name.into(), path: None, is_macro: true } ); + self + } + + pub fn create( &mut self, path: impl AsRef< path::Path > ) -> io::Result< () > + { + let path = path.as_ref().join( &self.name ); + + () = fs::create_dir_all( path.join( "src" ) )?; + () = fs::write( path.join( "src" ).join( "lib.rs" ), &[] )?; + + let cargo = format! + ( + r#"[package] +name = "{}" +version = "0.1.0" +edition = "2021" +{}"#, + self.name, + self.dependencies.iter().map( Dependency::as_toml ).fold( String::new(), | acc, d | + { + format!( "{acc}\n\n{d}" ) + }) + ); + () = fs::write( path.join( "Cargo.toml" ), cargo.as_bytes() )?; + + self.path = Some( path ); + + Ok( () ) + } +} + +impl Drop for TestPackage +{ + fn drop( &mut self ) + { + if let Some( path ) = &self.path + { + _ = fs::remove_dir_all( path ).ok(); + } + } +} + +struct TestWorkspace +{ + packages: Vec< TestPackage >, + path: path::PathBuf, +} + +impl TestWorkspace +{ + fn new( path: impl AsRef< path::Path > ) -> io::Result< Self > + { + let path = path.as_ref(); + () = fs::create_dir_all( path )?; + + let cargo = r#"[workspace] +resolver = "2" +members = [ + "members/*", +] +"#; + () = fs::write( path.join( "Cargo.toml" ), cargo.as_bytes() )?; + + Ok(Self { packages: vec![], path: path.into() }) + } + + fn find( &self, package_name: impl AsRef< str > ) -> Option< &TestPackage > + { + let name = package_name.as_ref(); + self.packages.iter().find( | p | p.name == name ) + } + + fn with_package( mut self, mut package: TestPackage ) -> io::Result< Self > + { + let mut macro_deps = collections::HashMap::new(); + for dep in &mut package.dependencies + { + match dep + { + Dependency::Normal { name, is_macro, .. } if *is_macro => + { + if let Some( package ) = self.find( &name ) + { + if let Some( path ) = &package.path + { + macro_deps.insert( name.clone(), path.clone() ); + continue; + } + } + eprintln!( "macro dependency {} not found. required for {}", name, package.name ); + } + Dependency::Normal { name, path, .. } => + { + if let Some( package ) = self.find( &name ) + { + if let Some( real_path ) = &package.path + { + let real_path = real_path.strip_prefix( self.path.join( "members" ) ).unwrap_or( real_path ); + *path = Some( real_path.into() ); + } + } + } + Dependency::Dev { name, is_macro, .. } if *is_macro => + { + if let Some( package ) = self.find( &name ) + { + if let Some( path ) = &package.path + { + macro_deps.insert( name.clone(), path.clone() ); + continue; + } + } + eprintln!( "macro dev-dependency {} not found. required for {}", name, package.name ); + } + Dependency::Dev { name, path, .. } => + { + if let Some( package ) = self.find( &name ) + { + if let Some( real_path ) = &package.path + { + let real_path = real_path.strip_prefix( self.path.join( "members" ) ).unwrap_or( real_path ); + *path = Some( real_path.into() ); + } + } + } + } + } + let mut cargo = fs::OpenOptions::new().append( true ).open( self.path.join( "Cargo.toml" ) )?; + for ( name, _ ) in macro_deps + { + writeln!( cargo, + r#"[workspace.dependencies.{name}] +version = "*" +path = "members/{name}""#, + )?; + } + package.create( self.path.join( "members" ) )?; + self.packages.push( package ); + + Ok( self ) + } + + fn with_packages( mut self, packages: impl IntoIterator< Item = TestPackage > ) -> io::Result< Self > + { + for package in packages { self = self.with_package( package )?; } + + Ok( self ) + } +} + +impl Drop for TestWorkspace +{ + fn drop( &mut self ) + { + _ = fs::remove_dir_all( &self.path ).ok(); + } +} + +#[ test ] +fn kos_plan() +{ + let tmp_folder = env::temp_dir().join( "publish_plan_kos_plan" ); + _ = fs::remove_dir_all( &tmp_folder ).ok(); + + let workspace = TestWorkspace::new( tmp_folder ).unwrap() + .with_packages( + [ + TestPackage::new( "a" ), + TestPackage::new( "b" ).dependency( "a" ), + TestPackage::new( "c" ).dependency( "a" ), + TestPackage::new( "d" ).dependency( "a" ), + TestPackage::new( "e" ).dependency( "b" ).macro_dev_dependency( "c" ),//.macro_dependency( "c" ), + ]).unwrap(); + let the_patterns: Vec< String > = workspace + .packages + .iter() + .flat_map( | p | p.path.as_ref().map( | p | p.to_string_lossy().into_owned() ) ) + .collect(); + dbg!(&the_patterns); + + let plan = action::publish_plan + ( + the_patterns, + channel::Channel::Stable, + false, + false, + true, + false, + ) + .unwrap(); + + let queue: Vec< &package::PackageName > = plan.plans.iter().map( | i | &i.package_name ).collect(); + dbg!(&queue); + + // We don’t consider dev dependencies when constructing the project graph, which results in this number of variations. + // If you'd like to modify this behavior, please check `entity/workspace_graph.rs` in the `module_dependency_filter`. + let expected_one_of= + [ + [ "a", "b", "d", "c", "e" ], + [ "a", "b", "c", "d", "e" ], + [ "a", "d", "b", "c", "e" ], + [ "a", "c", "b", "d", "e" ], + [ "a", "d", "c", "b", "e" ], + [ "a", "c", "d", "b", "e" ], + [ "a", "b", "d", "e", "c" ], + [ "a", "d", "b", "e", "c" ], + [ "a", "b", "e", "d", "c" ], + [ "a", "e", "b", "d", "c" ], + [ "a", "d", "e", "b", "c" ], + [ "a", "e", "d", "b", "c" ], + [ "a", "b", "c", "e", "d" ], + [ "a", "c", "b", "e", "d" ], + [ "a", "b", "e", "c", "d" ], + [ "a", "e", "b", "c", "d" ], + [ "a", "c", "e", "b", "d" ], + [ "a", "e", "c", "b", "d" ], + ]; + + let mut fail = true; + 'sequences: for sequence in expected_one_of + { + for index in 0 .. 5 + { + if *queue[ index ] != sequence[ index ].to_string().into() { continue 'sequences; } + } + fail = false; + break; + } + assert!( !fail ); +} + // use super::*; // use the_module:: // { From ef632701498efbe294ed35737797196e85b76689 Mon Sep 17 00:00:00 2001 From: Ruslan Date: Fri, 8 Nov 2024 21:39:22 +0200 Subject: [PATCH 42/67] NOT READY : Assistant: OpenAI (#1478) * Initial commit * First implementation commit * Separate CLI into commands and actiions: part of the work * Fix formatting * Make it runnable * Make error message include underlying error * Rename OpenAI subcommand * Update design * Make `Secret` and restore old `main` * Fix warnings and switch to `error_tools` * Fix formatting * Two formatting options * Modules overhaul * Make the program work after modularization * Remove thiserror * Rename commands and actions * Add documentation * Modules overhaul --------- Co-authored-by: Wandalen --- module/move/assistant/.key/readme.md | 20 ++ module/move/assistant/Cargo.toml | 15 ++ module/move/assistant/design/commands.md | 73 ++++++ module/move/assistant/design/entities.mmd | 78 +++++++ module/move/assistant/design/entities.png | Bin 0 -> 558866 bytes module/move/assistant/design/scenarios.md | 32 +++ module/move/assistant/src/actions.rs | 13 ++ module/move/assistant/src/actions/openai.rs | 44 ++++ .../src/actions/openai_assistants_list.rs | 68 ++++++ .../src/actions/openai_files_list.rs | 69 ++++++ .../assistant/src/actions/openai_runs_list.rs | 70 ++++++ .../src/{main.rs => bin/list_resources.rs} | 7 +- module/move/assistant/src/bin/main.rs | 42 ++++ module/move/assistant/src/client.rs | 25 +- module/move/assistant/src/commands.rs | 49 ++++ module/move/assistant/src/commands/openai.rs | 75 ++++++ .../src/commands/openai_assistants.rs | 52 +++++ .../src/commands/openai_assistants_list.rs | 33 +++ .../assistant/src/commands/openai_files.rs | 52 +++++ .../src/commands/openai_files_list.rs | 33 +++ .../assistant/src/commands/openai_runs.rs | 55 +++++ .../src/commands/openai_runs_list.rs | 34 +++ module/move/assistant/src/debug.rs | 2 + .../assistant/src/debug/assistant_object.rs | 1 + module/move/assistant/src/debug/file_data.rs | 1 + module/move/assistant/src/debug/run_object.rs | 73 ++++++ module/move/assistant/src/lib.rs | 15 ++ module/move/assistant/src/secret.rs | 219 ++++++++++++++++++ 28 files changed, 1229 insertions(+), 21 deletions(-) create mode 100644 module/move/assistant/.key/readme.md create mode 100644 module/move/assistant/design/commands.md create mode 100644 module/move/assistant/design/entities.mmd create mode 100644 module/move/assistant/design/entities.png create mode 100644 module/move/assistant/design/scenarios.md create mode 100644 module/move/assistant/src/actions.rs create mode 100644 module/move/assistant/src/actions/openai.rs create mode 100644 module/move/assistant/src/actions/openai_assistants_list.rs create mode 100644 module/move/assistant/src/actions/openai_files_list.rs create mode 100644 module/move/assistant/src/actions/openai_runs_list.rs rename module/move/assistant/src/{main.rs => bin/list_resources.rs} (94%) create mode 100644 module/move/assistant/src/bin/main.rs create mode 100644 module/move/assistant/src/commands.rs create mode 100644 module/move/assistant/src/commands/openai.rs create mode 100644 module/move/assistant/src/commands/openai_assistants.rs create mode 100644 module/move/assistant/src/commands/openai_assistants_list.rs create mode 100644 module/move/assistant/src/commands/openai_files.rs create mode 100644 module/move/assistant/src/commands/openai_files_list.rs create mode 100644 module/move/assistant/src/commands/openai_runs.rs create mode 100644 module/move/assistant/src/commands/openai_runs_list.rs create mode 100644 module/move/assistant/src/debug/run_object.rs create mode 100644 module/move/assistant/src/secret.rs diff --git a/module/move/assistant/.key/readme.md b/module/move/assistant/.key/readme.md new file mode 100644 index 0000000000..4209c24678 --- /dev/null +++ b/module/move/assistant/.key/readme.md @@ -0,0 +1,20 @@ +# Keys + +This document provides a concise example of an environment configuration script, used to set up environment variables for a project. These variables configure application behavior without altering the code. + +## Example of `.key/-env.sh` + +```bash +# OpenAI API key. +OPENAI_API_KEY=sk-proj-ABCDEFG +``` + +## How to Use in Shell + +To apply these variables to your current shell session, use: + +```bash +. ./key/-env.sh +``` + +This command sources the script, making the variables available in your current session. Ensure `-env.sh` is in the `key` directory relative to your current location. \ No newline at end of file diff --git a/module/move/assistant/Cargo.toml b/module/move/assistant/Cargo.toml index 144cfb6557..479871b6f7 100644 --- a/module/move/assistant/Cargo.toml +++ b/module/move/assistant/Cargo.toml @@ -15,6 +15,7 @@ Assist AI in writing code. """ categories = [ "algorithms", "development-tools" ] keywords = [ "fundamental", "general-purpose" ] +default-run = "main" [lints] workspace = true @@ -32,6 +33,14 @@ enabled = [ "reflect_tools/enabled", ] +[[bin]] +name = "main" +path = "src/bin/main.rs" + +[[bin]] +name = "list_resources" +path = "src/bin/list_resources.rs" + [dependencies] # xxx : qqq : optimze features mod_interface = { workspace = true, features = [ "full" ] } @@ -41,6 +50,12 @@ reflect_tools = { workspace = true, features = [ "full" ] } openai-api-rs = { version = "=5.0.11" } tokio = { version = "1", features = ["full"] } dotenv = "0.15" +clap = { version = "4.5.20", features = ["derive"] } +pth = "0.21.0" +serde = { version = "1.0.213", features = ["derive"] } +serde_with = "3.11.0" +error_tools = "0.17.0" +derive_tools = { version = "0.32.0", features = ["full"] } [dev-dependencies] test_tools = { workspace = true } diff --git a/module/move/assistant/design/commands.md b/module/move/assistant/design/commands.md new file mode 100644 index 0000000000..d1c0410dba --- /dev/null +++ b/module/move/assistant/design/commands.md @@ -0,0 +1,73 @@ +# Commands + +## Legend + +- `<...>` - argument. +- `<..?>` - optional argument. +- `<...=...>` - argument with default value. +- `(...)+` - one or more times. + +## OpenAI + +### Files + +```shell +assistant openai files upload +assistant openai files list +assistant openai files retrieve +assistant openai files delete +assistant openai files retrieve-content +``` + +### Assistants + +```shell +assistant openai assistants create +assistant openai assistants list +assistant openai assistants retrieve +assistant openai assistants modify +assistant openai assistants delete +``` + +### Threads + +```shell +assistant openai threads create +assistant openai threads retrieve +assistant openai threads delete +``` + +### Messages + +```shell +assistant openai messages create +assistant openai messages list +assistant openai messages retrieve +assistant openai messages modify +assistant openai messages delete +``` + +### Chat + +```shell +assistant openai chat create-completion ( )+ +``` + +### Runs + +```shell +assistant openai runs create +assistant openai runs create-with-thread +assistant openai runs list +assistant openai runs retrieve +assistant openai runs cancel +``` + +## Anthropic + +### Messages + +```shell +assistant anthropic messages create ( )+ +``` + diff --git a/module/move/assistant/design/entities.mmd b/module/move/assistant/design/entities.mmd new file mode 100644 index 0000000000..53e9e6baa5 --- /dev/null +++ b/module/move/assistant/design/entities.mmd @@ -0,0 +1,78 @@ +--- +title: OpenAI API +--- +erDiagram + File { + string id PK + string object + integer bytes + integer created_at + string file_name + string purpose + } + + Assistant { + string id PK + string object + string model + integer created_at + string name + string description + string instructions + tool[] tools + metadata metadata + headers headers + } + + Thread { + string id PK + string object + integer created_at + object tool_resources + metadata metadata + } + + Message { + string id PK + string object + integer created_at + string thread_id FK + string status + object incomplete_details + integer completed_at + integer incomplete_at + string role + array content + string assistant_id FK + string run_id FK + array attachments + metadata metadata + } + + Run { + string id PK + string object + integer created_at + string thread_id FK + string assistant_id FK + string status + object required_action + object last_error + integer expires_at + integer started_at + integer cancelled_at + integer failed_at + integer completed_at + object incomplete_details + string model + string instructions + tool[] tools + metadata metadata + headers headers + } + + Assistant |o--o{ Run : "" + + Thread ||--o{ Message : "" + Thread ||--o{ Run: "" + diff --git a/module/move/assistant/design/entities.png b/module/move/assistant/design/entities.png new file mode 100644 index 0000000000000000000000000000000000000000..69906c0fea3a5f3ccab08e29af868e08053b5947 GIT binary patch literal 558866 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

8@vJr4xR6DnJUR;p} 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

tXvSI347xf%U& zEjHaDcgHlW9z=!JV>t_$qe{w1+Yg|ulCY(j3rufW9QRRGh{iN9CIV|YJo_vxPmF_f z;qjh*Ubv*y^UI51%1#zMmHj@<9-r-v>#ED_cN8>Cswx{lA$UYx!@D1@`FGOl`_qLq zo$tRs*dl7n+}%%1f6`sDphcG{bg^HhhXuy%n__Ex;R}_1_F>;|Y!T)Dw$r-FttWTV z(sG|sRNisJ?Uf_;E^Mjo@n(ZXwq^Fj)V0@L>A=6$E<2~F=1G2tHJ#_;Xf`RSe!?{X z|7~mvf7NqbdHndWo;PsYf&!S%ku%s;3j_1J>M|8#n3$S3^ zWrs)#H4#abCp}NR@nr4$vri^uizB$!0(0EI*+7OYUR-N?Ybl_B7-jgCOA!@ZWRQd7 zUj!c0Kf*)tBt0q|(sCf3tn;k~Cfg-FjJ&gNJj@>6x9^{6P&)L1YAqar*_G{LyGgY2BW$M)XG1H*vh1kzSzniL9l)4fL2?#}@jyjq1ecJST0bg_?MGoA|Gj z*1Tpek4zwh*lkGEZ%V65TdOxklHR7CYmqNvE=VqP^Y}qfu4z_|!VJwo?-%rnm z*GG2JMcb0h?C*yTP1W7>#lW__biGO+}F9Id3H zqnEs)uFYYigKzSW(gbF5>)Idv?7jF*u}orIc151ItV-I6>(bdARW~mUGvV4nID!vn z+$$>6C|%kFO&4U#9@}<&{+?N?5bbqx@k^(fRPNo!-LAPxXxe+?1`BT@I5jyYLwbkV!she+b&L94~z?R!$s)bX6$(W7M>g(CD1<{oGkKY?Ds7bxuKD=H~ zI$uY`S0DhI`EKNYX2ZP{Q8qiUX${g-VT;{2ZzwAI*;Dif@O(t|Bm&k1&#xWA*!HE&*E%8|<#3(- zh0U}m>i)2L=8!!`F8ae@lfTTQRKB^qCpADZQ?5&n2aijgwIwf#b4Mh)ntC&-H!_)Y z7C$`8+u+HVXR!_KxS4eATwE9cf-rb@?m{p6s^?7-9p5_tf}mIW&Iv;fkK_$>#Uk%>FI6$2=UKjH}RNKj;D{z^29Hg4I%R#psOg@ zRa(FD+uU1^X@!o4r9#zk^>Iy4z3G3at;Jq`c=VX&(VEf5p? z6EzLxPuEjyV}T7#FOLNwmVB?#jI|PCX_yC6c?uNxK0~INTxo8ohY=#Nr*P=&G`w4 zi83oja1nCA=H=yuyEH_uv&;&blae-zIR~WVc}tRCUs3c>nGJ{hF6KR9cm{R%+$tkj zSmu!SQuMTka^@`hJP~$NF(-R$fy%yc#$&25IT(0Hz7-M_!>-vFLK#3DWsXLn`<>@I z;Z#7`=xWkSS0$m?z&CpZ%RxL!o*8qYc$v8^VN@)JW_%}L&`E|XDyV85j(+*&m*VPE z!$5qnt7JO{Uo}jxpLNm|uF)WEUurBFfyc{ z^qZt}O{?eZV6acIR}_W;WV(f8y;;u^!$yre`r9!A1cSLs%5Pa6O@#o&noh^*gNhjh zjs8~ZUq5D5uo92$Uu-xGUK22YNc0U;hLGUQ0})_s(Z(qNYM36$0DE$^!^)NE5@P|o zAY`FfORzVKA|rD8z>3OBu_q4UE$kY{>zWv584|+w*{z47t{z9)Ng1iIx;^R{fKf2s zU*8Ajim+xSkL0&v?@Gs)zfgh?X0U3>y7W%s+yUYOE=!`lt_Z zv=}ly+)!)JZg{h3&CgpS#RmbqnG>idX3FqWAbrYg$IxpFBlG%mWmB){XUW2X%7vwp z7S=vS(AYD-1BI@9aYqvekUde3ukDKWRaL5#6{EW|Vuk!F)M;A5Hs;xwMF@w9BW7@y z2|UCwA~8)mMS;Oer~nqz8R5ne(>Pk?<)q#JMQj3YwTetWmyi&`iBOai6T+NtPSKeb z11FxEV0JY0;}cz4V`Uv#Z@&6rTJ@zD2j7I0m|ke7urqS~^B)gSc$+-;^muh`%?9hb z_WSTh+_dHKW~a*huVa$(=Y}o8ucG8o8Fa}jGfjmFvCn7yGAhnm^Of5E9St*yp!*nMTdXK6r|{UneLi2yLbQxs}iK1EzO@Zg4uSNcHko zM^=S*@xeofR3&Myl8qPEk$xGd65ez>@M`tUMp} zA8Mq(Y1`S=x3pnvxIA?>HD|@urblJc=Re9Qdxb>FU<>(~fd2)~- zqX4Y${r_%qwNZh{amVX0XzOU}fKf+%AgT#hfG+G=Nl7TemS7fFFkimMk&`-lb-5)~ zgIT9#7zIbrNn^vBKdQ@ddV(1KO7?q+>mq@i6YC_H{DB$D)$4z}Rcw%#{TiPZDJ+Ew z&I+V<6?b06;%7h@=l_xl5WxNu9Z9qo6P*fohMmk&`p35Zw&ii+}Re|Kl6?kf{grF=PY# zEXrsDOrKZ$q%S{s+35cruPEdx`5r#3hWn79MZh>Kot+J-8lCXHVg#Ch=W&Y*YGQ~0 zkWu(z*fJd|nZlgUF4#9)8#G<8`=Q_EVL`m?*|X$DW14N=CEs5EpqMv(C61XT|GHU? ze|h%*g@ir&|0ZGQ{nGp_m(62BE7@hw++GRGaKeIuH zv9kjshP`W`w@Gn5ZL=(^zyJBZPgA4pu4s%-S0ZY464m8cD{~v$Io1*xS<iU6S%r`kXLHZ0n~I9R5LjocVm}zAmt-g*mzpZn&rKH`*v*L z^f4S3h5!BcHOU=S!E5Ns4Ithy*#!qN;DfTq%)nk+*`ceEd7Q(~`~s0e!Jhc&hRlvC z6|Qn@Dsy@1dKmNMRnUM1h zg7l_R8;~!V9A0_jIz-Vy*#jXvfh3Ag{sc`3B5SKY-NUmApPnr1<|1wqR!dSDqq6Ou z=~0ae%#MM-r#~l$%zr&)(8Yv6hpw8*d+fnN;BZmxkq=2n6W#ryut5P2b0>7AXr=Hr%6Z!HPS2^FcpWejTlduFrV-)-g6X+3gYh| z#^B_h8VF%ha;?;pNKsAjoW$%_5(TJWbQr^pPSNK{i+vHOmz7bcE5cS`57O47sTM0U zAtecElCyRGQjT^>q5~a>zUuV&JLR>zdcrIhxSuj0Kr|ylm78($uZEyWfX(72*rDgx zc`rlot`8mRp{q+K-i#er|wvb*qEybktQ1KYp>+lGC%TQuWF4*l)VbJ49 zcYb_^P#o2u5G|?U#ttrp@GFO^80Cq9B%{b1!*OpGZ*^|fJ_MARkg-hU-4-%2T|<}e z%*a=e3>*2PS+izMJ#r&oSg#Su_3;-L?^NPm1W9cMyNF_Rd3BEUGqCQ)ph!BxF6(wE zU8AVxywQbCZ9t24$jYN*AHsiZ4y7Df`@Fm6coh{fVn{w2_vt+}PbhAPpZjsqjPYl@ zZ7w$pF66D>M`(-wlHx%I{SXjm26WYdE-x7ik~+p(`Fy}WZKL3E(2Vs_TxMgIQ~Hoo zvlyqQbeLRiW|ob`=w3=)z-^gcUmn--<@C(6k`c@i1=D)kZ`UTLdj)Gj&=XpMMAVfe z0`ZAf+YDRDbT}?Cv`Y&jwx%2PsMbQQD43X+cHHdN+h*ZqVXVXKh1ZJU*!@I40 z=4PQ2LAwF0zEkF?4#p$IO=c0sy+SBSC^dPv28R^;QiaMh;gLF*D{I5SJflA(3dipu zmrXkcM2srmL+jHRwCJ~7MC!Ol{^ls$N@gb4)5M|@PI%l^$F&gEWfWtY!`RU_zq-)b*V63bvu3G^GqKk(t7cv}&|xqD+!a&oBS}eG>|O zUET<4w9sHr)Tm~^YEc%k zs(g?;T=^hm3ophTm?7!xEFjXa+8;7_%cfb$8}4m8`I8jMJ>BZl={wz!!$4@^+D4sv z$|n&v3Cn5^c)fs9+{YLBFh=Pv79a3oTE(6yMwq89IV&qk0n_r7{)!cBwo`*rO`6>R zi*R%GkTsHD0_dkXml9hb2U?Ld&oq8;2=9>{8>z?Zx$9qVg8CW+5*U5K@d(hq3_M=6 z#RM1}M6@jG-XP(Z<(EFDvQUZR84c^HgEwUn@Z~_jF~e8 zSqZNbwzIOuxg>GkWv!D3`=5T*I>ZhDY0ekR88Zs9Y<4)AO7YButm%AtXXNaz)mLHg zgJ(z<()K#DVS1WP<#)zunsasWv2{-9lTDG6&mx!xQU3Jvvhtj<$S!fRvKIIYO z9W+USMGWeH}2dhISp|_g0IFf!CwHtRyQ~)Y{gL`cne*K z{3wB@$sRMSA!8t#p%$(Q`FcMO;;fgK6>5=f#kpAMuVnRRr8_%1X2`9hT{Z~R{d3r8 zpfT{MyKJzwFLUFaLL?!LUgB|&h^v*7EYiq;pcJ%WK_SszAOE?qp&9U6HXND+yEgpM89tAY>l5;N&g}l|vF^6sm zBrfx(JgL@bXZiOGHmORA)EBgP5=SRvH_4`06xp`tsX=AmfM|qFIRjquBYuEs^kkAQ zOjVPa-VKEaa_VlyzBGSw=D_+gHV6?TELNWyT71n0yd>IX`za|YD{Fg-BoyXk6;@#-eH{g78@ zk9o$rCtH6A?Q~s9LS#iO;M~x35e1-RYb^?@LbQJ>$%F+;ye8bk;EqyjpnSwkF$1qU^?9Qz@TKd`C_sd2d=uA zdHaZlomRgUz?$K zJIqF?AL?3i2LU+;=1tGMHF4rPi6ND=1L)F#c}m8rx}L_+{Y7_FH?gQ5tR?wy8VGQ+|n< z2%%u_UV}T5KRv^DH3H%!cwUx)v4MKv_Z*Z4faL*o5;Mmk8f6zw;teZudCUxXLcvn=}$?5aQjfuN5Wey9A^&Lo0+&T`7h<>Tjs9VEL6KN z_(h+FcbolI{yLvSxm~H}^j27H4UcA$vec-HZ?Zv{sR7UYbTpI-0jDnR=fs~wd*bcP zsk_Za@rdY%S-tlxTx}hg+*mP9{~3rb3;eg8>tzieX#$50OH03f_3CNU3~!q4 zu6BLKdYmZR!*N}N+6K zgyv)m)<)=&6;sbt=Hc5s@~t@8rR@Z~XO21wr4?1>6o|=}b29cYlLN!I&c27bNbFn2 zt$%mgZ&E|$r%lSCAgaOcw~zmwH)`9Q7C*QthlYGRB0Pmnx_zmLt$CNSW1h1WwtaAd z?eel|>cSC0w9g{``^`#die5I=5{_!u#|+ic_MUTs^a6?>ei@Lur&?&ZZ`q)vt1wJq z1~zK#n}s~Z@Ov)f0mqb$x;!hIZtYnToM%1MCBH4iSi|9+$EXiUX5<1e60-jN8}B6P zRyn^sR2Fgb5z3v>4+~-9DApDbYyl;3QLc`EGEkMXNjy=xq=1ivFK}CdTIC`gKjIPY zo~zU~rmY=Oe|FK^)j=yQUmkN;=sZxhe`--({JPuf>It2PwNP-^+jscD59)Ohz0Q_L zcA7YB{E+YO?K`~W!}~vfObyz7XP|vEDt7jmop!$;wLyu{E~&nDLPRpp<`h0rIYvLY z-U|s-2b{ianbEr{w78&Pj*82Ph3ap5@A2n2lugRl05Pte>J{KRK& zeYKvpWjF4}`=j3|D|2c(Y(N${=j4n#j7r{8Q0MwO@~()qQ&M=S55!l75BZXzYx3hx zH_=iALvPXM6=q_zeyc%fW2N%Q^y$K9*R-!p8g%RWqqZ~M74u|PZpLg3(mDFu| zam-cTh@y(Bsw~jyN<6gWu-!D#jmk-6VHDj{G1WRPhs*+lz7f+HPf0n@&2r7p`L5N6 zNt!aXnS9{dLOmpO+bz0$@;JY+--#%EvH@#mfDtE^mcdJn0`f$V27fO<=v(A8ym)O~aL zFm6obFIyI@d*)V!Z@&gw-c+Xu#m=4eADsbB3Ifp|=T?87`aTc{OsKs9w%^#NGY9RK5wLHF#Ge)yr=y7QAawehH;=wF3*Qx}&$dGSQp zhl+hUY}wyZ(7=bvwsf$w^^N?@FAn?qmK@o8@Sv^Dm#tcLwoCPVR8U}*X=E6&iKzpG zq2nBw0~sVpU?C`P7yL}6`BW|vzzOz~0SMR)rKAwL_AZ@M zDkmvo%#a{!8fZ%Fdt=arwxp`h&*bvc5 z{1AZsWX@Km&gnHOny6Xp>O4=+hM22;=9o~7>EUsOZ=(hc(lj2k$SFF1^UZv%r=oS% z^t{eeUKp7E0tZ_-Z<10RE`i7%&JQz_{Tdxh9*tYO&g_R>bp?J3nXLyGy4xspUQ}>h z$$qZSf)}-g8Na=0?ck(4MZrDye(0WQ`}S5Q=bPw9ln1y~wOuuD%4y|wQ$3wOIA^AI zybOzU--N}}nz)sZvhJQ=>!|6%Lc45f#mK)gGXa6mo16Eh52=Ug)m~uGeA%=BB#AGL z#X&xZgIq>dhE`0;0E%W&E$XqVR9DWVH>xaqU?#LqzXsru%iF^I8+o%;K$a!5NIKz- z)f`X0uhL&Fo9i82k7gIH3(IC*e&>CfqihMMigy)7wHNI**!X?5NgPVzA%(5>AXhZl zJEarV84tA>MCJ~eD9YMUJ;=Zg5p$}3*{N`PP}o=-4WjOnxqpDH`4HwXE1hG@T@nOL z!OTfBAEw}P1B2FMA}AH5;N~qc$&uJKKsCZBNNEh@=Va<$UWyexGae%b6Aucd_jP)i zemPuvd#FdT(INUl$5BlrRIB_5Q(`vu&0^7^lTXVeR;W|Lwx?S?{h#P`JxvE8sBQ9s8>)?6>elTt3y!O+k`!x0-%G-PT?Aap@1Bx~91(vOzY@vOl!IUOwI=0BC?Uq5R zZR9h$y!!LY_N7!U-E+it&u@YG{P0;5_W2`_NO{ex>>zn!y zzar~T8;oSQ@;a}`K3S86(dE~QmzJ+nB0!=&3RTRHW?GI<_sgs<6wfxo{&=9QP-o1M*cP(DDsE8hYl+sEG z6XAFrnl(}RB|^PDY~ziOP54_>+k;nGlC?qzznzWr;CTyE=-4%sIbmSdS#t z*B{mHRb-^;K3H?nHVN6SDF#M-HDn5pm*Jhss#+9{_e=UT(hf~w*$ZVO5!RK=$>%Sn zhmD0kA@dBFbi;ePz=r{ZyV0^b9RDR0=)D6Aj z+U!NbOrbH6JGyrureRl)kRu2_l8gCkNx7j7iMT$#D`IE#lMcOdS)uJ1^kGH z=}%j=diB}q=Lf6O_l<~_v~vloKmlTCMO_m^cPpV6!oIT)H!=>0{cuzfv_DFJS1z`z zz~AAIo3iwH)6IwOJ<;S1=1>u66mi5LAFlzm)n(?|Lh}(KW#g4(2Z|ytN-|O`futuz(FtkJmG>XJZ!P8`olRX2DeOeBp$Jj>tlf zDA`@3v)$lskqjJ09_8iqvv_CA3;|*o^p1E6xP81pPRxX9e$p7`h@+6$>A1i& zhTdb>+P6#dw(b7pSks_dA%U4Ev)l6*3dEYUsG_1GBe|h&xrkr?rq9WwkJ;qtZ?8)) z!F)LoTMBFGvaF3e9hYz=%fo(ku6xW)Ur)<-X{g!!#Mw5a< zZBy#QUyZUEQzX;;oSAvOP$Ajg@~wq4j{NkjtLof=#`ttc;09RNN@v1_7hu zVkvnA@bkkya{5&KZnCoYk4)a0?SGsK1uWKNDUbB6%|}5z6lm56Ax|uvKEfV&@Zk&u zyTsQEqmok9xRMu4QpF_ykYyOw={lq%+3WB7ni;AXh6Zwem{};YBa=E)%(*0XI9&)@ zj{CI?Xh1q@NoTsrT=ft`Ec-G^(dw@9?%CpE$EJ4%o4J?G%!;4RoaZeeoHp?mSKVt4 zyNp3HLT!GfhGgt9167SK+OH(8S?sQQz#wph7OY+h&XOk21w+i3zn60)euen(yV zQu3haXa#tcjsnf-h?{4Bc&v&wBxl4q`RVc#Z{PmBVPptx5e1VNhfuB|le|^t_~)$< z|MERzs!ZA$?{wr_0P~;OU!zoegVR!(RvT9O# zyUc*z7G}|UnY9=(v4p(Wk>MWtNqFtLYV2+u*@O77&a*zx zZMDeX+Pu^i;s(rG8(aU^$EV>w(=BvH*3shI9K+vP%-P^&(W~>G0wpCSv321U_eI7l z;g!;4ai;NKDyx<;NtV(B6jkRIM1h%-iLubQdsA2bBV- z;P5%aQ$na%cB&Y=L8hVg)nnJ{g3H=M&lfwMmbbB%(}LdR>GS6UgvSOW5M+1gh~c5b zq!a}>7P%XB0Xq^7H*k;Vaz70KTy$Q|n4b0QhL6z3bV@+5@hHvcwK~s_SN2@I_szw5 zjBi(A+rsruUe&*J|djn^E`tVa?*?pT7L1mDzD4r6noL+1ef{wRgNw?y%x=OoMahzn|*` zdrDPX_!c>8x15}u3}~R`XfEE_pB94AWA>q%ox+`%DouounWyx zF;<3m!5?3~&<)qYfUf0dI)y%)0aNFo^T>q6uV<1xN@(TSig->jHf3zuxl<>-V3$v; z>U49Qlfp7v(HIN;gr_WV+LXV8BlU9fDe>poWJk#JOXXP2x6vC~)tKm2R;&0=NDC#79 z`WF)U{_Lw3$L28rHaei>g=o&ZrgziNfu+`=8IOy5sXl&uqYDESe~QVE($V9`p<`Vp z4nmJjYUyd!$0_~C?6D-!m|av$>T)|MZdr=s7w*OX^0TB}(Jb}jdh_FVl z09Lv1Nq2VmJo~roZxq6NHQ$M*iyPyPVoewn;sXe%tpekc->ML&j5;h3w;3UgLssb7 zt3&uV5&ad+`+TZUn|_9S^t?Y?ik=Hj+J(XPoVAkIcjDp&rzcLW+>8J1cruugye@V( z(b1wxzF^~zN_ulyM}@PWMcprX@wINtHN9Fqs@e0uhOf(Xn0SfK@^2cT?(_fOG{t{k zd)xu0?_y;tAh0+L!{!d+|7_t-0@_}maD>qyG{T=i^X1H<@{rVVp`pS52V0FR(*SRm z-JSZC^y(XI{TvTQ^lprQ^ja=nF;&hqG%0i$svsNenPOAT(SSumzr}@~K@krBCy&kN zYR*6lk&N{QuAE6PwW<$S4+C04A(@&Oqx2fGCr;d@0dVkgf|wjc z)gxn9@D{^<#kq81|2MhL>4i&7bOBmj`F)b+F1%*$25vLl&8b@_+TEDU)(0i*7!`OC zNL9SU2g0rzZS}4sDs>bdP=`_&*tdV3`0B5=a2$U6 ztx!5g3hfu5%=+}-w}u0mv&I@*Lk^-xrk~$O!a-mZvBjegrPS5cWs)Nz+csY)BVjQ? zzVVu2)^&jQtFDq<#LAHPTFwawb`*DVXo(XNU{TOjwNo<>orDXPmu`7>+2IKq8jVr% z6k{-TqIxkpZkN*a$OoiSS(o$~Je`ykpV>CbuErKZ`C!hm(N zL7xz7?P-}J3BY2QM<-D_{qB0CYlF)VLH&8b68BaT!sJJjx}~>6AUsKYD3IuPH6qYt z>dw9za$&9bz%!i&pZY)9dKK(7jSV|s@~?iyb9YHdeQoJkaqN&&qh~jKTq)vB5oYXT zHgan?MPG*KhAWIA5%FvWl?wO=3D3Hd(a*BJrQ`$)F%UJzYj<}Ayd;h2#xJ!$Z0_yj zBXJlrC4+*xZPm;4fXOM@!3H{F%SB!V={sXW3Y6cEYpLMx`F`COO*_-y|LdN6)vcj+ zt^k%Yv1laU0U64DWmQNXPC$=T@aal82*q$N1`hL^5%1aVH8-^w-EkJV1}FG<30crE z1S%Z0@apcC-(FXGDO)bOTv5{Sa(AnpKn**}`g&&po&hp`3HKl!Zy;6iBH$d&e8L<@;##{FhQ-B$T{Fe@WRk)f$EuusixU!UzK6v@v zpC`pPaRKs^*0HwQ&IE6jrj`hJLFt$VY2j9W+%*^zx3}QjGXX5B2Qd!k&S9!}aeRBFaG)KBYp}!p_%k(&EW2mvO7ns#UA@ zw6^w?x>*8)8KH-=72WuLxWZj-7I##N-rH9J5o%<>%ewC>A$BS3(2R*Y-F_Z@ z|C`R8FSY-=OJ4e!^uw@XYCh4U9B(aMea%xZu(5t`y= z&z7QjaCmwrikDLpZ;NnXd$YVC~R>pS^ibpdGr`azU!cbp_JqmFB5lDl*2G zSMX2)3OXFVA*ws6^U9pi8^DP%trXi(mVcnk0-zLSuu*^u56 z$)_a>mAa0sTx(x79Rw5^<`hk?*zPVQfkc) zk?`tv_Pn#}KD_&+xdN+s2#pQ*7n2;cQg5ZcN7dv# z`Zgy7=EdX(L>hv#2#%~%qVE04auA*?Iyot%_+#7~(X2nzw@8sG7&XR$eC zqU{@G&wiOE3UNZ8+hhl7p=JY+w+Yny4&ocI)JqtJK42vQ8nhGO9vM7R2@<=Z{s$ab zTHKN5g-ncY6e8y7J#+uCKUXrQUFMORVz|*^W@(5&WBqF?^M_$hE%9{A;vRpY6SKW*Rs@-n z@FPf=ObEhG|MlQmuqAB4b-b?f{#|^{_(<5VY%-&j4qDC{*tT`+JM03&ZMiX=4U*ZN z4d5s^X`>UtjWfdRK&uqI(a;2j&s(+*vEedIk@^~v?`&{61F(hwS3rk&BzUY#yM(OM z&sIK}cg^k_hV^Xpnt(lqRFrkn$cufmxIe;3=-cRUz=u%-@4u*SvEit9xU=yaWBc-u zr#;|Z504f%g+Dib&gL~T+Qr31xZT2l7EJ<$fcLrT_|vQ^41n)+qCJ+d(m@r6ivZh@ zr5W*?Pl!_kHH+C!`I_DOhF-;ADV%<8zTy+}>hGnuPZH2VeveG=jU`Vb8#k75fNkzY z;J5bL9jMn5<;@!D7wTUeo^V_D!BkI}`b(qA(JKr#xTO@tR|98Hmo}1(+n%`m2Mru&5B$zvfg$ks80abFr*d&$|;qgk=lJ!2`bW_ej|& z*>kbN7JMR5Crml-dthg>(2!1hvUiLE4bbcofp8sqfWfn!s!O1J8n(7p*7)%1;^^{= zO#XXxx;|YI9Ov=-8(_FfMfy*%vyVKWYncAbc8d={p6vS05%X`r1(QUft+J?CQ1@r# zsr-l^*z)tQJ^V%#uzL>Pdcwd!v9?k7aBV((ly$f$18`4=IC4kw`yP)^j`a#pUyHou zo|k`dd3glWvhF$4jRv$L^Q{?ZYI@syK4K~Z=8=*;O=ywpfH(?(;V zknpWv8Q$cw13wY}1S)-xiKw8`h&!^faUtHkWO~=)ymL|VRS8v*v@Q?|w6wG~wRTaK zd6dUkkWTsp=N+yX0hCyN>9kmW#_x)pyb@dIyaO~e!teMU!IV(f>3pCd=~0s}1RH*= zh2vS9JAW1RHa)j8;DJ>+0??JUtv>&}`;j>>)1#=sPOVI#dr=uR3UlE-d*){o$oE%c zCSZ6H-0@+TMBg#ktEcLTg$ASf4hu^?18v4pcY$t}ZN)D$-Hd9_n%t{MSyxUb?%4yTgjwB?1csA5|*ACc{mwZCnwN$YE;$J972b%}+F&4=s zL$9gy^_qjs&%s=E*cAd_(7BZ)CpIyJti zD71Y#t>-AWdQq;`@((5at$v~oIeiJ9LQF_}I;ok9jXK!?bhlpt$XA+^{mk)v3B9lL z4hb-SVDJSI=mMF^Zw{UKtIJ2aymuUHDZVnxtk=O4?BpJCZ^wrvm4%!>ofce5@p9Uw zo=Pv*ukLO%bhj5+)cJhmLKzrWM);?v#w51>OTqp6zhJHlEp{py!v9x%!o#z@ule%Q z4o_`^Diw`I;v^Hdu-_*CHX;4JvQQ!bOBmc+&P98~QPFk?>u<1rNbD+1Y92f3juDho zv*blgaeBaZ(Nzk#%eQ?kEG*ie z!4B5W9QI;K(P|TTXMKm%Jv=R5Ui#+dOSc|Uvs>hpsIs6~V_x~%>}N^PtrrEpdJfmJ zx~{Lqy2az-PvtBqPl7ol@OnraU*$-lc~_`hQtGmV)Xa+*U9AOC-k$M1LFH>&UV^L@Xs!|QszUe|fVOA#g8 zR6UcrR3A7!Oyl|7y~E3osnzJ(m4!~c8_PD|?5M~R$HsamyPLlRuX4qOsfIog~(@WHV*4-O8z@_m*1tgK5( zArBj&D1^N53(?0Y@$Qx;SGaijPi;D~O@VsN6?9$W%p}EG%m0F03ezPzgT8JcDjLIj z3mvnf_)$kZPqhyC`j$RB5V+S-C))ESh4m4OAHdd|73-Z1=Ca#NjV16~~AS*EI_48xV1czl&Ao(Uav z2YrwmxNTc2*@Qui8x}o}fv&_uF)S1gA(sviNVbKOr2MUcQe}^ z2M@lU(1(wu${Pwy-|FYj=fj!~aZsYC)jHtiRrIVUWo2RS4Gs>TOe6bh4`xC~fAv}9 z_E2v+HkimOcBtzZtzUlE9XM89V;-}Vz7f_7mYXG*PRRJf`qx0sXlh=aaI@Nj1B3Gl zR(j6wQAc_B)dk*iXsJ)oXOso;I)gtvs_}?~7q4_U_H9o7%LFv5%hp?x%HkI{qBL!T52H#MCFh zoqCb>;^nE|8FmD_wx?MC0|^@>ix-f9!*F!Vb0vaTEw z^k(NL4)6>(LQZ)C8sjy7$PjJ*ZL~}MAGqc6F^JtOfTov@BMH%MGbTFg#s}*}Z5}&A zORJZPg$3JKh36&k^_^up$u8V{6AtOagGSwBPefA9YHebW!azEBdh5ETh7*ro&i584 zPUMYRi>3a8$B=N+pk8wk>^o~~8-=1rShCTZrS2_PNl~>3y1&wM!W!a%M;SdCP~4E+ zv+NY7Zb;YrM900^|4{@8L4%F{U~m21h|PiNg`li4F2ALsj;lf*D2!WKRs|vGyuE*8 z2{y}p8|j;Rsx;A47R!`YG-ZskWcu$eX2PTzTB}Eo#vhpML%&Si(pt=KC-+Ug1()cK zE6|qcBeWJ5*SHZ8+d!h+0PWyrzyzLh71Ky7@J(4m1mLC?>z#Mkn08MoyPRr3MB1kx z3qdaSfQ`c|QL%36f}q?6T8ATg*_?@ zMyEWoUcx3Ug>54(xU{>czsOr-`2>`v>ed7is z!5kgjuiv?V2_<{ovp-RPLANd7cqhE2j3)*RhfgU^xiOv*8x8;9wapd)hCb*h>k~lgTE!`u#rda`vPcoV6nF|&=kpH*qgE9#}lSDO7=tS_= zgWuicdRo0afBlU)qLjZN@CEc#nh3LpkJEftyb1$C51-n@6fR=3sgLfa`{zDlLDPDn_Bvg^=G| zB|>khy>TckXRuF46Cn`NlN}?9d!OEu*X&z(22EPKL#o9Ks{R7ySRCuUDM`n^l6gSb z+nm*MRrNYxm5T~8)dBi~t1Uz!=j0|rhr4Y$qDL|`6Ogze0wbyNpf95-76+x_=d}n4k zz=-WbFUaOV#*EFrTj08ZQ@H4A#d1}FL);O_1rEckOkftld7(+G)WSrSHAn*Ah36au z0t_g*>j@D~OLlw^%D>;Z2QXeXM9Q2%1b^K|!+xa&FkX3w=OQE`w{`|HYFoBv%263| zp^C}SM1vp@T0vv=IC0@xV@s#aj>!`VbBj0F;o>6#U!m6J(OHQH!Js;4vaV>Nc8E#h z&wVy8M2j@WpdJoLmIOY0N@t@Ni<@9kzdh?13A4e-qMHT5F5BL`HHZx-bV zf)2Mn5rav9#=QXYLNPO%Dj*|-c-pSeQ8lt7L`bWG!%6cf;?m+~Q%cvMnit+4^xnylHu3Tp^AEi>>! z41h3sN+1dfWcX_ z+51S=rZl}av0+WD#?Tf+NPg?kL-w*5@72B5f{%za23n6~&$62%eY4wBk%Zt~7kX6# zPoY92E3^02F~krvo!R2Q6{hxO&LzPEEN!pv$9% zPo8Yb{1}YI*vCHCi(7BQ8d6b+2bS{)b9qv*v_fc;c&LLp@q`ScPn^KIvk6KCe-fd^eMSOiRL=u04!=LspcTgds@-f9=n?v&z9dBR7p z3niM&idnE5v}U!qa!~Yi?$KlMk^&*C$gJ$;wMpUCi;Ncd3}7^2g|F+W^0?b!yX7$> z)R~V%4geJ*F#*i28DVd)JMSr!0V<@VG?!||1hQa-A@Wksj4z!y-^PW&MXWSh* z!9$R%lq}=}1ukEY=m2OAQEMUuOc`|*&JFSNfdnEf{2iZu`l;-YCI~N5_2BVXiR7Ab zOo&4VA;1%rhEQ>bF2g(3h}VTVmaW6VDQKKnK?t^cIR7;gB$;)N+QUAjyzjHa_TTj$ zV1}?*{18BlZLQdYkt@%c)6>0E3ZHHzGzxE%hOpu7@w%6ZgY`UZ?dud3&7~n?+~4(J zpGPtWECtyyVA8Gh5S^U0{lOITS&Fkw@3Ca`=AA+5Z8^|M7Ds@$7M-Af#v;?4w-Dp= zOm6|Is}J&h?|~tqq;(H7WTT(CG)?&$Sg~o zCz;vAs^9;L5&fY}FLp?BBA|NR9>?*OZIa>%-tanoi(}=O5^>-ZGB5Fqb>_dPO{I}* z-Y8Ev58cw#s1db?S7t6N5fKB-zaJ~oJchMG&YAM;Y-`N0Nf+W)Wpw4FH;*M|Zndg5 zuDh^=Ln|>M@OnKJ6wescJZ9;|o$V^J^NN5I1(PPW95?wP`Uq`+$e@K90u!;Xwvl1Q z)W%oKd_4<}q*(AVsLm~lYIJ-+_4N&q)8R4l=lw8WZlAL;_c+1*jJrc8Ce^YVlc1PRV?_WCw_N*;P?*L)Zxjt~L zL-S^FO}}NIbd7nMsP7gxsgGSq&Ixmem_JY1<9;t@B$PY)1bjX=vEpD@t36k>Y1hyi1&DuB!gCA`Hj_ige*#MmhfXbfVx|>-5`uAvd&i+eBdAZ{ zhKgOObkR@k&d=41m~-RlZ{6MEe)e14H~GulMR#T~#}WRESh12E8F*p)_J{0|B#9WT zdSieNS$M>2FJMe3lva8>irv3?$&5kf z+uGW0|Kw11`8w6(sk!IU(&nJe$EZ|q`SRsH+B21}FpX#a7RHMZq73PDzVIcPf;c%D z)~}d1r@SykyU(8@6E46b~&KWP>P02gr( z=OFk}PS8r4miW*G-x>1STb=Er{9YA(yqHCD`qQRu1-X!^i{RPHMmnc$d4g`^M~#Xa z7}r)_d#3}XEQk5uub@Nhv6M_E`VMg(K+a?K5L!p)K7CeOg?7khH4ie4_kHbb1s3n? z2PuL2A+?vR=0p2Vqq;Cpj+n3UT&qhKY&@bzYJR-siAtY zm~n&rjO^J11_R=4>rKh>bIKkr$bCoOBY&ir)*vTNxJeWaUU4L-(j9~Rey17)v=)vG zcSa3YGpYk4`+5+eY@`QfzPCSC%&tW&qujNtvAT`812g6d_l#92vVVM!&FCSE(lKcJ zS$YVoboTfNv%RN~M1|9dfo{Zo*q63oYT1N&8_QvfwJ$2R*8cYXPr5?2N)*h79vloq z+J?^u4wQOyGGDUtQTVcQwk{qNHs|rK?|sridcgOxUlo>GF=wBBl?%{B^WbPorD7t? z4`D*e;c`ecQ#dZf3*T(@2W$%VpN`H%&un#Th2FgPKakT>OpG0C#Dv}ea>aqmeJ6fL98m^+#JK*JEzJ~Pup}>%TH0D|7HMwaICcT76apl z(ca$P#<`29Y22g9Yxy!B*fLAo>KsNcbx3U1^S1o8spbE1<>fE8=Dp{#-~2uGKb*{} zYGIRQoq?t*Q5L?t%%K4#FP{>c9xMj=9nYAo|P)pjoNG%17-a3{$|hY(dOUpU$oI6HhJq7rX$L7l3Kgx&A?$ zBYb;hWmRlj)gt%uQ+TPx6rnf)8UKU*hTd;)t2<{-HnQxal64p%I zGI`7Yk~)Q&O;INLWNX($S#Ei!TIA0>(yY~Qq3^1jeE6}fq0pU7Cm_?_kOBy00^Vsa zfS)by(c13fPsI$fzAm{r5~h z3}M~FX)4v8h!tXRz``}$m(35Gz>&s@PLWSb%|0TE)?ObeTKuNofx;zXE2{|5pr}Ar z7$dXsb(7MqQC=$U!i0hp; z0H8!BRxk1?#rjDQ&_$;m!x{XTf1gZiBtKdfi1C|JrX0(pj0&a>E@A9yOskju9DS}h zu0u7nNSr%7^r_329AXhlrY+k|+5Yy+;cdFOr&e{SseTru>B4~>*b2?MTXPYulgHJ+ z?H3#dQ7DnkSwc{R<&#?Oi_Q!xjR9`lx#E(J>B6npZ@43OolA#_V|Zcpq%DnSP%wnR zw6&%_b3An35?f!9n1bLKE-A>$G9a(5Bw>grI2MMz7OB$^3o~%Qeoqnjh$JxJwO%y& z0RdC6U^M5Fpy3G`EGyEXE!*I8NU;@MJ%wik&8^ZMWT32MHH#BmYF0^Y8h^*p_tNfO z>c+;#r*oOq?Ug~V?`3Jnzzp-I7&~pQD<22@V-9_E$nn(_5F)$;CXnsE0%6+xM$u#i z4XiLp3yEkIs%2|ZpXI>R?AW}dcP@)*_jkfohy^um+VWqb)Yd@J7eB^Aqq^XsZ}eTf zYsd}EBH>vWaJ<4gS(7m;X}n9AsYk5&jhG=)(C=sXP#l8d1p~rrc^;-Hl^)!2(4yJi zAzcKp5bYtt%VAFc$4R^@KnpT<6*AC!{wG6R32(oC2shFMhZgZ?R#WG$z7lF540 z?#6n0{xsrPXx;gW2+^=Uby)mtccZX{0aUSzcT1fjQ1JyiIior!rhF3Cv~DYn%lA(v zj8V-+4cH8}R!SSkv-b&T67K@6AY>DOrQb`i(<(god*lf_5=snlW9~Y~q{;>vv)lmf zrIBk#0F(Hxp%Hu4Nq3abxBJ<6-uI7-RMtW-ml6_nX^&cPMMFu(aGeI2)ozj!f*uw6 zlEF!lh*J?+?pVwenhz8;(_*MIYS2EptSe4e@7`YnJ*pVQF!}mJyyG3>x&cQNy*%$s=H}5k9o1` z0-jXqiOaq9VJi>S{*!E23lB0>Xvi>yYhfp=*Z*p*Kj&_1!^Gx-|DOm~(z~TBI%_x( z)H5@1z+T(~g6I?69!iLZfhFoovBqJV8rxHR59uA+@92)~h&THC0P&*y%9zA%XJme= z4A>-_oG_GO;@)OFSvgM5JLE7CH~<&B9oY@cDqdXRY$sGUpc9cP(byQ_#zUNq8?e_b z2WE+|#$|mp85Oop!l)YaJi0~<*hcD>p$6fn(UGNuL{hd3S<%d;JXQiHqWgZunXx{w zw1u=48=nd4scNP(S0EDkhp8t7a|Ffr>HK3}O*0oq7Im{uDCU-68=O-B&FD&=*y23K zA1h|7g{DNu3)+DRlGk_)ePAaJw*OX88u)lYBxGmk$HSE4h0lpy_8p?7WrTwX81{hT zd0T4t4*YhXKji<^6O8uY5Ty{)qkeX;=&tRyERITLB~75)Pf>(?kq%I%#Ftd?4uv6v zOgEDU;OXw(Pg_>*2wu(X{x}H+d~$A(d3L&S3Q`+%-77f9y(aN^%J~e*Ab|Ncq0B?`6iH;h3;a ze52wn2EROa_!B(9|7#U_3dEwU{ZoJBM0?w%TASvV)95|I_73W#2F^q zoDa!bbgaYd*rg-{;qpbs?#XoCCpW-4R0V=CBmt}u$MCc#C%Qwo}I3UDz_Tqpsq0DqU)OpX(Rw%dhkq<67X z6q=qmS_+*qXrD+<7P;Tk$wU1Tpc24fiU@DCDlm>PE`jjb@bC^LCGW`p)>ytBo1bh) z!D@z0N5SQUnkN(-GQ=)N@(P}lDs!jY{PG9c8PV43=3<2n?7oa>C@tzZS?ZipKBjpI#uYkxU6K?DZ^1GPQZHl zeV>6B??}>G^KuZ(A{!oSHsSxe*t2FGvq3Ju?BS66NC9l7y~-QW)3uItuz6Felj@#^ zdf6Yh#L&QPBX~;TKo?Kp$%2;N>O}4I1QKIRiE-4r0|TVb#xodJ8lh0JLlZR{gYnd-I083j|Ct3?-WyJv}K1eO}E2$id8uOk}O(&wf(~pIW99eWE7= z)x@5T?9W75sUnzR)p(Bqs@f7?XaI*jC%nrPTGw>p!XW;z+okdDSx!iQ#Nqg}^5CQY zc?8p1_U+wMVp7*XWTDvp#C(lo!$=crta@cZ1g`D&0La0bPfCTZnUL_Oj47^P3p?~Y zin>!OHchON1Mb}2uuU2^SQ2*+O`%$AU)Pq9T6bo6bk)?6zG+^s2pw)m7GZ28X0%UV z_wV1o*Ij+P;P7{p$Y(u&)Uo{M<2`Fg8+7?;(zR=sD*Qqs9{s?)ROJDzQkjw&?tjS* zFO0ZOivPJtBwf^Z4scdS)g57DXi(7K8IFviC&cU?M(LwmjhsE?#l)aqGGP^?^k!2< zX}r0`Hb5NiM+CZtNeipq7i@nx?b@P(W4CNY@dvFz==1_hqUf85+JupJ_)~{j;P@w1 zHdxD)KOU2i5OZVv@ZJN@9HrR**u)rQ7I?l;<7^Uqt%?mH-2 z?|UC5JBy)HRVTcvS+9uozd6)Cc$WR^tIKy>hGfZtmjPHX1HeO8_pd|v6Vfzi*8 zxgN{Z{MO&-EvRC;o%wv}dRGWw2M&}p^{FJox{V(_`bNh|?%!@OnW?-9qDfCby!oz{ zV&~i6=FeK>CRWtSSYUXOp_WFcGtrI3XjnL2c(-FdDAG0uim)uJZ6=8JWPPyI>tq)$ zNyr}#YA@?=g;qvenDMiR)Tob)@glo|cks%Ho^M;}Ahy|##Kw2Q z62s#=n}^$|Q@TH(DI7m}#mp;ef$6G{J!mr>HXh7mkUhU}f}Uno*)4spuYm~B8(*)W zcAo=|zJULk)+khBRy-lxSum`|YXEhpIOd8sic>zT3AyO{XnDl@5pV*5$#jV9i?Do6 z)E%tW(9@h!TEvx}kL`FIJ~jqN_Zvf~^{g>4i->#YI!&v3i}Xy3wSU9*8uBL*iHbQS z#{r#x`{$?vd-^?ONkC!m8XnIDj*r}e^QHIpTxM=2#Uj%w(YY||!p~ur3}xx$!qt|! zGA%!a-Mc`|YT=6sS1_kcRKLK?YbHeLXsNcTVpA_(tk(S8BSNvT&#T(nMX`o8_D^s~ z`XyS<<)25@LMD`0pZTXP+tPDY?;Owk^S2XHkInya;!UNkFAhxIA6cn?smZ&u(;Te? zr}5d>sTj8`Zxd(wd%~+NCoJ?@Z;y>_9A%IdUa&`b@UTGtuRqlsm!;Z*Qz^23;cm;# z^}@9Oe72u~EPz0|vG4(bZ^D?V>gu|{MZ@FgQG_TxN6^iOa+p@<=bUqWe5o7dLqKaI z;u?|DiUNgJMe9W&*C*tFLA8*6aH!9yzB)Sn39V+F&8FfDgYixbY%utl(V^BR;*zyR zfBY9ZQjmlO1A9{@q{y4?O6MZsH7?q4vYo>-Iy256@xdS_zfK znmPuJ+tE%Lh6C{l5koPFouSKnFDBxMKuOwJw`$RsIgOwv9(5tt+$H?Zm9O2`Q%7qt zRWSV~Jx{;Zbsa1lq|Ot@Tn4ZPqUC+0<*U`yM`3rLd2)=hX(<~i3^E%6&Ck)`qzRr0LW z0{;HPUUuD+8>j@oqi-sC-JbN{!1Ifju2```w5SD}Uj7zXP9tSZYAWB~ z1bZRMWrRlf*oqHky@TU!t$mv;6=g;fcZ4iIXS@=<_8~U4_SZEfyKx8g8RZgDh*zw0?F zi(B#qK4mt$hQ(xF(WEl_=lyQJq+|R_J)HvRU~iU64c ziH^4kr*M!g;ug1fbz*ji1;CxsbGkDj5g3i%LR-m`T_Th&ySuIb2llFJ-O?8-%5o^m zv`nL{^;XhApTfREiNI=iLqs4;GXLV?#*oOgZi}sSY|;Q>yvcgP3n0iMP!)B6{Zo1_ z@hB6yaDvNV8DPl44JhZE#u+S)4m^Hbk9kr~vp2Aqi`=pKvyo}FSd^+>!}}VNhT55Q z?4|`>u~K(EWLETdbXS&U|H7U&8=r5FuB%yKYAT`{S$Z8a5p}ik)0-)3u5+=;Wpbw$ z-Nck%6>>oH$3#0DaPCMXERp6dAYV_$04MY(18q97y+U|AuOg`v);nOw?VqC`3g>mmLLQ+9;fL1#GGfCZKGbe`Qz6aC z@J_UkhOvQQRcQw%h;Pj3n0V`KoeqXM-E&Zn#V{=gT-hKi zTtq}ohkPVoZ5idm*xTGPo1imBJOWu>B8~zA*=bo&qEPu36I<9*=-d7>K4zcE(D7>3 z+CuSoM{m~NPS(mcGRn8+@Ed(HmiAO$xNwt#ydFP&8o>vbP7IsuLi;neat_Ej1W%jM zpM%2ay@9nVSuJ_f6kXQs?_CT%ryiqp6Du@7OiU#5ErJOe@W2=%`sVuQvy|Xt@QUrr z9@#8>@BIF#Yqt8$XLQ!{Pi!8H?E2upsrq>U>^>2T7A|C(A!Z6z7=wwIb6^AOKm{cL zBMO51LW5dL$;*s6em`8$OM))+T^SZbT$9gC@allW*D!RI7303kKsiL{m3DAaZiWbq zeH&W0ZmoemgJ>;(|L!?sNkO6ZQn540R@D|t5>tlko_xZxjU{z*2h#)@5sH}sxN=O4 zIBQD>kbmb6gdjDh@}Q)}08nE?zY%sj-FNJSn=r;m?yzEELliUe7iMu@M#2q!cT=mmUyn=eo7_0+F%Zl zQMveX>*!>%3nT9+P1^X(U-kYV%pQOLHjCN4 z5M>0(6CQ}J3r8T}@FEB9qFQvTfB}LriI&6A1x!{}x$=x}_1udN{GZQaDd) z`L_=>Rt}QoKMa0G#XNrexKwP(VdveS&;i_%nO9+Xx!L{v*nqJWKL{D=?U%#9+X+AO z0ZNoP&C7+f9U^3O0n5|CPF!%zV0d|e*sjXmD7tnq6e9o4#CRn5^3lr59FDfs zYaEbyIKl8@7NBn$!)|4vxKDAeJgzhG1a5MW_r#-LmJ5J&c`i=8LftJ0m#9MF$&xT` zxeh=1;w0e@db$V>MSBih7MS0N{#vG3!s#4c95rT^-xQ4HC( zrId?eC6F1YW2}k;=BaZHhL_lH5hNqD*Cw}g3_Oo8gtijuC{L1!<)Wu5xU$K}N_s!|9tV#y0fmjjd1YcZHS9Iz*_Gi6rFoC@X!3j)x_KUw$1mm&Jp zN&)({F8mNV+i>P#16g+e0@lI4efyl}o537jEY|NJn-p7L z3iSx>21PwTC}d52Q}l+%^9_ya`drp*KdNo3-CGYoP^jJC=XGbG{omi#1s?r=@BWp0 zzdvfZ-zU&$Y`*V}v|rby^!<6L#+=klo!=Jp{PMueuRm(jv)iPr$(p8qsplu0o&Lj* zwVT5oa;+;?R5WZ#zdyN0EWc_!qV9O-pk>KY2av(3)>v#}|9mI0a?9Ru}xZB&IlY*F=X`TefTt zdQ{~9`0ncEmv{0xu9|w{dU0*zu8mp+u6tewnMXg^(zo;07qRxsb{>uo>{08SXliDj z9a&!Skgc04y9OSbKR2f-Pie-T_471KlgD?gj$WpQOK>8_k);A4wNqpnrBD)zhBEg=0B%xpAq!NCibkuFp<@M2FVTo|3B;* zX5QWSP>B|Of1M0b0OGv0e|zV970RCkv@GGuWdH~59H>E@iwG=%B%Sjb3=zd$hftBP%XJ*qS7Zy zb+M`BF;S~cbMjJRJ1P<(%r}plK9$8l5Gq4WpSCoH3dOzJng71e)o5l}QX|~!_6>L` z@x0X6`*Dl~6~}%gGEDcwHD3tp9@s6V(3~t&xK4CsZ@zNJ&3FUWDI#2U@80dqvTUr( zx{5!(EVudQn{NUhqCyo`oy=26k%bHKQ+)fiwc+!Y{poAYgGdF-Fq$zO&S=68;^Sfr z2NI-DKrl$&?8?2G&F`rm-u3L9!z4bju}DmP`{NXf(QjYSs`0c3!kxxqHc|JT38f-v z&8z?0i0+HN|Mf#_{>kG$fBQq<`Av#7|F8dih0D92s(jCP9ax+?A1Atm2AplhIX-sT z%x`$&vLa|h_i5X|LUJyfbn^WsI)p$VcbsJOQ>hRjMVe)Q8&)g`OB~e#eY}(o%v)58AjG0mYKCv_#f!W5?fg+2A8AcbrgWZHJ?bbH0L36j6LAC?uPRxJd(k`Xu`<<@pP^!}rT zJ2?ayeK2FzqUvGs`AaWb9}%mlyM2QG76MB!5JFKLWo)m|4|L{HE+y)o3=qxJ=?@h_ z$KS@1H!otVuTWXDaIkRk{^0D#uUq@`7bhJ#Q7y0X-T&14Hx|A7$7knUxOMm6EA`J` znWi(@6C2t$gwhaJx5xrKfxm9wKAm667MmO7FrjiW&Kv@uLS!r258p82b#HoWVWrDL zYbZdnibxEU2$N)Do%@EScntF*zmF z3ImpVt!rs#FjQMx8`3}yI3M+{8flU$PIhrZP)aDOTDEK%!1?Dg9fIv4GaJZ(!e;E# z=TNBmIC3n4gG^dsPrUo`^G(Hkm0u8_zit;Kp2&o1bpj-eaIu+U;BOdVDh{hK?-QU1 zwmT@k^%Tlf_CxSkCny)W1_U|936YRUC>$R7CJg3~HR&-y(Ji!D5&9y}TSpBiKl&+s z=p8hzvPG7HLQVn!)-kx)8h(Q8QxNqAg@|muWp37ZHxKMXR@9#UR&lcLduMQ5Acjn+ zU9aYErqXJ**80^~b4wB+@pr36U?xDIA)9-PX2lxWq5!h{9zS^!L@;i4Dw7i!>qcTM z7FD71Fn*L?fjFK}q;;-!=9LOFUc@+p6=si=8tEHi&eyxQ$f6v+<;OJ}wa|TrAb2@pgHz99 z*j3}=*{9r}o=kc{#u$dc+OPL>OvKn3slmD7e@$B`R}2=;l2!8%_aKcueeopWV87-e z&KmZCy9Q|EE}bJ+n>ix8f)`)4C-m~^kFm=5l1Z^wP#k4{bI*^obUkC38JF*XwTZE% zn8Ntx?uOAXO&QN;#-Wcu)}-u(+jxZL&!}v_kQ!*Tua*O4NS!7e3tFZe1ljBWyjCF>b!VgKfgLQ^* z2a@6l|G7inOB$*%JXX==(NQf8SVmeCXM7qLVSdrFszp1CZv=dY&UHV&5`!a#&$3l2 zh@;{RV9G5CT$(G=M@sT4VCT*goKR{g1?)MK!4aN?7Q>(K58CK8!XiSP!+tm-S6V0@)ZnFX zhfk=`rBdVx=D_G{S1%V!%_ocq@W51E@%Y>pk4dBx(Kx{b-09kdKq&MaA+OS>Wn<5@ z;KANh*%yG|SAh7LcONtDz0TniFFz+|S&OER+{N_7OQGb+pa~(x@9|St7_eW@K4^2@oexuD7`CWiSIri9PPL$MH!w&254o< z5kGR6uq3+tFbXOY^q3H&VMbf-ZAM2fRbTcOJ6@zBFW{Yqf1CnTuH3ELV1}y`#*g1u zw+Al>Zm#UKEy`U2i5)EvO+iY(llL(mXIvmP4mOq}O;h=x#fBcVEN{~YZLv>fQA){- zNb!gvy{y}j^Yz7-bh|6Tw7UEdb#8_+6@`FFa5Q(CxzJ9^Lz(G+715id6QXOlpR>#> zp2)tsG5#K86`-VIesWy5hpat=M*FAnl3 zr(&Marnt&OijnecSyMjv6U0 z7E#$GtEvaPMl4eDkR%1lBNp&Ole&oX5!3+Qir1*%&?Ax_yv?1jSo>f7hY~AGNy(}! z9U3V|Wu*tyxvFcIFHO*71&jc(;jT(dSE`XsI6}RG-LQX) zLX!p6P2iSeHrKzJVNpRGtwPDIQVRhXjaNsRKJ4D@oj(2~i6N<}(M5J3KtvVc$n4pJ z*WcL&BzhF$RkvD^!Z0gVM-MA!?T zn$>|KMYU~@Dpe>TVi|CMOau^yTq=TMQIaezf>>n;_BAXA^?wa;*?MYuiVk3e))IO)vy2c2@7FvZb2Y>3RrhB12#N7wUX%@8VpUWoi7X2 zu4<5@n>36b1G@p?LvTJTu5ufB|>;^>j|_}02b zgB1Be|E*&c9%nuGCQ3l3O-o;s26Tm6g|s`gnDXBCMM^oD9=Y%Qa${L?j}$ONgeEY)x@{Y9k%VOtgK2^N?Z5t-&XGEm{hc@d zN`lCWF$L^g$^EbMcuZ0}P?IXVq^tM!5S}+jW=tCgU>vrJ1@Itln`){ZRi7T5;;C-K zQR-r$5At{i_SGpsL5!JJcb|Jwpeyh!!tJn}w3L)ga-@ylMU8apt7J*Eg`UPkTP1B)#Umij?uI~j%jP~37BGkwdt#~f-6`2aU>80CB9409JIA9i z$)jm`^tQTKMoNFUG!X)1gU2c?>PiXh25`SFF67J!Wk48e*dIv=G5b`##v$l$0H!u4 z$gA`cg6$T?@rfY6Oq@|;PHdFfBl7TgF_e9oyV#w(y+JGJDujUg-d#4iy|ie()%T!l z(mNE1=9FoASt94IX(rP}=MFCV-IHCv{yH>Pg@R($XKvbp#UEu5bkW*>Xfno!b6CPM zrcw+fN^Vk)@0+Kwcle=imJieOvOvQvQT=vL1J+C2pv<#;(gUgdoI%?~1@P5y>I@u; zj~6Goi>V;Ot*nlR*RENkC*k;m?xqEN9$oYZbv9LS*0l7@wS~{y}PEYEhF<9iCKs%oxtdy5_WcCaS!vf zJI{tbGG_Dq%ix`_3H@`aWQSEcJ%=e7REkDwZ1wYHO!U*L;vV&BY3FY_h$u6LUN*;wG@RGuq1YDFPqf*{f;DX%j!5jCl0Kv9x_tM$L zB^1=dEeT9|LAa7)RhOUs+xqLAMJIXm#+IJFdO2`oyVXV<*zFK50aK=;1vE@{pUsfT zIR(MU_+`{&aB_QGVnJFd?@XgI@zsM}W4Fs{;0Pv!Hw|>5kKNDeWnodUw6%-uME*O& zoYxKO$98`ms^FWO`j{SqE+V#v@i*7g==H~qTyYO|yO>EVqi4(y}=UE(kuN=>hELMRF@vJxt0kt3!pqN zD$7I1KQab0aoZ4WcUeO`K07o>_}r^ku`i8}YID@L)}&tx3a%g&Wv#xIcP}BA&ySFr~Hvcodlnk`P<53M|j4_+F(zs&42m7L|J9mHrVqR zIE4+$QV2)C(VW?QK6R*0IC=M0w7H>_?{(-r-1oXuZ%ZS6mM z!bgn_U(I`9@{!`^9KR#=nn_*}69XPi|kHS^HNF zIbQ2D6|t&$|1I~XyVH^>m7{C#o)_omWSFiTFtla)d=djc+=#evN9j=x$ zhgf=kI6#E%j*1^k=V|-pZkz6zZ<@NL8->uw1LF+?b(HBJ(~DdXyLMK&r}k>7@F0sE zB2SVt==4Ko1X;Z;x5ExIcnBUINTnxHsiM9e9`qUlR)HGmUI|MOBJ>3D%i~$8YoKhTT2=!syv8^>0DW#To&r(XEWt#cs5F4PqXCq2>?U4y|rEFEE|1`pJhn-dd{=!WDf{Bx4R3K4MFG zp(ZXj$-sji3=U+sTFgLP=Gf?XYxY;3L!70GlKm+HT*EvuK$SpO80NSB@`#UHZ4K3G zL8jkaK862>EC;i#=p-z;BAbD#s@?NcKY46A-}K1<`LOqkHY1Ou z9;<(}K_l`u1H!*c5g1yoJNIksUfXtHyu+&M9$fPFw49t%Yu(x;hiGf@GB0OpE`Zz2 zYeP!c+{|9LVZ(^$2ew5f-@9TSclz}m;Y^i)*KwtEvMP_8boJ-*$tKlW(@DP@x;uvecRf6u#W`O#pRsIT7Y54IWK=W z*SWp3nT_6HO`sz!MVWO@T=fC-$PZF}{q@&Q=|fIFHchjM460l3*D0nBRXFgP-`MXC z5Z_>9{?K|kJKITYkSb;cER&5vz>(RJ7P5^(3Q^?Ye7F>@(}ui#kse!Jln3ofiZcsI zxpO0`&sNu&tL7}oOxTn>GH3d!(<_wfl-bS_+z_XBJtLwZEUH8!-1eBY-oE7cL8T9T zni`(@?cLij?~ojicC6{Lv~qEF7D-f~a|cjOtvZl0Rr7~HA~Ve+4T9Z)R@>kAL2*PQ zb@D<@s}wEj0)o!uDIqM}0D&A84??DU=9|XXnT7g)s~IOvZTjr+kqSHREuysct^|(} z&t26^k_?_jdvgX$>NY#kCPlaP$i7_BfGGA$h4$Ce4Z;^2)A{RDBxGaUJY!y|`bPJG zihCt*(MO%L*>|oVj?`W}!Z6(M%4gY%!6_$_Z?q2>)kb?*#q$#u(-m8N7yYH@v)XlB z(g?$8YujWh1&o@td4tF$)Q*$QRPwMk8B*nf1MEtQ!=h@UjP4lO;wsEVM^oscvLDsH zj?UJx!)gWRzl=P4=7LQ^DUeQ`HOQD_9jdA=d@hpJuwQISL z_xyVK;zduZdLce7=|68?dT2XOZDmsGt6MS!;(i+(o7-!W;rgm;hcsn~*1eWyGlV)V z`)IliPt?ToW5qbn(wFnz9n8t+HSPojn2^7UstJkgt6;g+?JbjLkjYqA@+AQj&^8nR zCuC7<$ZKaP8tosFPgx0P@Z`$Kk5J0p9uq+&?)K~w;WU_xm~k}0SXMj3??#I&M)N>2 z{X2Bn(<%IeoBe3h8Ne4k12$T)W7`z2qce@Fr?>YYVHV%uP)LA;<%w=<-{q%}*vUD= z=DUgyKuiw?_=jIBYeSLWaY<;B8J{UBC6>4tUvLd3U4-Z#+bzV!piq2F#zvxh2$e;G z01QQm$^1oO@5%LaUA)OD*=Of%p=?iasUGUs05&iC zLB?ihLhC>ddN=~LS$ja3!@uA;`5+$ zOjH+fff#3#bG7Jflq^!o&bZb`Pd#Wr#Rx^dt!>>Kdig(3jsL%Ol;KyMf7kp~PX_Bl zTYbSk15RO7u}ERZe3&x8MCl0NXKF?xVAFaOolA3@h+<+x=xdG3f{Q`;A`SaV47#W@ zMt`_^=L<%rJ?@6mXIkj*`*84FTHN;K6j)*ra`Bo#G5nR1E7Rn!nl^Aqacb8({|Zv; zZBub>5Q(%&jZsNHY3tqg9o@Y(X8289b#Zc zB^EnJgNc=lB{Fi!+3PxZXh&`AL$PM(S4Y`ujj4xQ(&4+&+bSwN^*{bV@m|%h_Myb@ zNlh!q1r~(a7-~pI`Ov?9YL4ro-$5m=+k8x#Ixztr#JKG>gwJA0t@5#EHs zUQuafmLpmicxy#*XkcgVi@s;yK=WzEq~M~Pk5E~Ns6A?XW4s5t)OAAS;oI~90>&~( z>@sm6+f{J%9aG6~p2wuf*tOtAzPC9x$&w4G>bPT>R!i zh;pgm1<-olx&}xEmi^X%3NSL2iR*)mBF)2D{dqo?@v3!W{cEu)17b`lz|)JD7L~nU zXm^Zqnacar2?1E>kHVK^C?YJ^5^hT-Ej#0&t^MYu(YYSaD`3JaR+Sm_coU^5Wj4{} zSVm7kRe`5x{(fZoj~~m3<Jr(%{AjaoEnJrC-H0n~Pz+n99S51T|m-h5o{oGaz zox1$sO(+Tmm3K=bP&e;2fkxT4{a$$1aPvx06vmJLL8eGjEkqPM7}Q%3P(aC9vJqN;A}vKTM+}jKeG}-k0Q~&SI}%v8LjT>r*FqdU+iM z?lw7auLxBqF9q6L;r^ExrlQ!3|ti|brbHODmqXUenr5ir(-C5PRF%DM9poI^$a z%?0+Im&uEcciv`BZ_rNZ3-6z~5J4OhadJ$k;GD|^A$o<&*aQbA(0ya@X|jafz5?(Y z)@!KDSPuadU31(?V?wwAE!%k)k*(VQeJwXP{_n5GDDwXv-(pwN@>%=0YD*FX-E5Z# z*uHkxRFaA0G>{mL1GW9kroJu&eGySm_B2@p&tvQ(Yh~5q;!tHa59trPC{;-{+k^w=S)m96jdOMVfJG8rt3q%tgmO4AgSy-VLOv}Hq&99Wrc z$RbXe+SNS6>V)8yMWEwC+%{b-_$F^2FID~IrpD8>{0_{Ra88lcdqB3QxOCa@i1^od z+UvT#j{C41v||mtGG*}>(btH&v3b?5a1W77hCrz>D(LDT29vcZQ;d>9s8O^1Wqoq? zsh$IY<$e_R)?Isp+x5_d7cEGq(115?+!*jzSDYqdR~MKg35g!^%4tdhzg{r(C&?_3 zk&0*6mzWKrX~mgL ztL;MF%w>3jG9?ifC}FWnu8a}^p}fw8w%S^{OUo~|BJ9lt39DU}<}}kb$?A#AQd3uH zI_4^WF!(Av@-nZSZ>nqK2m$csmZ4jg4N@oymlUz}rD^on@l*8X#5)YDAE$SIYR=}~ zkq_%`q?PNe`po%h!#9p$6!iCszUXlgimvL2mlZtC@Z)FJa`&>$fwNTocVBhDVl+tMTb7zXH-K+>7O-d?bP} zPgo4@r`m397jVFEN{1n=RHUvJ?@vzMlq64bd=&>5$xssp~A7{UdRbg$yxa;=~X-0s-{I?y4-G@M(Xu1`Yuo zrqC7{K^Y{hD-4YYoqPa-yR3Nv$xBN|W3-2#I}l)XLBcHX2~1RF&>DE`SSm6dVPrrx z=|?-WAfZj!itCR?%TD3b(}R4U7C*L2-u7MgZlxi4@ly&8_33%!etnOKJNfx14}a=mi^t5Cd5dVn7!g8S=6J85u+W zrsCN+VldXLMpEhuo?ubuOu?YUfxmv&(xW&@_N0eS9T_SMESZcAF*exD2rOlCr70f+ zGl+n1F$c;0;=PjVxL81n=N`wNP4gOfU@o1fb9yNgdN)V%Q0n2Yb^Se}mH$%V z(K5sr>m9}i{1UCPX+lavzNF+zv{&2JN%(%IAHDx;n-`}1|6q3N?EY_yrU(;JWI!(l zQV{zAgUxWL8fl7?wXDM(V|()}y!pCENaSb0I^?CZyo8&V-Y1IrFO|U(8si91JD)oE zB^q$kSr3;&6V4z>)_YPB2ruQtH;h=T#Lr{JEydPOzx?)FHsL3=(3}B^0gsCRrr3J; zErn8^n0!qO5Ml;Fjc1XPaR>qy^MVTns+iPjz=1X_DYRfveiHJg)nlus>`GqrC0`j`y~iipunm&N&{-0adE7OehK(_67> z#(NFVccYncVKg}yv(~$T@}^Jf)T{g#bZGwb9dBV6`JXLcTYcNU_g>}iPX2%XbN^Pd z(nt(#8w(&8mz%jy2dhuiRcu}Nl47EJ^&`q#lV0(RKF^w3u>p###|}u2qGUg$l+1)cvwfL@=nSHEK zSmHD^MYe9gOQ=oh-13zCTb+iwWC+G*f4iWdV0ZRxz)QzOO}=qh|NiIisWSO#3DHf7RI9|8_SbBu(Zh@I;0V1Pl)IQ0Kmr6SOC?% zd-aE$*_leJlA4*$iR7#cFl)@HAPY$n;AV2xQ}~^|%L|e%NWtO&!9h?TH!gm`;y#v- zc}>it4)M!9*qlJF3ne%FgN2?oj3ny*iF!lOH&nGo$Xj~$?1`^=0KgIcx6O-zV{_+w z)y@8AXcfdfg0`YV`QFIy=D+D$7( zN@_zymU5Ff#F9F^t2G6u5$4d4GuvM1-%bDbQ&`jgEk z91=kx+>ap65c-7s5%Gke50j}oFZbePnK?676df9+YFN%QJl0o&HyK6BF@)7IDjwj@ zNmPOvx%MJzOtvI|bJwc2P1ows>-qc2as8u^yUp|0v2)v4@l8 zcoZ#s%rFDR3#IzzG9l&cRNCxWICa535$i09nT=X@0i1;Fk;HYO3&5-Q>GY9?e13L9 z@PmXTdqL=fx=b8Si6L&vbOtb4^$xoTbB_&lwMfdZevniTnk@_ar1O+{Rk7~iae)m( z%MjS{UB@SiSrAaTMm$r%W5}c|ZnwDWaD~dkmNwSTd2`irZ5b!1&<%?%v_{*0+K`aQ zXW9KR^@B3tZ8D5&Qwt$gVhF85aK_f3*S~%g2@Dnj3tLz!X*GXCP7`hcxLOw#a6&;B znHWm_j{->VaBg3-so}-jyP9VJNyqYSGg@0_7VdQePYk49wEaJyug%@TBYVEQVwuAp#y`I<4JZ@y7Fxz^Dw<&tg-yS9p6of4 zwc0q54jJ>r9+|5=Wytwma)qHKoIdWJpbpuurIICCnJ;e}p2y3#a(j173fGZuHgiVjuoI&jvhp7F~`5(dB5acB9?3*S&~R~6N1s0`w;+s zDj>Q6u{MZNmfyw)pGfNq zdBE)iFDRQ-;{fu(@Uq|yX9Nsoc=cxCWikLuD<^CX9z1w9@RVc;=xx%L`&!R>J>I1{ zteLhHtSu5nA|x;G7!TFAZiLw>all^*H>+q(DwAXZICibjbC*@ST8c~{9C`a-QA5v- z<|o&JmW#-!N72`0WZ4z~y?Jq&&5&}}0m<@K;S9SaiTeiKbQVVF0UU0hI(8NXgNyv# zG1C`=O`%v36->g+de_ZaDW32U=-Lko!NfpIEOFsmf5{eP`zE2~!*eJFvNoV$;v$9$ z51hyobE=I+_tD^OY<=~qBTx$G3ip*8J@3K8m8HnCZvae368iR!dzA-<`E6(5B6c_K zvUEm3&Q0NjPw}%#D1Y!^K}qDl;dAinxAfBd@5JYvfe*>uhV&-{TQ2yr#=aXl4??=a z0yFUGsnX%+^C@KU63osJUXK7{q-QsM+X2Dr3R8+~aw3|Duwu%QX4j@o8xgk(qD}Jq ze!RnFYHkXWESmpHhz31!USm3s^X+$4RhKSZVt|n}suG|;SALfiqT@f8CRC^)9IInw zs>NoJUkTBT0qj*fWD~;JC99A7knmMu!WoH$f-csGMUBW!K+f(S{bC*SrX#=xb1;wC%MNm+4U@vWhCdt%tU^rY1jL;7g?jfqkgu1CXp` zc05N}FFm5zS%n@ODOC!@Ls?;eaYgiZ`$E48Ox$C-q}&N8C_1UOq}k-lZJN*e30Ft6 zx=3s5P>JAR%@^|uPZW5>sUM3g?BIdQQ*aX@_4d^pXx5lNS;Y8Yj&x`?Pc_ki%Zq*D zD$)Q{B)9Te8J7tJ8bH+OKKCbkR*B%g5J(@sc$1m>wx~^AK-JR_63`PW=>Nn$(e6~_0AW;1Q4x@iYaV&}gp-m-BAKyyL^BRtbZPQj;opm3 z;Yl5=I;C=g+VzY^w$&x~J5;WYgs753*rOKga@Rp`i3nbVC=3BN?Dm&Jbmt_Pgss>`<;~n^)!5&*yNZ7<$Fs;0(Um}~H}dP3bme9u zTHFK^cGg3GS;{GaKd=h#EIQTpKmNk>+hFU==dDovN{P|65V z-O-h@Ie%cB=4Dm?liVIZ`1#ENJ$W~O?d4u>`mW6cMX)&gI!f&!o}l&TmCCu+ z-R6!~+YJK%T`nQ2ueSS@AWi1>HcA$j!T~%J9mz%%q6yq04c5NS5{mz!8733@_!EJW2p^v7BjiHKgFI7 zX~HiXFUoFg+S~oNg@Q|IsI;tI(m2&mLS*|E5Ylz>f{uZ)$%))|tFT|*slvn|U~=5O z4uy|3uNN&UyPH@SVsonBmcO;!0bi8*?y3Kwz4q8cy&W$K;zm7yM4-#WA|Bz$1HTOL ztdFHTxxy3&s)g733CGuAqI73n>adyDgqE}8Y3sUM%ozgAGSip-b#KWMTczuwOryXO z$xQ%$ExASedEd9l!U1=%tMi?!eC9B*VY|8do`$0_v@9w~y?KAb=g-zTWorMI#!Cvq zCDUvwPKOhg&{f+DQ&%-4bh5|ef8OC>iv*)a=NIRSZ6}<**;vz7kuodADYZ@YdfT`K zBMNT?s3^{+4Uar)(QwQ>Wq$qq%~wok?)5Ub_o?UQLp{pg$}88jrt*w(vi)oCah=jO z2S0>Ke{EgQN3Z+Wa`yBpS~w*P;J%nnbA6Z z6}EbK_AjVwJ=y(rVzIKeVk?CShmJM6%-3=nHf?4{%{Wq;qZFiu_ZI5K5I;i^C6NKGabY9435t4{G6Ettm6I?;Z$buXoGAsQ#6e zrKN{+Dq2tdh%STvhT=ymr*PT zF(AfenuR#*feOfzK5IQ^%H#6#<$v_7TooLe-F>p!K_^&%*J(N(%`48s5M(u~n~reS z=v{H-YMf!w&+9t39Qm7yTkfAi2N8QStz7359S#Si(dB9O(t@pm-HNH6GIOSSWh^*U zpP92}8R3>pa_O+#Y|=JQL1qYLhT+y;+=zrfr_(K9Vm(l_2;O!G9rQQadWOe;^F5O4 zepJiBFso$8QPf#^+eg<%Uj<_OYTm%kuqw9#zB)O=Qt{s0fbIn*P491SN~vYt>4eky zZxu7rUgkgi#LO$FXOY(F`)?BOEpK!-UswC0YO%tEf_Z0@C$_F}O}Mn!CxR*j9k<& z{Ot43l^%(&9Akys@(F>turdW4r7&;0?x^DANpH$xNkj^9gnH;bJe#+9FcBY|yu)vn zQL%Br_3z?&()G$=u#=tILB^8~`y+Puv~dAN73*t#SFkSAw$-YSD-&*Q-8cptMd^#- zG{^<;)kM&j?JAkkP@T{!dBZ@vr`jsx-ZxqYNFILBsIg{r1$m_6y}3-QF-_FpY+<%;GR6aSTn9Wyl_9p!CFE-=Cn4 z3j#UF8ijv@NP$S3M&7wdzQSXW1P;GKjNhb~HsA33j|gE-wVMfSB&KOxOqa?l0~?lO z_tX(pU0!?@<%x{W&~PL?!7(@Xd+0A3k)pfng~ltVp~_Wygx$*N{=B;E}{0bnUZiy^2hRS(qwGwV-aob z!Sc3ys#wS!DJWMlnyp%4qLP}e)v{SUSU;&{V~ZO-x|{1U!VHZb-4(41n_JeX@#E+7 znWE$NFW^{pt4I63hIaqXO|`tCwxDL5m5UG54Mws8s&Ycj6EAt!N1TsK8DZxbmW%+~ zaZaoxr!@^X7#rS^YpyY-x70&$$fc%n%nZ0}e+xwgG}2V}X`U{6byQLHkCxoOD=Le>%kjReW~Jr?F@!pG%I&VAhxo6Dj@3Cg5>7A(H7_tV- z_V4V9s{I+U=EI-d15QEuH1BcJ`uJ6+Pz=n%Ow@Fz#d$b=`t+a@aCcZ#6`elboDMVo z3VWScQpM2Ic8erikiB3U0Q>fCnwyOcIjT1wnP0+EnB#BW(6X*JZT)ay#&HGieC3KO zRcv!&-UO??bM8_jp7hx$VcIQXmy+_hlKvxijh3X6TQE4IX{5qEutkBWuReW(nSu*+ z!&Y-%qP#zNO9`%%US2$@;(8Ue2~vax{kZKU87@pr+|k|Y*?$=f8oTeIT_s}TmGQGX5Lj0-f&^Y-611Pj%^4&)VHG+KdR|vwCe9YOzolfV?56 zH`J^a&77!`Xrx0Q^YapB2>NvlbbnrA@{%a7@uDavdFBtYvZR$&IW>*C3f`E3@3zbv z>iQIn#+sc-WOjC24P~^b2o#-DL~?M z-rmV|2XFG{3F?-BC~2EdZ@gLNy#48fhq#Hs~;l2a9(n{K*r&gAk%B}xc9;B?HD zx{H;`_Zggt3Q>fHNK~h|OHm_-6Ih24GzsHJkCNYkTqAFZ3#f>gx|I^d4@R*rIAT_$ z@Mt1H;=7^M1`q*>ZUPN)Lco6{yH4V?{ay?mH%@HjPEW!-Z^(2Su)1Y5l39`5M43k_ zQUS!0$HpHzN~?U?l*bbdNt3y&Zmtwg(veBN1sK4RLBVW@?h-Zw#DkM}3Tlek%KWbluE~m#Ox}U;UXYf)N4!k+dCE|b zYKd!Vj|ubXI>BE+^Va|`c>Ola74n%y$%!>+)$^te5<9yMGGLVYQ8D`8j62-a1~y&#>~FU=`yuz`43Mb@NpU2Gk zL0czzl~*0xp3~=Zj@a;KWtdrvJ_yXdrZ`)-(aiNRjLZ*L1-D7ZaUx+gqNnM5FHV_l zOI-W4Jcr!o&+V6!>#L1W3;yBx-4OH^AiE=hjuoY}q@V z##t=Enn-A}5FW9QcWNiuw^a=1grdAP1Cb}^Zd#;>&o(MLwXAprgWdsuKf3qu{mt+- z9v?9?D}2TPx5&wfntNS;2~$l9EcV9BkHO-WWNb4l6hfL4Gz2yc64*aH8OJZD{u*c} zvetz82B6UA$!d7zc|n>)kJTMzt{d!2=qeg9k{3+E$AZVpa9nJox;z8!%AnoMdYb_I zsuFGdxuQ)$oz4%fs4KjxWN#+}g$2EWZvd zwfD++W%7>Db291Ckc9-)G;N@x>MkMmq3Tco>?6dBU#!@(r%V2ZUWN%#yHV-@=}(ff zjGc?L%%;IYlP9AHyWPji_v!xn4c_5zI~VPkF7G*srfwP-2DRz z3c>fjShQ1h{PWaT`>$nL6xY0|e5t+b;FIHqy0s1!iu8%czBYWJZT9wIp6lcH@*3Zt zgiL;9f!*LF3e!3m)ACq4 zo{YUmYd)$}!%t=M`FjQdzP`R~2A4}d_Q4S`8{76(ecYs87WdEVb}b9cSGA>GeGn^N z81V~n$cXgrkV{qWFl~SH)4(J;J-T%>`FcR}X)8Z(pf=Ocr}>4<+CnB>O-!_*BNfg7 z?7PfVru#u}D<#loJl#6w!;nX=rKw+Y6?@Sk49S+;T59-{#smGsVZd&2Ku4g-EZI*2 z8F9I%+g$&jAJ+Qlhs&T-G_FW^K1f(JbvZ|G_izfzr7!`^mOde}>dVn|w@I0q(SKKb z0B;l&0joYUx-x+RbtL1#bzkrpp36JHPx{La-das+-(6g3LcsW&^d z{5;pZcy=>1@xYA@yRFb;$n7LCNW=hQmVp>ze7hu#F*}%ti|c5RS@h{e2Z=brBw%HD z{KLWUK=q>+T%_qGIcMTxtqK3&iC$| zY5DQX^6!eWP$VrZM61bwed5TRW=QY-cTQ+M3fPs`2;h(ol8(ry>Ao1ZeEjB@eDDiT ztBlE59BCY^)iiyw)mTHx^ro+;P- zqQjM-;BT9M)5rfb=+$ySfBeV3pRWok7PI5WU%|fvo{6*^NJ<~Rm%{)l!4Di7BHabN zGjunCCqVxZOo@4&eHz#Xu6ONb@1{9t<2D$Jf*2~EmX(NsA7r9uzdc0+ zB5Yw{-<)i`4FDo(ud?-Hrh1=S3alc^sh}9lYTbKJ`o}%>{dpP<5z1Pb-p&v19W@wa zh|2diX9NwDUa2=*s9OL2iA%;Hrlv)SN+6m1x$=g{2T@79>n3=lN18bF&4uL44mu z`}io92|SU8bz7fc{V!vnibSm)uxE!10Rpj> zfMvWqYZx3pcjanTCL`Q(oTh*u`Fh*E(wkgxL$>9>OMPr-Xbn7JUUAR%6sC_Yw z01#q_5=jS5fy3IulcEgtFtWHMf?RKBNtb(-q$VXkDedzXE>L6n#Cr#8ec02Qo|R3( zUlFyA$L(H#?@c-uN)c;ZMbaO0*Sdw%CzSwKNF}c}@C=GF{kEaM{r1}$+Wh&qyV2%I zqWg@A2N--6Q+Px8XRE_5S!s%4qr{jYfxEU7O?ZAJhlg;mt_dSas6#Ep+|LW%PQM*o z$_2XGd9QgE-VPmaoMn7;YfvlA)|L;qiOLbk_ zWy*<3wqRt$)Ic+gH7J$&r_wjbM6Ab6={jh61UX8C@Cv8S6IZnRF`i6|+hhHfj6$0z z51Y+hwk**zQbh_5!upj6rmk7%p#A0fUjR(EL$+}eC}%nk(EV+h&ST%P z$0%PB$N-qRM@3a=6u5G9qI8)HT%8pO4lOecK|$Tg19BLHJ4r-juY%5 zDePR7c@dn!s`74Zqt{g|5lx`mSdl;BZOiNQ2Pa$zJuCaQk38|>zRR+au{Sammk;zd z*glo91A@P+sMzqFp)=`2uj?h%j3_D_sscLgt8CT+oVl9)*dXE0Ir`7;FZjmx{VOvSK0oWW(mQuo@4X>^*Y?~AgzcUz|?_7@qtSlDD7ODLh$cp|@S8gqHK73vgXvlab{E?EfYBmE{H(M68Xf-g zpEU40Bio6&9-`AAt+K1dag4Ec&%UQW(ChWj%3zZc@Y2Pva611T?jW9KC{VSpcP`H6fp~dbA85A(cIJ6LfLXO|5>I?*m0X!cb&IjW6ubFox}YODiO;LC|U@>0D=$? z+TzUxYw4Q}a_)I@kX{d8_pU;9i+mKTA#;S>&eEk@I&O-SU5^tu+D3gPJF#c(vgz(J zj{XmA9tfbGxYU;nTPZqav}Tqw#r!h1#H^g$ToYh=tC*^@6vORbvZF_+O3w8E@m3+n zbQYK}m!v5m8RhhP7P{cVps0o>KpJAtdRT}7@n(69k)w^ff$b;vJX#M=@7=rb)lzPC z%+sS!WzMk8Jsz6&tHf&JGWXV}sW`>=I(O8S7T9mvLXx$U?G5))ZswJm7#puOtdWHs z_NZYDb^`fY+3MsI#VNzOB`iE+b*BXeG4~l)YZqUHJRkkxjV*iWf;!#O@ z(B<6Vf`n}U;(qbZsynvsn@VARu`+e>Fge5p5cV6T%AiJ)ZnFttlSCk7E+V&+0hY9J zNLUP#!gqd)USR-pmEPoxJ+ss(P;J{Hb#3O9_UX{E<6xt49F7GCcO4AjbazbFW}oe& zKC4fG<)pSTSjXt_zw3YeRb{nG?FMi5Z#HE$r*TGf9kZ7#34nf#SVsPiUPVi9vsY)y z)bq-Z=R4o6tlu203)?6x(Y*&z4w*IIENP{n*RA30xM2^CPk*!1&^)nKPU6C#Vrggf zZt)-mBCZlQvp}&i%Oj0NgearcWMVd+txTvK$-bwG2}`@1G^f;I5Vp0RvGYw2zU;Vk z@0ql#ob~XI^w&B~F>f?o#UxYkj34aZLQiR@2ai3?dZTN}!h$q)lY&vhd8qhU#snc-h zR|B3aWr&5&F&RCf7K)J< z-R9YCvZbOg!yd%2QWeK!79Os!uS8J%*I$2mc(1AihxbMj1b!3YG7^ z`Shig-yywZ0@rmVIGYRhZ#Yuz62b#sL@ixq`kw9ga6WtlPBvm)$N#y7O^cNO^Bo_% z7>~zPaL079AV?JD`#EA8SEt4d6{Y@t`wkd0=5Bc< zV1VLoMeAYUPoP((k58RCwfgz0g7vyw1@|pmCdX92tgMXgCIN9V)l_jSKWlz)H?4jV zY*+U~^qtu`2}TFvx(hg5@A1s;lz;oG|Jw`Q;T=a@ zAskvAHB$ZbhBR`1 zfddirE;)4FV7T`EDlEiOT3ucEmom})I^sg52}m;Y!iSB*vN7aYr`eY=vszG*HFf)j z#8p##mzo~NR4OAWhh%X}LX!NU6WBXAe~*VQqLI1{WL$dhG3U%}D*NR-x+7*GK(jXZ z71vLryQ|Ur_%+^d(^rOyL%!jV|6rLoF%41P=EdrHUc)PN4(48_a=WOwYt&Wq- z=z;{Fipxq!Of2DO9kyNaH;sId_XOsu{4?ucBV}y$-9>*Oez!GRkVxTj%C&K$O(_%? zivG^p+0c)Taf}iU=vwx3WvXk#9CnYaQH*>L88I!;R-eOMMLxX+q*6#I7jg_Cs%~#6 z!ZOgGfan_+$0AkF?X*e~t-)Bi?`T2CfeOxh3~(Tv0<4OE_%v#Bf{OQgdRq0YKrIO1 zz4l!Nwxd~~)qEZG9^;G9A$}uk6uq|aa57(rNRT@>XVIm@zBNT}tObCU6oArh9P&f2 zz~SD4$*UBOm^*FR`IK1%f~0p$7zV-Pu=zq6-zG^Vj0L)^AjxArr@jLX#cVydHknaY zv&8j11ogu(iHfZS&IcY}(R1*?ff?ZRl}veB2IB0d!A6M4`*FQqQb9 zj!y^*>)=@D)6!#9fY^z4XDkg34f9AdXRcxLr{q)mMU1RNg0^=>JyOZIPTyTW1fqU|KK3*gPO7P<%@ zPBzP4O$=|XTVJQ-LA)__+O#N>-X!g^FjLchePR6(*B3xLr|UYVH@c)OY!7sH$EdeK z7>y7w&>?C%x5L3g*Hx|2RM@HiyO)$n2nUB-?VK@rs4g^M2HJ$CT*kIZGX6}?AEIdb zmNydoiiOo?wSh0K|i@LVAL8{W*JZIc~RTi{Dl z0NaR54T=}9y?Lpb7y)7LMbyjJ<|ootNUtWf8s7pAa*gu9zD$ZG%d}| z|9T*gQk6GN3xwrbgP=;*nt*!mCZI?OfxcQ0I9p9mo9ArRMG>y`A&3WppvRdTFoSJ{ z1w5=%@EW{{=w;3oJHMg&FFWWo$Z7-m9!AkAWcN__>ql6`CwY6JoB0EMdU)~lyK8Cs ziciE-98an^0UcI1+HK7>`O>yUB>C~x{LFO`waHZe=c_|e}T_H10vPpy; zLo#6iw2^^u{J@L-!fmKY0W6y0p)L_q(K0Qy!QkN(TzA=zqfv+vnuq1m*7b%rvJxDv`6 z+H`R9zu@?lUExTGwbheJ$H~zcX@tt>$psJ-OEJZPzK~cy$lzMvgnxnc3O$n855cZ8 zIUkz_j<5;(r*v%J9<_D7U>;0Hn5(~!pZ-747X{Uyc=615XD1$NRZdQ!M(I>w*bDF! zbD;%18ZqKj@EjpItUEQAo9`|J|F0)LLZ*o%2G5kn;9_H;!M}Kmrf*j5_)pln>5uh( z|My*zztH$&njyI&@4hbX5G-798gWJY_zXhL0??z%GDpDiS4fDB;qi}b`+3j3?WgWsVJ-oYFZw1eir3k>69k!nB&u~^D=@q{gm%n-W*zFC@UOh5>LRWG0 zt7`Yn2NPm7pGm+B<#DK$>OGF5U@cjA@4vFYZeD+ztVzS$Xz25)f;OBA@J6=;QO8Wm zeJg00{CUfl@4;2UA9$g&Uw0~dVx{TD@}4~@x8<8y$itdno3?6G(*$`Oe3K4*{4H7= zkG?-TGZYZaqkoahhnqV_w_OdsHQ+f!K!!1)CB^yz5Yf!k0N0qBP)_Bv_zCpdu#^|vGd+i2A_sS`+Q z*43vIYYP2B;~~X%G!)NeGnjvg78HB4{d@y&X><@x4y-ce@oT6$bD>*8FM{l4AhM=? z+qP{5F)NZmHQ*BtFCSgiPqo9++<D?+1qcftyYtsY=;$Ar z+es#zOMT(^#@SRBLH3MSmxdU^VJ6*{5mil@)(=$7GBAblJ9qR8AtFU5C6^w9vvu`Y zvwdVY;WM`8$gxZzN->Q)UCAzJgQT`V{n=TneBQRrpb>HU2<4kZ?DcwMDQ3kknNCSv zYGDtIBBC%+B{R?UNr>vjb8}hEhcRZ$;0+jA<44{+y)rjQR6=bBpVJF>4xBDb3cpSQ zakAf%E+}l9jHjV2UPEDTxYF&zgKquJ@!*;7|8vu0kxdEjHe;fRZvLM4|2Fa!*X^{q|hf+DO{2 zMCFSdv(7?dPZ{bE0BTP?sBPI2o?u7aVIcn(W2IOEk_;Af6!8gH9h^KnW@Mia3T(rhnL%^;)GhXX~B4 zq};c!j1qEJCY=EIt%QC$YsScY<#&>jWz@K}tXdg2VGd<p+ zQ(D^qL1fuT^)EWDNYcAzjDyR`pzm+D2y!J8QcM&IrfuV?%q5AHJhLl98WE$xTI`3+ zJM_)-bqFLiTX#XAlg3DJF?&GZy{wLwNKLGAO{_9DRVgV5n|O3yVA@+W;NjHZripk_ z{iW%KJTauSZ)bnT?oI%72xY_^_l7rbcr*>s4=@&9rQ)1SG-Fgr)>-%f(I5exc7jrc zEw5+(2^g?}H)DF2!fErPP8Z(ZcgM{Y1jg9?DQe>GkJzrex9N_|SkNi)oCoY4$nGlv za5SXtZ8%F_v!|AvzO&pu$Hl|{$l(&seG#t$TMggNgoJ5W9%LM9*diIfh894+KZIfC zfLrz%c3M|x%3fdZf4FM`8NFiI6~)}@RW%hx8XR0Qq)PffT7!Ehme7f5<|~Un5I|Jd zT{0*y+tkv(h0%G~Y_R-9@Vob4E!ErIU(}uchH;QUj|_&GU3OrjlpcF#>veh)6aAfPwtEZ!>&F(5HEwLIACUjBumJb z&ZSHyTNp2!WXaa$3aug`lMlB;i8qBo+tWk`BQwn@oo{~g@c8>_y5a2qq7C)iUpkCe z&0lkS1~sTRZRH53aZGKt+UY$O_U0ryKjFvrObBsDYQYhBSEQg0xje;{uv*58c<4z36*{pmeGSL9sOz#K#fE|w*wIH#SEw{U z-|3R8{?30jp;B5Yox5a7>L1R%Au)XyTwO2c08RBgI8Rr#B!=XU)b=moSs@HfZq*NV zG7MFxD3TfVO=uvl{781qBem5;RGWvMZ19op@7>qkH!Z!u@(G+=O|o6@E|AA_x*8rT zH}}tF-i2S35s&z5GSYJ5H_~*wz2U%~O%1o8l*g}DLZ}xBl4nl6HG6h!MT&M=k|kw} zs0ZHc)GpTpZ!I&Gq|V8`)?HaL-Wg)`OrGNPi>r_~%PK3=bU-ne6JHhJwS3qWVBrqQ zVaig3VaL=PBL=wsht~cbUeB|;-00F=%hAMZaBvnLmh*Xct=O5IJ z4-$-AHb%Wq7`V8Ah>pRCd9&~GQ$0tmya`?}y zlj*p8o~+T*S-~)bt^$lZkbgN`LN*bUoxizm((?j7--)_*y0OHswSNhcXc=jo^tMT6 zPFY)^)i!$Me#E9b_I*JQh$3sjJl+5W`$ zvUm>?*`Z99)O;SL0@LS3Ma%wBE4kmT`KBpc+_9^#R%OUs)0_r`Q}n2_7T7{&6lfhE1sY+Q@@2`uzz5A*fdBmG z>{I)dE&?z}fsX?I%9Wq6O_+jH%A4}jzHjMaXv?Lqr21&L3gLHB^!!^#qKlIjMyf!4 zVlT;Zh1y)pH7S82xf=dnTB`T$6LZzwi-&D5yK^w$+b;&so$;f>)HPL0Pj4L;(Kv69 za#a5nvp!caIREK1zwo!QYpRTQYPzgCFJ{ofnNP00sBd+lF<5^?_It|q?xxZ*w>h(= z6FzXD=!FZ#V!Krc-k?f{=381i`~jXdxcX4zu6XT^A;Ec#qZ%Im=RcqJTKv^dX%jx5 zq+6tEtf@CQAS?6c&7^@V9FtEz+;<>L!*!>_jdK>!Cr@}STG##Nup53ilLpKl(EjSW z&%d7d^_9zS>nenASlo(xQYU5fxx^&Q&hogEtycR2}1P}YikL;V+!1XPxb%$uIgJg`?$eN_ML9url%eYNH{1Nb zIPQQd*=Nk14oo!*klmvL5%h)FxO z3Y?az@(iCcPpW^B_cdppN3Vz-U+qxl{>3H@HOzFIHXrYjkOflQIy!w9bsRX2gGSQg zZO)usU0i%#ec-?mr;wZ(hgEv_434$}*bvn}%IT+RYpJm0ROEOiTXe!8qYr_7zT>`c zmcsfbt}OX=;q<<-<=HePuY{Ntj6h`wwR;LBlE>yl8X6ts@` zb=thyv;VT796*<3II17b5*w+&x-zhrGKT>OFrCL!bSxbl=6iOWqos6OyJX>=dK(@q zIcYX$-$I6j#X$fY=uv%gR#8j~!h}HR!@czH$B65Aisqg(U8gqw6u>rx%M}nryF(Q z0^*N1q_>FnVTwXnO>}hJgBl4T1C3}OVZ!5*f#%oTwZ2jSae;xn=L0`?vHSoh1aA58 z4i&-swAQ}xjgF@*Q0jR2CoD3!9kQwbGea~Q39`{*0Gt0J!2R}R>@)&Avq42<#cZ+{ zMFw@2+j&?LAzoHHgw-A{59xj?c!0yI`CFKhNSBmVMFs6pUzx2vrtxmAw;fE1+ious zKg2R4aqoUL1+7-|QW~bn3i#^s!{GUMT)P)#L`D91#MWu{7Z1$5jGiuYPG6=o|4je4 zPYUi_)K&aMp*X5_V~WA`(T8tz?R;CWXJcN@y4IJ%lhtM|bJlM-+*4gWMeXws3wS3m zl+M}5Fi!+G<_AY7a3*@tq@r7;bd<@MQqJ|dfYh-4V;o^lCIk1iadJ{!!R>rI{%T}pYo#=QFD@~^|LuX{ec z>5`-@e(p3if9wqFCyRpX7AhDdW#(78+2vf@T&Jk;phrQ2>1v0^&yQ`J-D;$j(I)$s z-JwXDzIr^>-!bb;$y3KqXJZGlZ%PcN47t#$h>D7;JlyzpGM=cc^P-O9TjHX7q6b$C zKl>x=ZjZ94_b6Cri)z&D!l_dR(lqHr1Fc;8N_8Gn17&)}p6ijle?)=zGaZNKYZvGb z%{X7bhAGpg4L^0~)UwAbyXw2kAj+kOyX%F_U|k?9t`r$gSQ+5^Gu+MOb%n34r{DGZ z&NGU{p#g||)3VRIIBzLjk$dv$l8u(n?HvxKzW+@8V?yaS@BU-c<)kfvDec|%I)QAE zFQ1yHK48Et)-2D*)3lgzZyvO|TuYCrPgaL*f&?LM_rW*dM~$ zt%{9O5SuMeJ9p~DF>@=eOYh#Zd_xbZx%PfiSg1Y531_b00DciCog@2Yas!o=f;ok3 z;zC2GvpqU2tp~gL2^fFP($L|fYR6$P6Zev4J%_VmN>ED0htfcy<@sB=p9T(4j@#wWv5pnp%N2 zaK=TYE2W62pDt6WR_MjcJ7-kkb}!j6@?IM7L%VuuvMv`U6hN@tOq+@Q6FYOT_Zff? zj`rMuMp$Kyx$`X9XnM)Whj;>vBy-Rsyb%;g=vR-OLDTNty9d>*@tVdoFlikX5PRe) zhc6QB4Xef(71n&H?-asQ)ZgE^i~dKctKF(VoE3@5;o3n1hVV!9pC@`TUFMAN>XP4I zCzeHOK5MFo+8gs5zK2RH5lNrOkj1OnzVOSA9kV6R=}bS43^Bf@j;jj=2v-(*rtwYU z_%)e^MnQ-tMA6P6PW7fPC-A&;nWc+NK)LpfH+;3^VH2yHu*FQq(xRfkPH}lvOoV}!dy2xzx z9CPAcj8PXrnle4pU}UueQO!akqBBa$8_8Oe&_Tfm5L84rQa(xQ9j?1)FLo|-=u0ak zfo`W=-@cqI>RP^WFT3UfTS|J3uAWF!o#N>~+N2sb)QoYdT?0lccnd3C#>pd{8vMZe z!y{uh^TU!eMu|K_2473=zOKIXb2#ftBC!fL(en@t(Wql=8d*alh=_-LQRTc8stkBg z1|DX0Y}YOW#8kEY4LL7>zGPS^!m(eM?gL9;0?oM_s8*iv>lat50dg7Hpfvv-vwF+n6>HttuyzAi>}?nMY|_x09f8NpQr( z&Vtzup2Ay^cv;bkJ|zuz5}Vd-=-x0F*=qkAW9h9k-MhF9hXKg2JNhnE z3%;DWUOi((_yvZMebK|>ERD}kg0a!Hg}GQ={uS8-dLQvW@VSBVR;=A9L!*RQB_i0a zOzlF+!WSlp=A-A|E3$8uIDxbO=s6|!HN#rga^HNLmy&S zORJ~>oOfq*}f4?Ya(`04Ne!G>aM> zB3iNpC0FPxmEm5=awBCqctuol!!y<=Td~!Z!R3-#5yVtjOiecR22nz+s97&3eS=B8MdV${h7cPgKBLHh!X7dO@77vi5Il)2g0&7?dZ9=Px=vxY|+Qjh8L`(pUv@|rtj8fHoOlvtiiQ$~6vi(n9EW@iE!I<78 zo)I*eLlTC9^oPKoZQLI3OCA&cdaP@C@smg2SN87tP(7->}H3we$WYu>0`IOP<|b@mJpcV*d_j)@w(6ms45q z{aEWk65wfShw2cE@?)C@(k*K)l(?Wa`TUwYT~ov zSzPsKSB)ws2mMJ3n`&*#bJ}@ys?Y7PqR`K)ZoiMwSEW-;-9y4`QfeA5O;Q>*Abnly zO#7BD^T9bb;pxP8Xh+1*E6D6*AQ0C}4s2L~6MwI0Tgh{LGc7zNe5n;CT$wvv=&dekRjD zc*=Vm6f_*(Co4@X@Odg9J3t>C`SGJ)%--haNTg}K=F}a{ zD_Q>8<-_0v^=165#woGi$S@yZMtv6V!~KAN6xtGoVG86tw1@Mn>-`JL%PmSOKmH7( zyh5UytK&^=zb2`P?!*R4E9*R6jij?0Kt@8SDMQa=OD&;3r8I~(Im)GqEeVe+K545k zbI7VkKS93uWu@L0PbLIY^u~|yY=$&y+LisLAt>;TAv}Dp`3x>?Cvu+^n|@8t`oA*sOtQ`Ke7eCID1KOo zL7$Z;YLvd4n=g3FH_5;J|6a;JJ}ms^*Ezf2`~CI_?k0OTLm{cA*=HCGZOpK|VP45y zHXLI27iTV&vj{x?_>{PSY+VCEE;X7gIF4PdFqcAuKtPZNV$iJ%a3po5qn7< zI7U1oiFSi20HPqsK^iXXg z2qe^z^coo#^2Q}`NSJqg6y;s<3dr__ zkLobhxvE)7DA=!LYPqc!9trBDX~-6zh&5_896QVaa&lxy02j0@+~?rIVEXqvV`cIe zU0i0~`QqRZQ7ammunkrIYnr)U1X^!hDzjMbjV7@jb4OX?Z) z2@sUm%OhE5pdrD3$G`xjbER!ZZdwI2FBC!LS= z!#Eb9G05gKhM3h6no!Ff6OOqA1m`&Zfu85xqD1i|F$78rqY0=yx%khGbdIRMW0Srs zsZj+N!9N=UBdO04^4Lk1T63qm6t;*2g2~aTXINm;Sg=nU}mN9rL`Z*r&ua|oP9faNd4`w?;waqiXDe4k{> zH-bX!>^wa@mesr}P|Py401V~#hn7iEZ=bBuA$4-SIF7}@yWIDtt)?0o`toX=*vaQ$ zw~GMA16P(ws}CI*-Cd=EbB|<_{ItbP@DMUlB91a+_X$IXq=vAzAo2pCyUM7!#KGX0WY;AOjl}5!|7-B#KFl6sPP9XUYf?_F~7v2C>_1G9nmFA{!A?34P7#pUtBkmWY;J z-2a!A*tZOsIsw?c;Mrgm6+Nu#^th4)Z&C+&imqnESfo0dm1{tth}JrB+H>&y@GZU8 z2PS1dD9uSv*SC@7?D)IgMH7xjfrDlQ3L`GhG;DHc^-T8Tef*4MDua^kUGsEfZ}?xq zsJxuDaow`ohmC0qrXgaMA~YA8ca5MKWhu)8K{Ynd#*3JF^k+}gc3RSi-bTmJSC@|| zjHv8W;*x=t5Rs!~x~;f3hStKg5I}4I%n`-2Mrbx}<$OZc+>Z%dCjzBPV+0x*m=A2? zfbumfrvrn1^=j`i62uwWW5hX_l^qTG$x|%T2HHQC>o1HKFh$U-yuQmY+S0nXi*;AL zYKR5&$fv-X5y(|7LK2zA)YEnq3{lGjn!lVvyv<*=@WFj`r5XVE2}=|Y5Oz@0JDIt& zzoCuT^v^Rt(DVG8M{UXbFup^)o@g&+n6c9adN1Y_`%;`hgs<9D-S=DWY#47~a0({Z36(IFF0S z3c};7OzSHSH)i0F3QOErQ_v;O?<5pK36IAd`h8`wW{Me`TdjJ|ElP5|mukPt^V>L& z+b40F&$<)-F2)qENE#g(Atre_aP-E%dw1*BO%iB;dTe^mn>#l^PL!!WA>VvRK&NqHz?eZ3xg!m%CJblrSP0#Ui6HmxPiJ&Xe<+ z@ia2y$IV=c#yi+1JmzO=4G)VrA-+uACDWe(gT$3LsbOe5MH9((f+u>x*nK#hfF+9P z9iplTr8JFremDErTQ<;NZagXdxHXZDz}xSw`Y6`rF`ZBT3l<Z)Lh-bGjhmlSvERD3T1mh z#zkh#7cwpcZQ7vCCEGG(QhHvOO83cgp*G<|o?F}gY z^hP(29(ccFD)`6478&$N4&ZJ1Mv!l*m0ywYg(r@$p*$Mb;rTzF^6VA!G>oi^U~jImlx12Y!Z&QHF5O8t z{NV4HxnnZzx_*5uiC5jd(x!6cY_?B&K7vnkvP~j0e@$7( zS&oeB73Xnq`^sz{n)YRwM6HvBFBIjnSDnO0P|D_2k%pM77^+IIK|SZ;7qLMv`4H8? zmekqjmrp)C(lGbjx+uS%M01sCB+Yk5HtAs>Y+r`z#LdxAyAK;eVq~*NSg-xQx8LX{ z%-UfV_2QUWe$Cr{4;I>&u8d5HpMN9iSV5G%w-H013*W|{vOhZMz!%fJjoX%9pE-PE zqu$^1SSWfcJUl$)+M`r3;`0fTC&S7(=0&UJF!@LYvG!{xMk7BqOWRO1eTX_TF$hAL z&xA)N8mA&6G(w%L8rz4R)H8u8czwUIAzNt;h0G2b#Ynl`glQU`al;?NT<@ z>}#=HLpMkCEzypZ+9J}+i(IrN+ULLllY$jj z!s@?j`cK>I>V75tYWUtWg4tQ*@)4!n5ThZV*e!pXwZ6mOoeBUpyv&_@%qGPZ4QmPg z%v=HyXu4A3K6j@1SVMHFK%DN!KnLm(8P+oR&AKb)rayqV@g31i;$^sHx^kT)`}>tW zOZm-kK<^{y{$8#No3qiPW_{T*Qrg$~galB90~|M%8Wb$OJ2-TsMyOo(?7 z4K0E!VYYf6`j%`^`XTxeqq+_J%FrtBDB$Lh_-*M@w-RwwLdno!M>V@S#?#|`3L`b+ zY^ood!0GPn*Gs-tWrIJkU`t=e0lyz@({@Mi7lk+lekeIa2lI2*pz#qK;=3HBG|Nm1 zpTBd=$P}xQqgX8!{{&Swtsj7XQ?zdBw{MsAa>Dw{6DJrk$=RU8K|DYr6zx3Fc%_Bk z$=DJ0PX);g`TOzvMq8q6P!j`YO9d|BHl!p4E2@@IP>Ax5wDW21qSc32r&hbGwM3K$ z;ZQ1uPv(v90e_MCOndf8WIyH55iH5TM z)DrQLOpp;Ut8-^4#kvdDCN%h6EDxRG`ri302`oE!V5PrzZLFYp4PzfQXDxFxc!NwpaT@R$e($ptX4UWlN- zALp0OZkc>Xxj8cwJzPfL59@!#mn)-+oR_Yhrm@WWPRENeozFWPZ*e?y`n0*(vE%(G zX+?goFu_H$vc}x4D%$+*8oMszV(Jz~J^Ot`+5M`*<2T}Bzg0uzb=Di*%2d#DxAVfI z2_d3WAu~40hI!4}voah9S6bR~Hm!S<$5Ve_Y(?{Wr}W&DYn8Ch$zQ3~p&<W~LlO zszAS_;IBa`GDS@1B9mElejTRkw)?ut&i^Kn981ArW!GD4%@jVds+t<)b^YQp=#ton zZrytXx88jW#gS4T_(4e4Tlm_gch9bpum@>AFyXIrKL(-&CqFL{aB3M61h5MO{HcSa$1Dh<*u+Huq6gII}RH7m}e!8uO^@-4Blr; zp#)-$Z>-rR1e(;yGE*`cJ$rE6snY zP_y)Wm#$rxw)betQET`3LS4DGU4Cq)Uvx*#6#ENn%JMRU9%46mQ}WOwKTd3hIQBxz z+}*zs&4QoLmwk({vkwWqPc8lSvhYXzYw~U7OgIm}_zg9$RGr2N z_fOkX38a?W1N(pAty=Y;kl_0v6Rj&i`Vv(`In6G}QjqcF*ahK+B^} zGsRpWbNO(%)Fex=or??|LybPW=o~be4TPWDc`!61u|u3*5nI#`Sn%&AwF;ik0G=0v zSVzV_2&jNzcV3;0K}1Zf8gX0``dujxQEm9jP>L;yCVn>W!p3-cNL5>z{I5X7)m+uJ zTyC$E$!7{5Ddw7sqGF$U%VVylF`py*9f&pieYXgflnrlC8cufDJT-?yyEsQwiStMs zX_q$wk;k>=ddk=rd1%vR@Q}8aPl#5w(Z>EK?dQ*N9B{ctk~P;PYh)YWk>;N!3?2H? z3I`jQ9Wlp%FeRm=oUWSu(@#17mC1g>tx|cJ*1_Sw2BDBh5Bhfv(N<> zL_2NQ(K<-k+m*@GWxlT#zB(GYwbuzVK`eUrUh!-m&)*>eNdOd6mK=`~r3j(~nIq>n zo=?&5jZt>e{5tFd;sX%djHrb{2z(3><#g$9WF=1BkH7S?m&X)zrcxHIY!f#mRHsbK zW$7vpKqm|ksLy+KFhkoQ9xk9aBHb{F0cSJdJ!x_5sBzC-`QXy8MzfMaQMwf24ijk! zhk?w_!_#g}P!qdT)Xp7${yCf9)n0o$pyGrk!}DEwNsPDIvcc8!Y;!_Yw6^G0tOn{F z&p?9p4>xxSy%fm4D!4Sx|G7*?L-}DyZ9}1#_nAt4eSOnR?U z$R8Vi=@ez$GC)>Iv89&BS0t2hiHoZkw@W}zBQ~o=9D4PuzHU~@?qeeIL*|mTh&7xh zt5%q7cKfUbks9DwBq2?L%J{s2m^A70azn+Jhk5nd<;zDqhhJMoEfziQPKuVd61yNh9kvW)Wp$yy4uj>8v4py?hvyVaO(|Go^q>2gLSSmZxC_(N)m=|V#NU{e?HKf?%iM=OwvC%| zyTPs|>$KOFZJ)>AOZt*@Y z%OqZ%x>H3{ejOjH2aJQ6EP$Q+RmX z-mg`3>|WrFrP{NxEPI!ekeQU{;3xXptkB$b$qx4RbEv~aji(eIyquIX!F_TqDd}fF zjS z3VJ_qT{IX1KznUc$ISV%D_i9yavm4|3(#9x#Q22UXik~1(+^IG3A;sLQbte~4%5*C zVp@A}MOEY(IXeGhtTV-HDK=WR4aZ$^v9Z<+fN(Ht$|#<^-}`;^l>F*p_A{E$l zqjPHBjaJWpXn70jKew!tQOx!DNz@rg-TW!WgF-`j?N2URZm}vW__g|jD5lHs4~55r z^p}uqGABMoy_JHx%@~`(9nbA*dHJ*ZTNbSP!ZUYQZ=?7QEkk@hI#MYJlrE_73rK^1 zy?*nRx;Uv6Y{AL#f6}qP3x~@wg?#mU1ZcYvM&cF*mGzk zWoH|SGIQ51&$u{epX@mX2FHMI)j$vcgJc4>bo^3PV%X|;d$VgV$>LbL>LfoO84N}+7i3*j6Edb z^_R`rQHX@8dWE)z{&GyDwl)^}yqD|SRZOm%dv@+5sk{hAHJ-og6%Ct2B$dcqD=#lE z>2#>)=j{?xf}mWl#-RL2=NC#IEd_L5&u{M6O&C+IaC@rHL z1>M!$Udeb2nOaXHVHBfYY=H*}|NcCZwXHa<=52mV=0GOiY>_c^*fKz@52Q2|(*nsf zyZoVSo7RBA@+;u9ZXkb5a!7UA34r+w*uX-;4u*z&PcepyBJ_nxcjWz&^usnVEjl}V8rm(C z;u$c+VVCmBTRHm-ci=WLl2$)o7_{X6#xiVy$1ftwVT#q?N<27O@6$BG{+@6^S;owx@GQn}MH73*`mlJsp$)ue z;Hjnj^S|%f7@uczq)Gx1K&4Kgex0+-ce+;8?Huv8V`(>bQV6+;hX)X3Fj)#m|30yM z&5|v_kTg0J-r0^x-+%mn+l6q6jvA(504k?2H}DAo7=Y!3VE^ZovAzy0y~I91p6 zzFx2A;qiPt9?vmPh2zknWqKb0a+)nY=^-ua8Da(ToDZIzkej=loDq7`yU&I7lBp1*5*W&pYnAGM8nu z5M}>nCCHL&S*2}VtOI^6>&`qOV2ro`(jb0ZT=&#PzX+U$T-rwzk1^eG`IqAXw_@QI zp>+_sW%zQXY$a7Ft|zy@Ok8q~nqYB3!sS3p_auw5pqJp*Wq(|lHKfEYm6o)- zj9_v%X;H5>H54|9)O62xv%T46zi8B5x;*BpdAiqdmX3jNvCba<9Wl>Sq9C!Tr?SQx z;Q)?~(}}6xRUAI)x@NkE>=}Y=f^XNbpTwfp07Z@3+=xI|UKt(lbbq4wy3m$ITwhTd z7D2@?`<*IJ-a?5h`adxTUCov5P?=x+Guh?_yv*6PPrZ1MVsQiNP^sHD;PYjEO`|`E z7zg!zx%&6F{L_0ogbgH{lc5jjR^>`Yj@-bxaR zCs3Mx7I=-gAs5zjSM^&pf;4vjmLgF4(w4TcG)6M_44x4h%Dxt+A*T{0fEz&$R7%n^}f~2lVYbDQkDhUt+}r2GW1{a1(NmEYh5NbzdmR z@r)8`5C;%$UXqosYXi)VTjNLK+zyj?I`2%aSF&iC0HmaYJLV~d?sSe~*ioz#_8iZF z+owA5f!*Tf;X*c9?{OuKxU4)AAN{iWH@H@a#%4s0N6@vrys$DWAi^AmqH3Nb@EqX- zgHT%TFA>uuv7_Noz_~n}rG91KU%2%FYzA^j$=Ly0OT} zgv<$>Fpr^=2v_xYFuIu;&JsRFS0i%1aB zUPYvMPytNy7okhy?D=iL3CALLjL5yq+78o;!(A9rIBfNXSp(I=MhC8`(M4kj>>Mc_shHa342 z=>)&y0toCT;C2^}z8>Md%Mzi_&NlU^zyx1*H=0+>qvDl2pXgQ*h9`$*qgB`-sRQW^ z&Ste~)5esIe}9hW#ded;jsh}O!>BO-eHpNeglB-M1$^&oA6hmeUxTKbZ;K(Xu4O>r zajktih@ukl_WRuNM-#8nG_oZ)k9S;~%P zjU1&9?)(c|`raEWH1+0i3__>%VtHak>>V+v)GMUPv70~GGWK%2y6xgfCw`;6s7Z3{ zFsmO{?Z^%=tEW{wAOE_}J+ScF`~7iXlP(|Ir3nx5S!0sZ{oi{%oZgl2n*8Q@24C#Y zd_kPC!F^6t2e9db`B#8-ZU0Nqt>!NqB>FfhH&hpOz{+u?uwgT6p>f$v(^6QA1wAV2 z#S13?_@nGF53W2!(9M=icugqXV6{8=15Tjue#dij>L9XY ztTt{9y^zgzg4M4xY*jrmsPi>WrE65}=YenaeDDvqYfXr@X3iJV5+6gRY%D{A(z#CX z|D?0CNb*8Cw2oOx$weOUd+8Wga0Hrfo^}p|7h>{Bi)%du!=`~;lrv+Lc(G!*G=BjC zG$d?gwiSEoaZ#DkC?N{}3cpy`)>3wKIlmRY8MDwBhYISgQYc-+s&QdB8<9X|cmpNd zw9;Lqf2EVz(xppxKivu-NUL@`)2!MjE~}b-)`aW9E>^T{HEcxbQn#ULU`B7ID#EI& z-rRHi-O3x;e5&(anac=(5ryU2ve5KIP?S}&u(^=zNP;qbR41cbIBkw*T8dsIRcSgw zSutYOqR~~}TeM)q>6b7XG?IS!Q2HCZZw@0S@(NClF)|waCF_wD1GE>iD>}0gnuEoo zP@PE<<6%%mMbjpU4m5QabN((^{1#L$eEvcEqdM16G|dF<*45>PNkLW|PDI zxM`3Dy)kKGH>Z@M$n6wdH!2hO(rk$0HSlZ~a26~QmxdPSarGB1n@TekXZ2BM{Y2n7 z)e3%-r+w|N=iyZibxg5yY#egxuqN1cCSp0Dq$Qe>$9FKDnU>{K(r|A++}ssZyfxf&2lj$8gs@9! zTu6sMGJ4~>4{keNd*179oH~dy+2a`Gx*(>Nwmd2HS!cDaO^2I&E7bSl(P9;#cjxYn z)Io&k0N4tK>N{bIU}>UfyS%5NY(SqlabN`k1f{m)6Dp2`%1=siWov}>-JBx0jUgQF zbPA%(COR7e{>q=t6PG@jHGjX3%a157v|MZ$3RPH~e&uoT?u6-87p7Goj@v;_m<)t_ z(!uk|)@ReNe|?<-Hat+%1NZgR%t0yRa}OQU4gXKr!HA;Y?t($*x&G&&9RW$h{TD^> ztNp+Ay>#`eDZ_eMRxD&+rytDUpA)at+@|b}Z+~mQi6fkJq2u_^l_$o1!$$Lk^QSWX zYvMvU)yf%JD_oN=uf|1B3nd%9<2M(xRU$wvotgkDwVymogV zai{jaunM$EqzC!6&j%vrH)*+p8DE?u&p zrmhAnz~ahRR>BE8=%QDjhyQiEWtQih{51kGfPB zb-ReVZ=1&SzB4LKKV?b z=YfHWX}o{KNjB@)CX=mUf^D^qP!fa>RT0Q(>n6htHktqS4;Wjq=3VOWGMJ~S3J-o? z>wrF{h|sqMS-)v;{NmZOZD{#>seIkB z;}N`D43foBJ^DsgR(kSza0i3r9+-R0x5ai9g_UQ*(=|%7s^fDAgFL8k@wmFoz&v@>L zy;x4pQBDvtpA;3s(-7+D;@ixWFV*pOw#n~p(CUwx?d zqgTE<&Vjb8?Fv$gj>HenX$Q8X>8xc-;JG1-AfFdU3EJd>4!LoT}9|BUBB#!#&&8h_)&K7`Z8Q=cWZds|+sc?!r!?^UtKcI<67E6h0i zlSIM^lU}!+vHDn*UwyX_JDlF~CG?_S5ZDObH_I8F!+$Hy zfP+q6)UK8ppOwQvIusEG^jLQ9BMih0#k+P+!?-$%pL9=SW3oVyI;B39y`RJ8(*Mxl zU;bwPGzpF{MuRKZiT>=_v(^PkuUx7anoI?zFSd}?94jzM72^O08r;!HxtjAXUP`JJ zbTzW3v9czkewEWo1CVZ&Y*vF z*5W(~_?@n}gRjGa`L;Ma6t5U;HgeL`egiYOn2z&nDZH76bx2%XRq~2DM{M@W18qV1 z#S>I4SMY48=5sBj%a7KH9eZu6;-+Y9Mt!B#yZ0fh$kgX!=WTHVPSZ9vlXFQ7v4eo5H#*|*Q?QQgy1FSdYG%r?{F)(wr`3MIJhxL0Q)67ZE1L?9?qo}E`q z`xNlJJI@-|;~hlrR=y_*Szv1YK@p5sSNrr~94(t{hi)Q91&MEDPM&y?Nu}+0&t4kf)p+C!R!f!r12rzdBnwN~zQ6l51K*$ejNBB{jiVMZRYF-z3h z+@oBpF=)?(Wrf{>L3mII#1q=B)XX>*YAUC1+kj_=s2qj-{J1CP69u(DUilKX17wbF zbs&@xV%#$NDJLju^mk9$fH5R`oobi=0E`&c<5VPIbh@gaESevD>e=r>%;XKo`Ey}?-hH>|Fxi)_Bzo&hxE_KML_EGLK2brQtcPcrVeI9dt=!r7zz@pJ%)v)l zq;MlH_|J_VWGzmtrHXZo<%C5i_RDbLMNKDTu`BS>P)sFRefO%!s6QMa7WY zA+x|cR`r~>>#%3le!B>k@&vE`OcZ?C@k?e?h(0E<$UrW8B`jGEWe8I|-xt3Ii1;1g zK?pp|YMTv3m~3*^WTE2Bw%ZyWicKIw zJ%(U$%ZK_x+!R?U)SH9nJbc{cN(u_t;kbxX7%@uUE>m5iupF0O!*P;bOcQ?TQOb;L z@gJp9kdx<^0)!xo!ZvK$Bqq^^$KSODqVS#&A|ut!U7P6!Yx%D*v7ZGu*@WG7j z|BR0s``)=Io>@c+Ed@sYGY2QEIoX7E-Q@bpB-vY<9fR^-wmic@jqTE32>Wn5H3 zM%0AzOX@~noe_LSGD$!`soKEOfWF?y(+FTau^MN3Jyr(^yul{li*TxNM73lW#uH!HxB;awvy$<&7cVSbPLnMK#_CG`HBe))eBYRub zN9H4-Zl*_D-ZH_OE~>?-zGwyvWxS3V$*&#yGg{g%7&7HjcvdC2K$ySH3Vj#>eo0AH zN%hpvLU*Dz%9!h7=TV zn$SSkGwevhn-|HTItKZM5)_bSK4BaujM?tS7i4r!*Dhq}Z+C{?j3S1tDEhNsHB-zG zY?Iu=ZU6op4$Hrg;SPwHSy4(yn510fGU}*-F4rRMkv4J;Wc^m?)83$&(sfA!6{T=P z@!-|lt1nz~nB<8GOy7uU*Z*+?oQC^PTxI#zkLz~D>cV~3W19kpRqKvQ*j=jqtpCjO z*_!lsLc14#+Wq_SBR&k?t^`?N)zbjiMI6Xgz&>JH#PMdYI{#=tFb^BjlZ?kdwA~HZ zctmE@d2};kaFf%O^=~&JavcEy$p{^RdQ)8Kgh*#*^x2lL;zmun@N%Ac-k?EQAqn>t zHWvt}vJp_+ZQ0v>Q0%0catdi~$dG3nZrw%NBx~DwT*5>asTsI0H`zVS!K_o-)EPKF z#=UW;O%ebA(gaPrY*a!x%CI81fuE6_Sf=Jc0$nqK$&*!y;@?r{K>4;n8eB zZNiR{ugtcteb6T4@eNPw!U@*yce10*Y=1Ew^h@^k*6*xRzn zsA1`y)L!^B#U5)K{mYnJ9beb4**HA!^V6wmJ;Rz7<5$r9pNZk$j!EH}R#aXxvZ6Xo zEE@fHKW!S6K*|^zo#CR9XGhc)H-xp7Tl0bl5FJ)H<{l!N2)M6zE8=sYm_xz`&b32G zIZ1@{OpG-=D>QQ*)Xj73$kgKM0c>7*++)-Z0>2m*bm{UWv)<~`ix2i@qdtBwu+X+( zc56cPr3jO_!KC3(`M4UE->v#C+QDVihw(9N)E*!F6jdVr05X!;h%e_PZUd3_xPuK$ zVdzJyo_uI-Km7T=&*B%^t^e^eWs74n(O631k~y&37!xA=J6?@IP9`~RLO)d1P{KyP z*H#_)QE0%b6}-~NGUz9<%M2i1l+Fyh^g-Bfa066x;B9LXRo3OaGhC9_a&KYf2NS7? zAE**#zZ0dBc^#Eegs@(KhQy8qFcl4UUtv{|drcjh_t&xaSK+6ovQ1P4%_f;UJjB6= z)E~0L?~L)Lpilpj~Ap;d+1xv9iiFfx877N-4Kky*t=h;~6VA&-bc&-lWr6>@oB#On|tdS+KOJbkPiIa)brt0#0y z0?p3gWTxC~%}6$W`W&0O|^Qffq}GVpI1#t`dT|oYt+9L!DoJ3RrjP{1GY5oZe3LFIZ=m*Gd0j) zNxAX9-y3>NaAiNn2nMft8b%|KbjV(l7U}j~0{_inZuJvo8*U{BVWwXKyw5FG@zNs{ z=wrVVCNf(g8Yk`3NL$tRz_+*QiDWhfgS1;6t=~aPRh>|cQD475SZ^Pkmw_LS#8hB- z8Rg~%t9Yf#HdRU&H@7ZR{#w;`!Va#4(7n^jcKv+BAmn@=d3!kO0?uIPy2%vzu>D6p z3=C;}58J-`uuM)6v^CT^=aW>u>mk!9a+5zAW#+o9sZ-klH@{UR7v3qu)?bzd4XmJ! zgjIU4G>4rPTDB?qxpudsS2e8AtuH^>OxhXGTA&rIp@1zM_Q^Zc``u4c(ay)ok29;% z?|wsDOW>A=xb>QRGQR1}Nznv-b~-UWem+68E9SCaYwEeaheh9i2YwO1;SKe>Y>UWV z;3qS45lQqj^)7usoB8LzcJh4%cU}kt;t^5@4OF~n%FcINymXdb$Oq@scVF`p1R*n; zQ4BelCFpQM2YmC*1S$#&VhSY}18X77AD_8K_HqEY%StR}w(b{`kql8jjT0&ir~ufD zESn{dqsZ~sv=}pBfW9!OKyZPf4^X%$IUuq+?{Mj~ZUMR;rn1ckGgEObr9mVHX$%=+ zM7cQ)y9gR!&FI+PwSVw8B-B!dMTtyn`$*=7FG}I>e`!wGvpnrk?;=!FvcOo}C;rxJ zZzIu&uPMNT;K|_`+dXW40ql*Db=p{F;`4$oq`69AMo05ADru~W-75h08o(JP^ysV- zVkECk#+oiXwnhIwL|ztJaKA}2Y=@75CTUg_k7;`I3R2=8vCDDG6L3BNgz!34rLxM& z@DBP|_-#?qIpLN^j|Q$t&tlcVG%>sqxr{I=cqgi5QoJdzwLAQd3&28NBTO0kgwi;2 zWKyZg8oF7(0D6f48SAMqh#vlf~C)JAB%*c*m_Rr*N z9BbpHOWh=%$a)7Z&l&mz^wx-LDKV<}e}x_Uprd>*?1t7`WhW>MbU8)<zKa2R`13O?`Y+ZXEKT7*&0qG3i@ zkRQe-kFuvr-D+}HbLhQxZE#8N?;WFf$}$`?{cR{6AF~m0y_I%?_z^DV<&S;MY_5ZI z8d?=@_c)6mrv0$Xayb}H$!Z09655#Wtj2>g1THK3eDBd8ea&1$fiPM<4i zH0Lw)m2+uj-SlQl6Km3bkH78`U*C#TvGvQn6GIy^z^LW4hSC9Zq2+&ikE?A=Jm-ZC9^CG%gi8O#&kxj@uX9m5WZgWJ{(SvdCCQFI^|jf=}-GXV3{> zO|w#_k0+xsb8b$1tD724XIo-ufkq*OAs{&P=%YSCKQNz&Zn3XVsQeM$oxOf`+fC_D zgDng3!ni0FEKn`B;7}nX2_HA-X^8IkJ0yDPeOU@PrrqNB287^%6NiBXRD+JmaTO!- zD;FHvPQH1svT{*w4vaiChxW;HGobPx#Ncp79+pznqgqR*5LUFs@R}}6>=Xw^eu}z~ z7W4^3_JI}m4a45(MxJ=-G0y|+N|sa`j-h-J`7b$h<~}L`ahaz_n?fGVlqw|AA{(RzmX+nM$Z8_&crd(`C0&(m^b zBcq|U*$%NgW6oreC-h`4aK=8f9i>Kk)5J$0`oiXJ^US^!yAklh;nle-#u*ytQVO}V zzDNujy;B5K)G#vjVb&)zv*X8yO!jYevp+R4T0En*P4hGlSoq0IY%YvEF_EKY)AXDX zpT5NS9}67h>4dm$2wc`jmVrk(+%0T;IZ_%op|kM$vEZO+{qsKh0@hQttH-AFAxTHx zbV{?+T~WP_({#VY?Pa;SGJ&Kt9U?(6ln)^+<|;BM{AwWB!aGj8S~|@VCT|EsIRTj= zfU}irWRgu>R?nlF(9BtT{fl3UK;%odEk=h`>iv@+o!Mmy^6?;BdK8+inPJ?8!&7lCFAxW#r?UjXmZVu@$z z=o4dAf|tR{7f;K_RCq&M{Cc{n(L=x#W?aWgpBw4@PxBZvg;%In0l{C;C+e2dSXdbB z>jY3rf}8B$Pd~9Tv8?;5tr7#Zuim+1$t@7}d+=aQ%?$f|j*(i0P>C+z2pr@>h?eS) z&hUz6xK`>#eVHDl%G(b zJDKrJFCDh~XK6WJdE!l9-lL3n@!5>_x&@C;5VHz0V@vuRJ{XbkXpi&e$rTT3raLJXjm+l!ACahGrDqwd|s95Z6zv5{qw&h#yLzSYC?62L99$Dlp%!Hc7@jgB|VL zHu(0T0_EvRwo+|Kn>thT7kU2$a5Pmb`0SHP5|fhT5&@zs%TIW99e^SYdmH!kWJ1~8 zD}lO}WL%lr$w)FI2A^M1vhXkB>aR_`+2b9)?d5#&r;$pd^R~lReb*FXWffrZrB^#U zTa6*7Y`^#-*zTIT^DI2m^RB##yZB0bUW|)wVUbnA!?|6W8n^8)DLQA*CA&AD%2*7V zL6aOlYYk;%qA;q(>@=g*BRYr`1jb(&)cS5T%zmRA_5GTb3g`WQ)7IMVz1`ZmJC%Pk z2PC01G;I1krhlh6zvp4`*ZgnDjRHBKh7 zm=OFnR*+a3t>ViE%I6Wh&OoWB_7aBhO=>!^z9+W|U?ux20V>!4v^4Fni1fQJ>`#zW z9y7`HPN50$ao9}F&1Knl3~`%CS&GkH;NgqYo(#*iX8#>{7P8RghPmCrJ?}@ZK5KN2 z8VwrS=scv~i_*GJ+d0JuJ-Nek)Y1IcN7sT&!yXS){Q5x8n@1R*h<}Rk zB(uPi!^h1S-0+oYE>?|#w~71&TF7*A)a7 z;yq7GO8Noy?+xmC?*!$w)ahcP0oq}_px;kA&Qm*+?toK8>UR5BEkl_)n3gr@z6|Sn zZ(E+B75A7ed`7$sjp8cBx~>cLnLf+tnEGAluJ}*3NivgT$R@_~gU33%yZgt*xhLEj z!s2i9c3#c`7+}WmA2YKDard@#8##r@c_T<~^{V@ih%1qhwHDBRDXhN@`z~etze4p* z3#PNT@QV*(=KK>OCU}c3rIx@*n>&9uvG09>;rY1uc5r!U&zJy8f3s*l=Z20~+6 zzW%vUs;D0+PzHXt^x=aChxjgG41@#@2lxz zIvNK7p*Miy#pvKDXhi1N(jpqSyalMjg%F>NM?}ZsObNHfhTG*{56X zD*?qATm%0xUYtUBEQYp=7Db3R1p?CyNFdky9&Gu|yRoOwn!Wxtah+POU2$>q>II*K z1-Kl&oSrdam8SEWBR%?VYN>K2YD$kw!=gRQ5--I?)Qr4%HX&4bfn7jprt zh#plu!KuUV4jD&?lI^mfZ<^IJ!ltXCd!T}kZS5mgz|0U82xnz=ySw|H(_YHQFFrp+ zM<85Ib9fEKt@q5CGsncuTt(wL20t~z_2R7@uSU*C$8OFS`bj7J*W|aZ176P0A{`p0 z+Uq_IS0?_oXi7-S2@6)O*FE}RowJr>r%q2^-v3ROXBh2G8|R*t+2-lP@W*>2-&iJ3 zOSrXi*^~kGO>gSg>}?eaob8o&6&kR-ZT5_bSxR3n8$)34ubrjahZZe1!c&e#*R9{Q zY4{)M{Gr6)q!QxtsDlGlAwcj|vfhRN!{yG*h!6Ya#Gm%px?gf&Dk>F@R_^ zsexo?wsb4Y#y8X#Hr*0heQ~iOs7`69_J3glnqQmeSn~9#tR?-dZ6#P5JjG~_&CmC# ze0D}h2tHz#LuBgJauC_+9lpXqt=noTgYvAK1$5&t@K74{j_xx=W*9!TS{Ps+3f0Dg=cxvb_l!J20i|+F>TyFnc6@vu zUVN@%vZLFg`0quVjPLJ2PZ|KB*oYl9w!@>NMvwaXf3b?Rf`WolpF^MmS(UHTKKTj| z$aJ=N$5v$YQYx+CvWRVzULmA+wu)Joj-_#o#1y)@2SZC}v+ThMw+vL9(6<}Q1ZddQJYk58XxjHf#sBQY zxH%e#;%HC<0aVx}c2_O&^`us`Tac8HAl>hX3mw&#U|nMI>C^~Q@g)YhpS^&KCzC%T zDVVBY$_USgdd5USoUOv6K~{}>zHE^qP^Fk&(a@OY0Q4TK>mJi@*|KG_)nM4=-DE`| zqjal!khUQ$d<0${cFw{tEaWdC?y;F}Kvjz<(oNSqx_gabMi&3YN>e`@qV0ORwyNqn z-(JmAD1|iWh5@x?HV5j~Hpf&a*uAK(zKKkhrGj>kZIf5HCj5RgKJow3qyPxtJhj}# za18FiO>auZi>oORNS^&l#CDP+5x;b)6IX2XJ$q|EYu$Qibibc>$qIIR9-P3%*$Z^d zHu9-G0iGODEss&wPSRC`m>{%X&97VHX~KZbnZ6 zTcBCPiW=tXqZNSm$4gxv20o*#z(D9m);KUb29^f}PatTUI`o91Bb;TLxJ8dwgB2_s z*TZ8}qBlDR^yANj%8kR8YoiOH(zV~e)1n=vbQnAD-7fN z2-(v^iFJIRGtLi65Ex6Tr9~Li>rbvjDcW9B2iZ#l#X_s9huA*fNDvfgUf}ZcS&f)) z6CN$h+lE>s@aSZC0^n#~C+O&^u^SA6uqL7!N$W8M_exFQkk<939Ag`NE4I{Gu9@6s8@4U zECbe?ezoP;U(hq)w`f(>0h{2&YuBY4!E=;)*$G6;hMgVZb9q}3AK6}{WK0vrXYbbt zlmag^sk+tEU$s3?FBUS{@)C8|^4r%-^B{$Y`I~Y=u1ITzD5Eg{*2`chE&|Hw+8AfU z1sm8;(PG|vs>Hj@X&;X1wg5xMmBj#W-<^#6w4-6qUiQ??YsZ(Q9SJEEzQ*0?cLbWFc0*CWT zW7|%5@7@)8JC^)m2uKx7EdeJJxwl%g2U#Z`=33klDM<=~SC=VQYRXB29bj9fsRnJe(w@@xR-t6|aqvxz^PhJI$KfT~q z+N7>^6ZM+i-mc8rKPL2`t!Y8kT-!;7R&}l7jA}lVq5t1BB-2X`43#(m-!b5sO12ZS z6nZytzU6#}@P6UI4ysM0kbb+u9Ca52ZFK~z;=Tdy=YFPlXhI53vCgOOhsW$LZ8@fKX#%`UAqE2=m_p=1z@`vl5Y$lcCSSL+0f%0;c(T>=J~g^* zZRRKW?%pkS(HQQ85s;d<7HcycQxX#!otGR zK2ax1X$eedT!L@iS2eq_st^~}82dE;3Y9b2oBlm~P|-+3wJRZ0wC02Ow- zX3HQqsDN4{eUPx8y#77=RynX^*U#TVqmwlvNSX5DmmI2O2$YcI->^L!IE2u6A#ReB469)5jTh{?{uwena48 z8FIlK^-G~7fqQkSu+WxAFJ_yZD5M78>7nAKY!x(AY5(lkcU*vfy?zr7GQHR}L%J2HEc~op8bie4EUM@ zFW+Hijr)^5wr)Iexqeim`qkF0f8WsRhe_x3dYpJ}Vp=z+)pWy*;U{Ct=Ol)!+hs2` z+i77vHK~Vz!!HA;YucLc4&G|9^nl6m=@UP%@;bPF!-fOSyJiNCU%9Vl?w51F+%;|7 z(;SD|;-`s`Whtv%+SY#hSHs>9T(^++1V2-FO|FkFcaiN<$cbn&u*wQEKY2umv4A6x zelKT-emo&~Hx0IOI!1bJCjAS`Q3)|2WXr-7$l(Fo($=3F;HDtwf?K#?1g>SIv{o7h%yFAv1vfS9Ej;_ zuilRk8f95oS?O@2PjT$+K`oP41TI^3H+2E*NMrO8S}B-_iqS!*xpU6#d9({F`x!E3 zOCx?ZQ?Sl%+LT}Hxe13|3IMG@Cigf!*yk*UttZG54!6sqz{tb;yq7O{m34UUYdE2= zfUq!}*?qP+VRQ$TkT0db6tOTy&{tS56(H=Pa7HW_m!05HSbg9a{u){}> z%9?2)D$!^vZ+x#)^}!P5o^$(m1MCT(V#g)BoB3G0TF+z&7mMP&-+$;#Dy= z63RBKU25+>(+M>br@e(Yy$tYJkp;2|qty-=OK%Y|sg{9?@eck_=wRGFY%R9xEP?VpT#NU3X>G!Lt&OdG=XcafPgG_2L~49&Lv@2X;TDvX96W_0(Y1wO|{mp zTPMQ{hQa-76O_)@k^|{#gK01DN;G2SkujOkHC1io#Tz$XYJB-^qLmJ z>Y~F@^s047P$Hsnngu>uys9*rgb@6;U7C|v`eIC&#;uI90fXlWfQ{^3%6F!Hiqoq+ z#UM~7ewoYjYgUkB9hjrCgLWFvfaPJ#tYjM(T>DV4)DA07+p6K+1v^|w=I(BZHJUW2<;dpydOh@Ls2=hy%Yt+vc zMOEZanXsepn7TWy9pwrdf;g?0*qM{~x~I?PnMf6e-B-k`J%E29;5tv}24%UBUP*F3 z9T|^KgQnn@xhE6OjUy__zEN$)rs%UfZxN`Fo~7>=Llhspj)he+?Lu6v6VN-q5O+Kg zC|aIcR4^KVC9lQ<<(6zJ(>Laj3q3?X3vi}}#@V*2Ec0(j@aE2`<3eGX9d(xRwNNHJ z89;Ijw4zwOv$gWnizy9Hrml7l`!#sztzbkGIMUqgh;+0B8&netD5>7lE?n>*Wd=4Q zIw;;#m%L%ahSd zlzTGN3-K>`rN;+!3vUJ*6NP;vL3 z357&b=8YF(1jkOMs~x^Lc6j5YdBf&j&ZMOf(v_9sBTId*M~(bCDx|_$$`&DHp~HOw zq>)7H|MXdRzV;^T6-?$9@~io5<+C`wDtWq#%9d9=hH4lQzALT)u`Xa zBluMpV`<~zl~5n`Qf8MvTPc)TO1%aB7XiIYBQ9~ddM7mk1&HWTJo?TlHLM9xm{VC7 zt*Mdv9pDQ&(4x36#MsgXQzoiAU!BSP29osjS>KxzK2iLHLK1CPvVO~!TY%@4`5Qlu zxW}L_2hWz-W=5u2gXZnS(xk1i++(GN;1!I_x5b-ZX#6L7hS}R;>IftpsWw=D(IcaT+ zE<^ZfQZ!+PSK+3e53D4rISf*7-q%St?od`-RnVmABT;>I(Zh0u%Jka%tf!A)7*>^cb!ig-~! z@zsQ-0J=bjv-;0?1tf`EK^Qxg#tbh3$)8#84*F1YdDt}}yWJ9rjVz(3A&Q9+J6Q2| zBUmha?IS0UzHQDjHM;l}54TUQ#+nEVx|^BqJhc@BLsz1Vey5&~_fs*6G6$>LqVE11 z95-|-&PPwT0(Ar*f##huDUgVD(d?t;mERh%n5wMfV4HzR4`GtzwRU2Tpv}#^qr-}? z!^p@Ao>_Pq3I&=JrN0A1c}OGSv`|8TLJKBD=4fto2c7dR%|D)CwqM0B$`kQ8Nu-H5$*LwC!%mQY#Euwlavm}COTNw$;0eS0xynuuoA zf8FHV{Ta0~m~JeC3!7N9A?D$x5V=Q> z-I|oLRg`Sj+Z6!K^492MJLf^nm1rXrX77(rfp5Xe%ZhdR)ow4n!2MO29IlZy8Dij5 z+zrVbSp8&TQ1fD{WDcA_b9%Mo-<{1fFM_{8qdy4;57+9KE<0b~0&#PN5R*do_xTpVX?+mPP2IvZXKZ zE-Qs>&fLis9*lPRXkwXoc>N>}XzlT%qe)H*_;b*B@umaRALIVLlXV81eLe>IcJV8b zotv_k5<*WO{l67k&CZUD%etH=6XY%`i7=!_JWQW)JM$LKnsU})J1DJ}%tq@a;G^D0 zyV-Y%tiSwf#RgX)M`fkeSBpoan3m%TZMefeCn8)n5fU$kY|>$HM=m6h1+#%h+(gAR zIDDHD^jIW}B12D&7a_`$fZkG7oW_{b`}d(wil*4pYTTj)GJEkqe+DDE62sM0haM%z!o*(6FHV_FA^ z7wMwV~;+S(9qO2M=j+&=ir4nZF}6!!=_NsCw(ndEyLrs;3-C^qv^Tj$fW|WA z3W_@8I~>1KF893M4Im8;X7im}PaET34i5|`aC9;dn<(*0yfbU$lZuV-^O`A4FTP_yKNw4L;M_vtb1RZso zZ$(E{ldc&#p9dn9NrCou}6i}&r_trCMdG4xd4zi(q8tbhm~yoCq5 z7jV2JvpWRV~;k>T2uEKev(T9Xgob;{ioDvX3L@g4@%+Z3ZyUA5tf$V zCE~)j?tCY+w+>@k;evB99b@oOHZOX^eSAs!9FdFGMiuRQ^6b^rByd0wx!W`f_YU5{_RG8|+Te}HPPYaY|BfYD-DCzeecVUCtxea#Nl&PAlQ(b|)NOmAW) z+B)56CO`d&Q(=*v$ zj-trmfoVB=GrnjMFik;?xWjCDpML$i6+K@W**_!3IT=i_qB30b5?d{aBX(xb>|-o6 zhacS$?T~ZTiP@d0th*=S-2mtiC--(=EP1aa4byachu-8HAyHu%w=inmy2%M{*ZVg* zt-y+E_w0QEM|q9jqlOK;5n3gUz{Ocno%4Duf4JQ_pv*bV_65%Vs!@m1G$zald*EYP8V_3%g}FFTug4c-ouf0gZQk z#n{2_WrAsQOCrJq232-*=ZY$uW=IIcWG2Jz(>qMEqv4!=ev|Nk)N-_uhfAZ$VUe`O z^DbqYYD)1bTT73+%@ma0Nc(EZj6PkO#>{HSQr-RPGtV;>?RN(k)s{V9upmEDMaBPk z`x(I*tBR9q&Qv{L#O-biZqK7@7CXf%pt?0M?cz9v^2K5w-u zVqZr02S5L2sNbNz`%s@^^^)@C8!wcPYH22)>)OD`6DjAd69#36OiO*Tyw9Z@H%{^( zP^&yNX)1cUHYw6yp_>wR&2mqX?bEBP7OSeN4m^2{kCZbkG9Ov$L_1VGs7GXLX|jQc z#NhxvFl7dd4N;BL@n*HuDzk2n7PiJU_CU%b>Odm-h7TjAb9(?2f~?>xwNq+V@17e24%XY*nJ+ z5J;PKr-%wv@v6I*W{1=M8ulam@bOfp=hhs0wW3S+?p6`YmG;R?{gNIjdVqF$t4Hi> zxcm5`XUWwZO(|gx90M#YWj(?oHu_QP4iI~W2Uq60*d2ZRB?TOZFBJGjEY)SX8W5>$ zGyu1oM%NxBtI32vM$w!zsf*miZG8s!tXIz{Tj!yi>sXtAse8!PDf4#u2H3rE3OL@s z=knLPtbD#gi*I-*U~rk;e$7WcABV2^rT^|FBad#~?dy4>ylshI_PsW5qgTEW%|5}; zG3g$<@CBqxnk47au~6By&%sa_=<+krNOS5axh<8-BF1y6vj<}eqN}%(BaUeD2lNbs za$XzkEn#dF`--t6$j8pq2W=T1QhO-z_Fxr|mix5NjLfBQSdWL{zOMUu$_&M{=Vwk} ztYicq=pi!IEDnx%%Fyw-$FC-2^=i|$?ckhKiWr;G8P+c}w$kX%N5UPujQuPENT^ zx6@6A9!Qr){~BM5Zq z_Q^fAU;1lmDbtZ?kN$6vm20?IfrZ%6|GlpBYq)e84N4B^k(#3{^3;3n`w(>TCP{K( z75op`?!msmsB0IR`QtS0o%00vO(JS7$>XNGejGkQB^S}v= z%zmUSqM-8WLuRCNvu@LA2-t78qKu6MDu z7`m4y5u?p!EdUj25;aHQvS|?K)4O{xnia?Cpyh&SvS-yYU-UrMX8WoLr(KKSJUP%E zwb^`y6&Un+^X8XvT?^LK5M_-qENuYLHwtiN~lLnbrfDv4eEeBNbkG$SzM3rK*(a?yy$64Zc_ zb?LH7TFVL0&4XW>Gd(O=)Mu;i%U!81K8FFrWuRIxmPWue^Z}g{BakCB>0R>nqlryQ zVYVr59zl+9km|VBh5-*Kwf5+G#0EK{GH`2K+Nm(AKNgNz?(;v|&<&PS&>3u@rtwZS zV~!7mM#%3(z8@Y=UnzZWT7;EMzi_$!ucjvNrB3dv5GBX3v$mGG+*V)LS1o<|c&?*J7Ug zvBR+&+(5*-6ybKl;>W1Vy!$00kk@(%6;dlM-}wIPcb8TrzT1bAW(0!6)YJl|LV(>z zkZ}IO@Tf2|4i_=e;R;`;f$q_JHeK0tTwA&a=Ge#B0}6{k5=d6<0#ZnLDx>JFFK0HQ zWE0=2HrJN)jZA*IN8ACHfZoXLA9JHJP?_TyFTfbHWrbt)%b3Q2Ho+}@j@Ys;=2^WUNB@>3Rn{0z0*2s zmZDgZ%V1=2+|JwSzM3%o!jT2()iXfV#>Wm3eA<(Vv)ny;G8zvP{JQSy{_(c3><27a zcM{}4wkpVKjQSoc^ruX@+Tlf5e=h3K_J6^DD#aOHa35gV1v%q2H4X7&SiwP1s}P3& zPe1(>T=JfQaL?W*V#?cSea06&OAt98M>a3Su|i1jZ~;3ohD0G2#^+2~skx-zwlS6Q zdJ-j5UD)K*5UXx1i>*cOBMwR|Y~}MaTsfp3F;9G{wfi7u6W0+i$VZ>5_gFSJu`9|0 zIuhH~*oKQ=H-p{0fCt3*fi>Ff%n)b56~(@03nvXBG*H@RQ>P2dN%&HLa5VR^9_z<3 zMP^#Foe_LN0&~l8a<`5-Jm~~Pt^EKf(CVeOS+!;V=E^JJ< z8QfREZ}I)pn!-}|!}>cHR)JZuUo%zXno?sEJ30vS3Oev`E%X;Wi!jT$2ttbz>V1;+ z>AIH{N^;`p;p@hvY~?dGhAvRCiwuiiKp+j0^ea~A)k22+zew*pv}CntokqQImw{8>F@bGJz^cHG1|2=~Yn#xFi3n13|v z2~^;Mfy$nZf+*6p?6nanS2O<^V7~BN`D9V=R$cQKN~$;!Yz#M5G5|U9@ZrODU!=zI~xIlqwwZlxod!7a$%LnA(Ud|Pk35I6>Zp8e8HuLP3#MMU87|1a#zqN%*fo<<&u`ny`>1oQ*tVS&s;L$o7p)_|zR6!OSq2Z`rEY{2?` z+AvKxR#U_Pkh-CKiwC573azZ^Q+0zuuzVc0oRDpN{Jk+tM!62bGIAC){-x~7lL5=J zXTLITyL(?pRuuE3q%#pG0D2bJw6Z)#jg;;`QR5Fj*?I2c%pmB;nH>F!4Os->O1?wh z{jtA2mwo4W?6IqSu^dVnSxPXQRmgRf#eI@Q#m|4NyCTxCvK0O{CojOjdTlG6O@TL+4h>wheI+Mf@Ne%UoCV zAegdmN<0L#%uk#11xuOD?ph{LxudzqvV#7sx zpO$A#aQhQaHK}UQ3YEdY39tSKTKgxH*9V`x&7VF`{0ECtQwR*#@x) zt^b{le46ulfm4dN(?Nt^Nk-{E*z?#di0OPA-!{OiRGXf zQ=sB%dWfP@xTKtG=DFF5aCJq(pacU_4*X&JjZz&67dZCS^O`KysgOF2>211eWGR30 zjj^FD5zi{Fdp5|A;?R$3Vo53GFY_a7KQqM`hNTcp82AhTKxEFtqY?Lw4x_h@N!}_; zu%RgUFb@UtR5BSC8VB=3fJlRakFJ2vD8*ijT$h{D$z%EQPznHvxXo|y^=pOsxTu{& zU0(;6-p;Tc`LcD$iP!gcZSEO(YkP82!|T89TsyqIY5x7Qo1_igzec~LCVTaku6Zle z)Qp#P%XvCq?acf;b=~`>bsK-q{@%u3Vd`h$RCtBOEwn!}aBb7(y>TyYsIO6P+B+ib z$FqsuD;i%*E131g$==rfM%ovqzI6#Zquu|TZw{QZo~e-<-0FPfk5$)VpA&o0zVJY+ zbF8lC#0YA@^I03vHr@iaJ84dl6VA9_5+UG<;@k@JuQcEab5m(uGjvnrDhQ)S!~9Oc z6VGGQk6zVI*ohmIF;o?fgjK+Mu9w%HC5@NfQN^5${|{+z9+z|ahW+2eSO#ItWM9Ti znG_+i?~S3dM4M8KAxp`Y%9b)SGGm=dix4$L>m5-_Dm1nhSz45`E3JkkTlKupD8A!) z{`kFq{urj^zVFZHb6w|kp2v9{#|g=-r!v`kTtm&qKYse*1AWZL&(HpMGLNFaCx84S zV?X?aSRLtg?cBNee}Cfc2ZETDD@UjJUGX&Osn|Zff8PB8MWc4IU~CP48o-*M<JDN;%ZcpdmD-T`D`HyIql^koX)SL!KaZ^=bHm>M zx;SyBriOXMhMC!UrG6kE$27o`3Qf=RKTk)SX&uL%0}au|8oa~&5AOl**RY|$FbaUH zC#aLKzTwvXg=qpw!zywuEqtUii#{70D;q(gpS9nQQPR~VU|z~x+;GK;Y?X%m+C+m$ zz+(@lW>il7_&pWs|1NiII1WDiG7C?y{xN*?RfWRjyM|l&@z=-dHjsWk{xU!QAq%Yk z#@)3(_?pg{bH`vr?MJ+=L%18X68YOZRn z2%7w!8mjsBG=svzy00r8pm&R?SxK4DC0C*~yj(B49iU!-NK@;gf3WpvY~`|cSV}xz|(}N z1M>uaV%%^c- z^shR0ocUSKO*m~=N&le5EL-F%ezh>0%#!ylcm2P-SZJnYU=thh_68P^!YeE?_A{$F z;^R!3e6#(KMOV+j^*j?LBh96YA9_RM8ihdR$;9d@+%)$W;&cm+vSPZ~@@AjBfz<Z$4_e~vO}=Nl{a;DGRug5Sl#q0cj0%d1 zf~g2-9G0(~yI^^TPu@@NarPhB4^5TQkV^_r!>2>W+(Gg#K9_oTYQwIPAC*6U&ObI& z20P=f;Kv{%u8C92yHVrDIH3kpb;PMfyicmYg3N)4DVHJ`G-2Q?ENLvrIDtI{-ij~hCWuoGM^y+M^-$x+Znks5*I&cc3lN-T zFYQefSF-=(`Vai5a(;Kgim_4N%zZ*|UIGRvMpDX~5#m1rPeYefi`vHXCdb=ov9q(o zvje}^c(^_EYB=Q6WY^oe^_l@TsORv%jQGd=X2% z@9}er8>yXskBlQ5wZFPP)xHdllqC3mNsHF4voH-6%S<^-)nD}f1HuDUces%CsI#<> zO(V|`d#CF3+P6E^gzb49Oi^7Q#OWFrQugoqm<<#aGsx{d4vBr9IJ9&30vRj42#C!+ zfhMAq3&OP47lVl?T={N26?GJrQk%Q(sS?{0;8^>ofX8epjkpaXoJy~X{(7rOC`D}q z>L8ZVbmsz7Q&Za&E<&6k@uadm|4qo&AzI@pn`pEzc?!svLE<~(;>-^;OM}zdln-ET zVN`O$4N>*eQQWOXYA_ps-zU>HDhhfk)Plt_6wqpSrw>i{_s>q@pDTt0Q~`Kxz0!Fj zBHWL;d>hSdeutIF#Tkx7oOOuGz`C+4y>a1GF#LO_ZKBjujK`%=72;L~^`TC>3;i1= zNm{V>dPmJcNGeoC&aT-IlujAHI`~>rYU(=sO$&U)7GeXY(jDg8NXZOF5cp5me$JE{ zXrg8bHGrwE+uOAqpKtMwv^b+Uerc4YnBPglg+^+n>Vjs9%!>qUk)i3;G5rK?p`mh$ z1*_F?6g4c^_vAN%UYA_@`ShM$Am$eHtVE(dx5H8R`64kRl?L=T4tyvpT-34VBfC?9 z<6QPMq~NG!xS+%0I9x{Ok!;32d%Zrnqz!_K)>VPH4;%>?J-3Q%bu$cPJhVtQcj8aTB+aT%S1`f>CpJ z%#{+IrGjr?-UN%Y`3=06`V|op$ag!nNX8S&A7ol|qweq75I2M+skIH}-gS0JIE`7c zfX#CLU#gg070yY*7CUx%zNYR;gltBLKRoJY$5V8Wm@^t68^W_A_@(!n9g+W>KVQVH z_mEljGAvor8!+F85d2wOMNDJ7iVj0`j9r{@un)I5{$ra_&yD^ljGEa;_+6zfl0|wU zg86@ca$vQIFbJs8&?9)xS4MQx7}3r#F@!O&=?ZZCHBM% zyv#CLm*0kFhWzu-KOs{)D3r6L7hQ*F3pWCud$(Nc_L&_TQoHwe-#<5pT7Rz3K?2Sq z%3<~L1k@#=!dn7e%mADbTXubeDN{`O_yNts2}mCEPrmV8wIaLwL~G6aSQBjJlvy|; zLQN>}Jb$N`R^}^%?*>xVtVNrYft`BNQ?%UJ+KrWXD=?(qUpM)sh?yq1rVp1QF8XFt zk{;QIdU;nlQ6!6D=K5Cd-UV%&<(C5QPbEuWZsTm4vM0I;L5+_?nQ85rKf$M*0KZn8 zW06uu&jZBZHrG|H^n*&E5bLflbEVOc{loMeaD-M>r!LRGP6*FDGOb5k0_n4S%;$7J z7BO(u)VG(y$-YN1E99JPX2XbyKR?{s5mmk`2Pb@0=^s zg0z%z`(SG!Gn>C?9fVTDJ{oaWw9+vGo;K{SVc*0*OV+9*x@E5dM6}*zGys}S=P?^j z2l$7@2!j@arh7CUdNU_m)%(x#JE)6Qk9?e^$a{+Eli3_@qR^gB5y3@zzj0M}`DUDj znbOMQ=W&dSM?FDhYW^m2xnBeQwtk};mC@VmB#n;|gOskdHA3Qr%K<{Chcl(wk83>+ zP3*qr2j(GpIc?BCMQLH!S0nBa*Gf^=-|D3FfVWoz6jOB0-P*Wu<4P9!2+tIm0MEK9 zX0f$iu(LRfhq-jlzCrUZ{i(CnR9zsS8^oNsTjjv^U2OWh7(WePb*Vi%wxOr>(xLk^ z<-lCosDO5^kpcj@($Ug$_k!PH_ed0 z)Y`P!spCAXk#-SvCY^W6m0(dj86}bwbh=NJH~De6YRF6N?K}u`Sm}E6e#MELt52TP zQW3KKeiIwPM4@X&)cFcfYJnUjgiKb+Zri|)sec}sQ-K2j^<<2;z6Xm2bCVOOM)FzD zvMt3n2jo6REs@tE8;-eNhy}{3me=9$pad!K z&5sy`5Rc-Ty@@!qdVtwezpwKpeD%cFHQe|Cy-t5Lgj*(mp|FCoYXJ7O@^YRCB$udDCVqOrN3eMoQ9-#>ZQ>Z8W!R1(dmWzW5zF2XxsE{Zy6Zq zI-+2Y6%P9QFRLY09rn9ZU|Z-MtDVv;Fs9T+r^9Y5A2I3I^kWYTS%Frp-U<^AYUDWK zNO3?_4LLzF8D0^__78;xq2n7;Q1}n7CZ?B10t~E-S@zUAz(8~TFr;YG7kAb?AY7-+ z9+~C>o%oFprlVQ5Tv+mYOZvVzso}heQ$Vk@H2yXnqAin~DMG>#h#Hc!9xvO^9dwq^ zCamM|X;K^kDhj3Bbn&?<4Ts&(vzCWyUBB(<7I!?^(x%Nzs6g#Qy?T5X(a9%%TGoBj zVy-Esd{ud#1@=!F9K>F`cFl9nqsd9;$Ab0|-Zb!*j2{--zBCYYhb-I@uXImLbltRH z&TjXZm#rt;e9^qQD5iRCvAt_~HK9H z&!^FD41q0hH7gqbC^3kp0F1cb=iZf($>skkmzTSU&z&j@L{m~M$_`bm)$5=8~`7z4Cy>G%mq zObspS48!%4syv8#24A)nejp-IOB|G<7=;gv8 zpOr6ovcu}p{sE)cUk^6_b8F;h3KQqV4eKtn9kQt4h zh|=swD8BC3dQ-6&#&r< zRS-$b&vu5SNUR}Uh}9hyG<6Yebg~>wZDx8~@ZQ`@;S8God<-?3)jr6Abe(yS+ts3+K$At_w33CUmWo!&} zql_jZmrlxBNZgcpK7^{r=O*V*L`$GD!G@yr9rXpK$*fL`1NIE`-PWV?{ZwrW)>`j4 zW8I8H`d=kY-3}yW%#U>I?#j_70n{i}+*3!RPi{F1htK`wS z**rXs2^}Fk9km+hs7_gDJM28BRFdc#_Nq zP^s(ici-#nZFa55ssk2nc0$d3l<`u6`~2YwH!W#0Fr=j{mlnQ16?%vHpF4ko9R-=u z6c?*iKzh6T|NQZ~DzP@4n!k>fd{MdkTPVKDIZyrm3faohcp2uxtr{A85yB|Zn|C}i z@;Qsa)XX;Mf*9S(MDlK(g?xpZY4W6^J?87pBZ-KeoO<_}GH%>wRl^k~0dV)F0M$7G z|4-ybLZjFk`O66Z`oXF17q-Ggi&6kE)hocBRzT~WW9ZNIY^|aaRbsq;p&G7rKe%Tr zK_kTi+&vnxKZC*}GFDJ!a@zMh-ovOnmWKoX0Wx6_KtBf=ig1LiHQAtK4=q-zwBb2F zleMyIhYfO~4I}d{Z33GrG|s{Skm-$!WMMMs3pQ5XXP;)Tu`<#6DapU*?%gvBm`^-Z z>;c*%&M)?!wY2xK2(W}Q;m~6p+k0VjwzrI`q%=Nyp0s%_RKBt;Pqio_wWLH2=~=ifv3 z4HjY`Yg_LY{8VYsYGI{K`1LphI3#Yp0(yu<>2l5J3$ElGI(Nd))N=vq=HLa`!*)Ub zAhVqVs;Q;-jBr+pN#xe^GrSfzQq;_WkRevjx1#z%@?{TeUuv!__yAtlFR73{^z$z4 z>=8kQXu)6*k3;|4W@YPtr!V^#szce8YwudE&$c*VI-PfPggKNGQE6@U={HA7HgA?; zxKgXSW@r4H9Vw@7*N&+<{ik}QkN!eM$ zWeaSFT|bfGox94%=Eh$ohlZW~W6ST2bWHzTQC;)+qT|cu)@L^T;y<$7_0h`Ba83&OMt*MB<~OLzN03EQ)TIBTM9)! zOKH6>Jf(?S=LqnuT#Q5oxJbzZ`>K@De5j{{4W??g*>3w(oBA94?r;Aep&(wT;;?u6 zR&-q;YF}f@Gtek2!DXFvjd7h&-#-Q&iSOjY?II2gU2#9TMdDOB6l7nhwry)vkc%va zZEr{(JUDg4uwkZVYl7y2?i@tO=h@MuPj`)$&6{6k=nq+_Q{UvcrNc&yutTLO?EcIs z8qk!hGn*tgZQXTI=Dk*m9Hg`x%FqB@188dWWyqGxRwp)Vy?YpLGpZ9b zC)8FC%j{qEZ-vUkuP!Y!XcDql)nS@e&A;j?H|Klo-`9QiCe_arwviSGcLptT_+?hy zo8qjv@DV-zZNoQ=8TWnb*(bL7Sxs5{cFcCe9C8J>E$vU%{+-ola{tuo>T%094UXzo z6SRoHE1f-Q&ZD=aZIFEJ+H@+iXH`|^?9WHV#g^p`kQ^Z7i<1+yDm{u1Gn(Fzc6zzG z)*7t#D9Hwncuqss;)2xcRLY@8zxAA7_j@sVaL7PICzBy9% z)0mJeJD%t`{$%)<>zLh#roZj6)GKy$O=bfH`Q3dqx(wvb!$r9^BcO2j@Zl2WgRc$@ z4JC#Q8#>hLX$?)>q}^A$X>JV-4HcX5Oq;wvB?I9yL$o>mJvV;pR`U<#_aItSK*x0p zgpysbqTF~_(n$9etB3l@b977obbyZPIqD5KTGu(dMII`xed(Zg?UqvWS0<@kZ)A%) zPRo}Y30>_tb&bOR!l`F9=iXgU!+*zqNueU0+rAIzC(A57n{r0WWfS;S9H@5x+_`hw zrfhN6_{0HQp(%`9l?UR~!txZu<;-0C4Mb`##4Shy%Y2=koU%kURTL!&y)H9}e)~^j zM=?S~_X2+sh!*(bSshOF(V_^Voy|bo-YgXu7G`hkGD8B%m1e_H%8p#(Zz-50Y#@uG zsxMSjG6^_Z)uXw}*9|tX^%v>Z;sG-vC3%&x8K`DdvI+5+Dp}m0P1FOFABT`3v{Ywf z$#_6DPT*NZRgw+|-r>2K^sBeJu75p+Q2=THael3at|LP!Ok=`gsPlqpii32V&c`nY*{c`~05zj|8gi6MqfLIT0_%pFmd z-)%bKzbTT!i$;SXXqBOkFC5Vf zR>vrv1oh;$_|vDKvnI^+>*ZU#ppoz6dfidRa!^!D$eakn14Fd`r;(bisCRa8>%4?L zA_$sLy2z9RLQ+0nS!Z+`Hce1~cs+o4nmYcEXLQVkhU?d5Hq8~?I9jG6aQmqfv#M+N z?FcZg*KSKR)yV*5NajXAQ-QOAC>@a+CtyYm#%*#52rW z_-^haC#eQbu0%2A(QF&A7#Vp45=O3&%!x&am%%iIMrHAmqKNchOj;gO*pRWvyefJC z8IY!Upw^YICJ^`^L)sEh7vO|SMl8!HFq5;hPmX``(Ssh+sD9g=alA`UNl5fGVS@JU z+t)5zG$z~yG1I^eFihD4V-_IA=vb7o;ZnictQRM86p+Wa4dIu_yt8!Pn+=7Ca9qqU z5U#W;6pI>$y_h4sg>@#g1T2&&P{wn7MO#d~)>*{xf-ZQRz=2sb@KX7tx$s!TGf$j6 zKuB&`%`@a`By$i#Zr{FX#o)X%2<1XP5XFeL_Edn4lU6+-O6|u?KkmuqF6P&=%khXC zgv+7+bwM$td%#si#maPc8AAX%Yv(J|ody5Zwh}k#n|awx_7b5cf{duR3lI=t1x+Q( zyTQS0Xw?DfA^E#4@ByQ4qTZ|zE=}*v{2nnVmx#>)Qt*ztC&0|FitN2A@L{2hz;z6hx(#T@e0=56hecD+*m>++G8-qPas-Kn#wc z4w?ukVD7S$k0(S>VbvR0`;ZnuOt+&f6FM=~fO$a<&ZwLByVk5h3gj7c0niC9jI<>r zL3Jc1pWf-#SrdJmz&mi>Oo~sD5o)qa=$eS73ZmM&W006|RAheZ^CsuBiE{Y4x`X811j2ANIdNG)69aH|n5bUuhQ%@3AnuoFfl*qoj6c!1@%#Ui;teMP@qSrR~ zP6rZ(P{Hb<1oX?U{&4T^{`f<^>4yIv;zOo1;C7cFyCTRf$lRnw2$E$^i!6wYE2XAM z%l32&N7Z@TajC|R}){5cf#GYDf= z0p5*bjU+`FxW7kD3y*3rKq*j(lHchQr1MQjeP!!T^tAkCHa{XW6&*Pd#a@e6D$H)- zi_(m;&2c~zBDFN&(k?Zj)Y0!SC&PP4TZ}goJhtwIpWIoNq*MiyM^_ZT*@<%EHvJ^QpqQG{FG-en2GgYda|tPi!g;IKVYi%5`U zf)&*s_D9}oyw+klUJL!4SJC1|&+n!6b}h7f8N-I@e{?FznH`-f(Qx%_z6Z_*F#BUT z^G3wNz$+;Gi)pXSpi5ht(C@=@&Rf)Q%$pcP{0Z;*JR^}54A&Sz=Ok%vJPGe~cnL!*+snNQ;X#ZrXSq97PAO~#9O-^00nZ^mP&qkf1 zh$|v#z5!>=AIXwcWrcr%Mk~)vn>Uxh9XR&exNcp$&SxX4ss_r?D3-Xp%chNym}uEC zv0O62N4iBExqP7nb%#08QsvDS=O45HfGZK{b{;>I^HZ>#Izm+=o9lBvlPRcmqqGrW z^sN9lUJLBgcii#&0`H>;PQgXaSUw_pu)gvUt9+%~Nd0F@@U(aR*MK3A5p3zs`$bSJ zMyzy|mNoa(cXgTF2DR|JnIHAkW;U%;cKl)Us1RBtXjTeg-renA3tvbgp9|&3-=F5 zgCV+m^TZEzWx@_2mpAuO(|)JxSsGZ0?R|;28lltJ0}H!erT(F zl@Cds|KX2rk8}~9auZ8)hj7xzbEaCxEDw+wagdo#=_Kz`6EC)vPEN`b73xJ;WcAfG zGLwnk-mMpGlIkdGDJQSY#d~UhGXF5EiecS&uK>@e!L-R|O#&sUX~z?T z$SicX0UfOX{mO|t$x4&$YqJmCDxAd}mt`N;LCm!VfFvaN{#;MFJ)|^R4s+mkXW+LX zd@XWFug<@mg1w?uRyv-|7-{FnTL~KMK;(+FJSkH#V^EekWy9@RIc0yRA@1UsVx#O` zv&ZX@sB}|Z&c`|AtPL+LD6r*paF3-2nV5QftE6}2qBfnr$G}@#`^wP;#3T1(qpVe1 z%CHL}g^e-}@l`;{&xn9m5vX!qdgu!mOXUH*7jL@;(;S5&Z&KILQ}x#tE!mLx+62zC zplh~&{WE7T+>gEkfC4dr!gyYAiFD{6-=2wrhU5Mg7fKKO=|Ns|riq*B3W5koAXMiX>aoLW=ayyiS{rT(Q*q{er3V|QXt zl5e-QS?LycBQ-I3>5m1ujk(Vu861}q)m@hw>W*sJ@gaRo&4R=Ur$3V8%c@O+#AqTQ z^6#^0Vrwvee#fjKXf*Q*eN;7tFg|xZW+YN%SXK4b#=T3p^Z))E6XHwQhLH)0eixj9F97I7!aCZ#>CnkhW#$k^ND z;dDe~Az%#rSXo)CT07K=$dT-8~O?>M+U0{2k`rCn&Vo}EqDmzr$Sj^U+6uSE6 zjrKsFNil4uwRbl*OLmF4FO}izk67X{-M_4N+faVT2y{VESKYEdN$10F#1WR8OsU8{ zz2-ROYO9iYKmRN?PTM;TJgk`u2ackitr>h+ZcWQ>nA}Awt|*sT9IFsB}glujD15)jD@r7vaGYl8_r%C zY}Po);9}Kmqqko^=y&|oBkeh{Su2A6-U4iuQ($^o^HS}dn~4cq_uMX?{;a9VAM@7r zdwk;Foi>LxLz^^grxHt2W^u_v;-CrNP5e7Sro`*)b>>H{#% zb6hr10sGofud6%&1ITd!0F&!V7#cg%^~v9Jj}LFLRcYKLQ=u`%%yPeJP@m%VZPvWH zRlAjDe2~VhtV5GtEl9cf{Tco6r4`C^TYfTJV7q^S$&ZJ_44#J8y%LedR+H^WqWYSo zN=B`C#wPIA!tHAG=XTUIOkeEUc|=`UG?_ z)NQ7P7_E{)xE`~?Hmian^xQJ2kryk|Dd4DtcYe>6# zXk)A{Mt)MBr@Um#OE=nj9Zgjx*4gfE*}8SKW#QeQ-oE&0_UzlLCTpvUKqz_6eILNJ zXJp**R2lgqvQVw#gZ$6Q=+^$TK?>N+3$t%y%m5O1vwt>Dc%fs9rPTmeVde$)_jPGR z7QDjU)oNqT{q#u0vf>S=j2BulaHfg_tjvh~st?GPTNjlNx0#91*LgYn-Fso8GnpYJ zZv+Agy|H2FF1cTkmYm}YI%0umNeW^<^J!%g$5{HPs(o|ISvwhAsN>W76ZWnXJtthV z4CT%-Mt*OrpSYUC=<198*!u0=Mwm&{)Nu}6Z=oDROuDR7$cz!@x?QpjCm9rxJTZw9{bo0wv#C}>lvXr50J^+FnbOH?X1$&?((PXBMP9ZFC4%yuQXR-so zcQ!nx1+uYn%cE%*XL9O$#{9{ zh+RCsQMM43X=dnzO`{XdASQ8BkvS^vc2IQ?DI7?9GZL}j7M!!T(OD5jk}wDWk#xhh z6TIQ4l@>2BGogYJZ`Y2e`p9G${K9TPUlwL=(rW>bgk(DA?NMR(W@EGU9Za&7`8_@? z*rjS8p_I4A;~$gfkiM7{+B+8fLdH~!D1i`w|3@%xW7r{T3JA6h&Oi}@gUB@-XfHg1 z$Fw&eMFiHMJh@8=0mM;X8V@<-so7}EUH|gk&+*}zXHMV8@Wc5 zO~&X`O=T4M;!A(_6fb_mk+=AtfA5}EpFM5z8DsK7Z!nR;z-*YHeINlj6uLP20h--sv0ty6*@NHPteNzk0RRAA6C_$ z=pnJLH5CUWkEW#`CSDxmaq7y-7caz*s**xi&^DX>qvl^~i-Xf5xX6P?yza_0O$wp7 zkk_FDxlk1hVPHmIZ!;$F@2_aUyz>Qk>n}PXLiKRgN7ruMWHyIPOrcYFbbZ$rBSxu4 ze*b4vlV)NdgK+qu9JV4tV_h3qb$K^T)7Mb5%U8@%C(EEf2LO$${OAZV&rj1&Xx?rdD|F!;7AU-$7NzeH)DW@`Ojp1nN& zrHkp7nl}z1bIb1)cDeNZzqzYI>DtJN?}!Ef+G`033VwLIkfc)bL~y2FCdJGvlu2h4 zT++>C$t z+GbPw{nAoAF8Xo!$unEI>Y;P#XF+R^|JdWN%g+;^hUdFDmgFaxnzvnIS-s(xsa;Pm z>NFyD&-0hAoz3S?AbRLd%d6RHdA$GG{?1R(#!RSiG4MZ?b6V3PZo|}psiSm)H=ceQ zkyh(V>nCH~gPodVs$8G$?iHTzSoPM^~o;-_fsihASoZS98s`UhW*$6Mt9cXQ6099-mhLFbaJ z&qGC>xxI^dO`H33wl4he&^}5d6uQX-T2bwbuMySvDF6l01Ko;}adzUWluFHZNGG{L zf6t``F|qJ{yv)#`a1`+|HG0k4ifM=?bGvEIV)<}z_IZHL32Bm6?@EcLvAPMUt zG!^Env`tH(*yZ7Grlv1# zW`KWDN&@Pj_L#+e7CA9cU*-l*TXCud=19m2{FvrurQ1+%1g(I3CBxf6+-6xDvZkeN zO&+*+W26WGhy%7akKWS_`ud~G^S^(sUOYHhYkJrO+%T_3!SOlT3>*2;cBTg( z#1R4fsVrXkVRb6{&(-*t!wBvd{qDI|DPLw4GX5>Mfit*(*|(q<1Z_8 zf1hXCVEo?joy_eyOUz)DkhPZG#x1I@8ygl$k!-Ht} z?Xh3NY`G51`E{2rc2)bCw=8qae^NJVVsaW?wx}0`eHL=+YsDGeAM3IChF@3wiAp7q zOmUjTSQ5X|TnBV_s0~fwv%n!XRyS{MvW;|3Z0iKKhNmGXM~L-t|t6h#&NDpv`D}+<)0;$~KUav&mp(vNfeJZR3=j91D62r-}~meOFOHI5|F$8sDR(+boqjekTkDvlkL0qXkFtL9n&feiDV!E&SxNJ>RF3#Fo2z>{Ffvgqb`acuGZ6uU#@w9lSB3px76lTpGl+g`k> zFd~xa#bm~k@G-9gljZEN_KGuJpKKx9(-eA=h>LKKYzGr1^ZS1|2fT zDP)JJdBg(>uCw}Y)7z0!$@E|0nge7kQTj5uadholhm;r&XofRP4K8dCDBF}d>(_MN z&(iT+>9=uD5qc^Kd{TaE(6P;kdY`7cx9%2#p6Qm=!1r{E0%irfce)^Y58%>Fq5$h- zRyfpaQ#2cW=FyCUDU@H3178bD@m5J{#5SY@49rRkXdFSJ&!<6kfUsg{Jxd0HR7y$9 ztEnd>Vzm+k_}>{fFUF&#I3}ox%RdYw$z#(>sI-NFXI)rZGN<-B>0eKM6j8@d-zjB0 zwLtTv8b3MAp-Ck~UkC6{BNBh9D*|5NfCR^7CX9H;{h}LIy5{dI4;}*BvhiH6Pa0K`H z$mYbpIJls$--BZUIipV}ja`SR@MN5b^c_{kT?!E+QOU0**wQX%EV;h8#}I967r|xc zP>(DbE#qECIE6_Qp2r)UQFW4g3!X5OnxfxRhdbUFpa|iS+Ddx>T|52=T}RmNaW&;z zXlSsQchpeS`*h?ZfMb;~O7<$1UmpkQT+Q1#%Is(Vu;q8CP=$n$Yf4itfsb>zS@+dr znM$J}uS&Kmcto98F1;TDAbUCInQF4PUE+`Qz86tG$*e_O@?0lW^-jT*hpy=uGUPDk zwm&z&c#2`RKd`eh1z7D}sZc0Cp+OnV6Vo5@*-mr&k%E-yg-z{u#FSfkiB4sf#Od_% zH!}1F)}{LFHZ58Rufy^Y9WD`=fLMZD)q20*cv0U4dmQfHGAo6|oE|3e}a{ z$@kN~vE-{{-hJj_0z;PPHPd_r8QN<)gMA;kxw)-$ZazM3Aj?|PDRi3yQb!CQ4j20e z!ZmykFFGrkw<#U#4%z4>v1X6FWsb1i1My69OyiWHMfbjMpH>Z+xTpWzUpSl%;-^8} zkk6k$bZ)nm9Sm+4s)xX8lz!(&M` z+NmbT3(=a@BW4@-EX5v`=`1oodF_T3Vsi(1S*&-H)-TR47TKd}Uc4n&B6rYKah=kO zF0PQGNGQW5UoAGm}2sl05yuKCljAEh}eRCK@XYjA~V*?;B)G-ob#eWLHiI2=M(3=zl zmLYdeAR?f-PA_P{PUPxzQU&4n_Rr;(p1_hQC5kZ9xsm6l1BHC#8Neef4H;Etsf1$P z>D?@&D|*j&|X5q*ZpnB7yt+>QnbiZ}m`Oqu*pE6U|U5(!pPfxLCrw@9661#wq(b z>B(HKJae|7eI-Hk6gY`$yLR8Q=8aGqL!8vcmW|_K- zQX%DH6gQMoPmZ5fCHVarOEE}0UaKNUovA#tBUg>L%Rd#>eeL6((jr6mD@c^gIa*aT zL)01WE}SrCLXPbj0dL?jkn>+^xK{l@G7)9fg$-MS`=;fE^R>6Py2dY|g(#w{2@@xF z$pRI5Ou2WJ%kuK}Uhrz>WS|>q*>~;wcxz)tW|RMBwMO5GDHlME_c7#^?}xqYEnyc& zaJm&QbA7Y7J5xV!y@3fRPfkWA6pU)AH%163%x_{u==Y#B~bkR#~_!`w+H4*|X|;xwV(iZNoEkp)++3m$9Ov5?$I z=9dbrbbsL2q7Rw7B>iG*-CMwqt;YSsIWNNR!6T;1NsSOkU__dPLP@Yx!bjAE7|W+Q zp1NlB>Y1kuRL3w;J*8mX_wp~Q1Pi5*!$me4QQ)Pu7T;DjJtef{Q6 zt&&2w)9vFM;7_#Evk_T3d8LYj7^lNiSrnCZ`Le!98d-KY?)NwRSC@DP`tJq2Y<0il zYx=(e+=ev@EicEJjiC#;c&T(7Zu0%qeMha{+}qrvw1tq;zZpdg5O6KVxcs8S5=T1BchGd5+rn?z zEuxR+-Zqd^BXHLoFkqF1q~2aHbGK6zkgeI|p=Bds%3g3}D5n6dC}R}q;^p?p?jO)+ zi5Q`6J1hK)+g5zU%#@I-dmZ|a$^+itr*qTZUqgo@40kNp;@ntsN zXM@(j$-C87W0zPX;08AHLL%cT)H}{toemD*W3d+|8N(75?>59w__sz|m_;i?J6bu= z5AIvhT`Cmd`#ulYs$I@?AG?ARRWvC!R-CUw9a?|{U778H;fyjOC(BpE7U-!yp()#zVxG$rLhAGBUzPYpM?vCRZl+ha0b-Bi(f zU#&dWhXr%2g{P`6nC0GaN5xw)qiQ#MtMjRa(#JlxZWAq`z4m?^00n@L_Cl}AInDg4)d z!eF=E2PIWH^^pHOc%m|65<7>R@rj(t?klb-%q_a(+z7YjR(wJbEY2QIO^(8Gr@YcK zoTOw!xL^HYF`FywTqE^Sb7;@BdO&c#vY_zr-{_a^JF;tUDNh+WKyxmR1grUab#b1jB(<;(cWiSaV|jx-yqLuaKx>B)zOtTujVk9C=TabeF}O z(W^RBy#yI~U^(a7wBU`A<3&xt^jRA#EC7J6b4<-yEVSKs&d7d=ajBZxKm8Ga*dgNx z$#5a4ib>C0ZCVMo`BWh)8*ef;Pv_U!6Gm|h-Rq=xd-u*N7)?pTrp!<}o^PDKshf6$ z*){Er9<90+8aS|{N~C84c6EXYbaDF|qXhEomj0oGSeGEgoRvxJv_8C7FhkASdS?q& z73)cnS{;oZga1dZvtJ>O5Y{D-zHN%yg0###zHXX6Nf0HF7tK0ft)f?GFhfxz8oH?i z6H3z;LeBz+YElZ7ZuiBfTG_vc?O_@kGIrGC5V*)*zY2o~<>gB|`V>|~{Y``W!m~o@ zk>(oAd-CRr97+!e!}+$$^vSgbF@HQZCPs#WDPM1C=4){O@#7%gk3nJ?IzGO>i?eS7 zo#a-IZr`@8KPZC5g%=46nGVzWh4tyx*KDUe-C5%2eA_}9(S_&%bp-axW;K1fT!QYy zQnW5kfJsVMBgb*){eV-_87iZ3Q3&X_$o&iPXzm;&QCDytyK%b3KCPw(?T? zkXdQ!7i_e&!k^zc)#7e$+-~1xy3WN{12uyvnDiIuPuNgiwEn(Lg0`t%PKWJ@j<>9S zqB!8@=f#w&IEk2tY)E?ka}!o~@bOr^Zr!ZvtFCKKEXj$?Terojsiq+)1m*ey)u8@+ z4Qr~z^*|RiGG#SuRoy)q4BTAr_lL-=q}f%bY=dn#MM_?&B^ueoQs2^09FXesVr_zV zsmmQ&H`xTQx@4)qaP6I8oKkbGZdYxuv(Vq*ITKV>y!5Zr?N4syrTXNA`JVd`!I8r!<_e*Tq&*X4h4kmC zF9c%o*v&`sF6|N4OUj#J3af<;3WP_0`u<$qqu&_qQL~zODKxg`EE)VZYsPU4%U8FH zG{c%Jj1H>4nUFitX<^Z&^01KLCQhOL2jT$4&p2{^h^SKJS8O$*8UT(KVQL|YoM#o` zt^t3=(>aa^i4M*0SFO4YdC$p_v%rN|GLoeCW{VZ$30Jed{;jNJORYK8t3B#0=Lv60RmyXW&FA`uT;19)A&8{;>6T(m z{0L%ovLSWky3BrhWwC0drqOO}*{6b9Zaa4`4nwpKAqtcl@hlt8J?fEK9Kd~D$bP!+YI zdMy>*vOxFYDOjK7_m5WnH*C`RxL-}(waVNc-MtHMl1V1++FMoCaT`z7ec@CPx`b$X z75k+CdRaFXHdT0Ts>izPf*St`I>wtogtJY>(!BVYA zcde{uBw9!%&q?4Zo$P{#4?A8kuShVqHZd7mYG6%fKVx_veg5E7-OS)y*|c*4xGoD2 zq+`;F9-_^6?VG_%w2*=9BD@cP$M%M-9hvw1V6Ef zo31pnpwtwnB0zFO4>B_ufS0xvpP$n!D+$Z1nD5v^5^ zBgw_5XYN99J~Ismu}2|sXTTq?DDz5E@XK^;~Mb!=B=+0T*>h2 zv;$-2kTS(yP;IO*XwZ3SG#j$4WLa?j+KA10BkfGb=_(AkJ`00Ud#e(^*p=bv@0_Xn zIdIp8*?Sgr9AbMfq-kGe?VWZT4(7bM)g(~uq2+}gJ2DH0Y^43;e_!GRrQ69Co>Cpr zDK#fEWeV*#6DQspW9z$jubo4}sh4o?=sUyduA8lbZ6YMh%eUelKJoFYdPRRXlX-qp z8+*s{D#g^FK58<7nLT{cN(LSvtxR@olVL^WL)Lq}cHWaKjYC2zYIi1TTebgdN|Vo4 zG%+!;(y{NNWuI!3apIBPyqx*E@bLL(Zlujt(LX`BNP0;+C2kF(iP|a~ z|A+cW7o?ar?adrb`>!7bx_vX=X6%&X&M6jpx730yX1!U~u8HD|>f2xQU#$ynnp-vN zj{`mOtV{$d37G%@NX@O{f)py7kn$koE%mz|`CQC(+t5~!um+qUr3n^ZQ0g~16WTLB zIK8v$Xs3eLBCu}mC4$drqa0$Vea8{Z^j(?VSX%@KJv+)qp&kjM;-8up(ff%V$~NzQ zbCy0Xfn|Vj*~r$das~&NQ}IYKz9>y~f>#R^c8+Ea@6S@Y{oRGr(>!z|UL>$;EZY@=xIdG{t8`pYfZ^hkjYrg1O zA12=YoiSVjOF%w=?~%qf9<4U2=wmMwvMo~cJeiNk-iY5k3`@!?KM8@07^q-Fn)pbTzCSav~8KuKi4{?Pl z5rqz`c%QOa)GS~XnaSkBI*7kfK`?cWdu0n}SG?A1%gw@gj%_iCxP zQ#WpN7YOt`Ykc3s^3?>CsSG}6Au8EeFWSjbq*#DNiJSrw%Z9?V8#!TMC=nV1|{ z|6cI7$CCo3bsX?Q&B|1Lq_2N;hsQATQsKj)imEimgljS_wrdv zPG@6tnOA7ccF}ztg+ngjv=PyHXFrUG}Wl)?EHAD`Aq-w@{n(-MIIr{xKqeQU90AY2L_$LD{l%H~(#D`mioEE}FA;nql|G`=&p0sRkXADsJ}^>#`(|b zY+|w5GmAMVm-S!0c1qJk!oSm_Yj=}uKGN>5uxYeloL1<9)$Fvm~}8hT=jNq?RgF~4Q}q#2BT z`1;E)131caUsJM#A>SG1SiXCAM{>Vn6wB@#=rofF7q*CK6(v((mEw`giyW&IqL^Tt zf&fTE8YZ-$?%v>;Z8e7wmV@f=T zmiHyMbjqHq<2#5+--6e+Z;i}1OU@3stU#SckOak-77VMj(GmVfyhSk~on9OtvR&?| zOoawt7fN;b7(mhVkrmr`7(C9-B1?y7-&*f?VpWE$h7ozO<1a6?_Fx$-YdNjz$(=bu zPgV0xTOf)k@lLlNcY$DIclMVimPdcu2!qtVXB1Ee1nRt{Wl%k3e6{H+U5=>f18WY{ zQ=#53g|WIQX!fwvfGO;q`|xRK5K1pmoJ92MW{k*u9E7jp+EfBYY)o@KDx-Xx@a0Hp zd{Ep3jDaO8{qC%YIS~*FicmX8kCicKxcEx%N@A+}n!T_TuDAYd^C~!0r8_5r=Y{(@lJah0{l2vUy`9>!X;@YC_XJ zCkWk@v;xK2cuXhVRUl^j>od+*~G&~mOmc#n0O-G;kc`N4-TUwlM0fE zcz^kkAVXc$<@7*g@G~YjA-V zk`pZw&F{aT$O*y?#%`Po^?O*d&at+Y_=X9%1{^Olh9yFPk@m)zlUQt19=U3m*bbx2 zlh%_5gwqgaPK{r>v~=_G@S86uxI3*sVE=gC4u zSpjb=c(x=w%wz(MF<-Dew$~)s8Ei4}Y<}}5f^7r3H!s+A49>7JFNq9Byt`PD@Pe6P z!GOLaUWHVB=T$pwi&jNNajWX?&X?2M^jiEpHBv7t zXn4W1&Hp(ct!{Dl1E*j`P*yx63GU5U7wuH3K81n&ey1MxW36E=zbqav?k3OOoVM?xzEzF=IC;8 z?5$Ah5JC=iN0e;tvm*2Eh7Z3K)VpDLNYHsK5<;keN`SQ`$cLtUw%ltH&l%M1gky;r z=Sa+{eVuo{`OvETNUEJNBhL9rR?B{;?-uJQtZXR5$sc+F$rR5}w#fU)oG^@p(2ZCQH3Wk|i!lGV}>Tl(5yj_DOi6vuaMy3m%AQXXFed zcO>8PiC2;`Lu^|G9s?!e5~UaM7Az6}ZX@;CpBY6%EF-Lzfb+xCHRhcp=mzLDTeEiU z?5sWSj4!W|>4-%OPn_7>8zh~B0e(`%!_ehBcmp&VAwXLEO5M=tY-PS`e2UlQpO0zK z&w!si1HO!!2yHl6>8FH?D7l0AUUtklHqW$*a!S&i2aLWafbePKW;PP!abo|VVm-g z(|VB!ky87O?xD2iAe+JY6dq55Ve^xFLY_qVZXk_!nIx?5A;ewb8o^nMoY}{z0wXCI zpe#Hc$PFt(4g;#lRupWCa>}q<2dq&=R3n%XtG?@wBJdp+SlMelLsg!L_D=BlS#RhR zh(JHwh1BlxJKL9EWFif8=q_0;>3e-OZDfGR;Cy|3MMJUVBKb@d=lQdKr#<)34@IPR zU}Yh|zBp-IlLiEja`QX_pCfd|5H0X`lj6l8pk#r;@R!n5@s68XoUZI8|^rIldzD#+d1-Y;TPxWvX(+bv5K2RpMn10 z>GS(Hpb`8Hn_&`jHmhx{^(J7a`&ei^)}W6&dW=!hr_zi#`(X6ieUn%#^C!JK zL$xFwE}?O94Tj}mHjL>!uNTq^5c3n}`YfoUlVYwz8Y5ytTi(sbF(LQG=?|`P+27V@ z`(st2(OJaT*1U15rmH11w+sS5dLT^sI_)@umJnwUEnN-MPB0BrM)-PRj`%mnt@Slg z#R+a@fAi?stIKqJsV(m<7f%?O0^h}H+sgTHH`m_V)IrjwlTOg(;D>9&Lz?AE5ihL2 zTY=4j`UnIe{m2eiR-8!jUDb2ZE1_L>IxU(g8YklLsFw)29A1NbGEV=z@=>xSN$OYT z%>8bmh!bs^Mwg$ixS}~K@jHT&J50?E})}GLvO&7)v zmM*@K5y>Cp=E)79e+Ib7C`=n0>2F9YQFB#7?#utVd&e6RhPyMNKGFhOSO<0sIhHr* z^x9$N$`jmIX>EWpN2bjID2ID6uS*9M;4Md&-j1Kehm7n-WGKP0q+d>M;HGG8v1Yb! zxrxqrC;?n@P_hKs-pHnJZOYdqO}Y}@(vmDr+=W6RPvg-VOT$QH8UioN*he0deT6h6 ziC+02e66$7$W2+^Lb#lD0IH0zR3NNL9qFO{`DJNn${J%!cpPE@6>L#{5n}>|zaV{o z-=gvU$KKnpah`k!Kq9glF_c9l*=j^3D5@3@ zI|-xFZEbn(VsImMXIfd}ohc`&B+qAO)yVosz@w9}d=)kKS*R?op2KuQWa z&VqWh_YPkw++w<4^B-@8;UHT8oU!xhubg3A3V7<&WH0POnL-7-!Z76Y8&hxMaP@;8 zwf)~(@cNH&XfXvyQK=e0u^{`Dtl9ovUpATm?VRG-*3QifKe;jANYw#^J0-5Ol z+kWwqq7irD-c62TP?E{-k&-3V@|r}0`Ge=YJTewaA`WtsJmz*#dR*MMV!+oclT?O0 zhMgMM_jh#xVLjpV-LUg2fGQ1?Dflu+tja}U_R*jW=9F3sZ0-p3c7)#$84dIp|S9$mFajs`*SM^s2&d)zDX1JW|MV7p%-^CF+UAUc4}Rb{a2WaQ34 z31&A~((2aD7m1gdINu&hj++xmQ#mWKrm`RC^{v;C86>*QIvW0PSdaDpj;l9#$1!4w zW1`sb&>8~-N*-C5`TV2ZsnKOl&USf)0%a0Q#nC7f8Jih+bN{eu_StAfWW{l05fO1Ai5p$0m<1{e^ z7Y({@*^k&e@V8?;^yhf|4R7UXEiG?)hCb}?@P6Q6fJIKP^P z)=j5VrARJ64Vg`Y@?VJMEn9|qc3WXkF{ZX+40?>P$V&Ahj?q2?jhwFGfuIJ0pX4%v zsc!js0*^}R7PTz)QG03V1zWOe4hl)He~%d9e_w+^Ji9!G(Gn28lG}dmcp}v2V0!C? ztr%$m;XOKi2mGcZ1=c%YL0LoURYq27F^ED&y}KuHL`LTQv`bAgRw3lsPn6R`Ja*U! zVOwUXB)l%x(u;%l2G2U)*kLccNYNG$Ax>0=TgA@Mb8of6cB%ck*$w4}o85=p-(9m) z%x?CQ^paG@%9J@%3-59C4@2K}y;9y<4i2{NV{+f<1gKnbD>@|g=-`!CcDN4*-6^_M zIbSyO3ZaYTp07;44pCWeQXx!zr+Ee&oM0{t5%xiO!keXd`S_f2;#}bb+ZXma*)uA| z)R8HK{qrvJ`a`=7@{*M%9B4tCNVw*xs^|9Z(GB%~&WO5k@r9*VM*EEDt}zX~->-WKCJ2js@*e4X;Pc&ToZtQXKg(=S z(VIYX=tBU>nHzItI0?#?z{11~_O?nHz+>mp>r!seCJ^7&+(tGYxacoxo(5wL{LM_& z@;gLoG#T6zJjUrxw0%&~F#A+twe>?$7(hG}`#rI`xOxEe9B@cvuV^bw0jXBemM(Gc zvNXv1&+G}%Dl%7ghkknGqIX{A;rVVo=?z{j#P|#4MT^X&D?@{psIIwS=)(uC)!DjQaw%!%A2^R%}WzluWn_5u7cW!-wY<-3mu7a$lZzNkd?_F z6_OH_F|;t{b;Z_g+e|PG40kD-x-&3%d%Bh+ss3v%T5eVERO@;tV9XVb?{EAc_TDq9 z%CqYhCNVK4R$^2@(1>C~K~R(;Vgfc$KziE}K%|36Q;?FFC`CZApa_B>0viF5B1OQ2 zrYOB^Is#IpiqfQ>xe$!+`@Ca(b#rsN7pVE=-}F# zux7s>PRy;&g}5(M9Aq^14t!p(kM_2ez{BLwJw|QFk#LX2<2?qc7)ccPGw#Zv=&Xz! z=>(03Wzvvj8tNBp2!_Q!N&dw-zW%?~&mA->SvKd?j^ycXYe)#6m@fX|*|(lv;Xmq5 zCA5w!3qZ%f*6P3fWUy$Qcl68YUnNtx?g^`(;dh-qui4ZcF_TKfp-+c|%Gcwz<<4mV zz=Rn39Fv!?LRvR71`6)3r?>umIu*w?)4W$swcT>pO0R*s%YsH03JS#qTfd7`qV9d{ z8C%fk(Fmo6K85HIp}BFSpCPgX9FUHo9qQ7mNX7$lYBN@68P(ko1Zkydv*@bEv@aQwk@o35BrfdCQ*1u z%gGdrW&m-}hC;`QH|9L7XsZ%MiG6Vx2ZpMK#%AE85iC)dVi@{=t0D%;I+!x}1XDew z3@DD=mdUIkYicYSem{#^Z9ohIgENDni2$=&$mmyne;>P39ow-TrN>PdwA_67@r*XSqPr z?+-RE`2Kr>KDHm$gD?WFniOE(ieMx)lessfb`(Mpkg-)HSAzGg=%vD{wl7>|O@DeD)$LeyS+FA8mCsx}Od9)t>u}Q(#ngWf z0P;8UStH4WoSJxY6axG}#NaC1!dZk7E9yw# zk#iITIl+uRqNp^MAt5aRe(dcSnZ(VE!;EPFW=`ghE40Ew!RBp8$xtV6j{Fiix$a+I zKZaL`{m5nrZ-igk79`DPZd)(ixzJ=fX!V<#qG(p%f{I;s6Ft*Gu_xTqCsx19{F;ra z`=Y<`1G6=+_=<=kPt%W5QdhRvY-yZv#ltZ>pE51*mWF1qf=;PdtT{kf5?cL0uA4ir za%fLeIpC0(ss^{k@?~3*QwO2@V;kWK9hPWq1+}57x#si%a3OdI_sN5K40YeBC#(I} zL!wS-`nvnAUB%?T!c`01Ej_LVd4|sf*1LRA7`_ZPc8Wl0Hf+IEHk_hLz_kJcT;Fzh zoLs9E?K$2CsR{rjPWvumktsurU}>PG6|As%FHT#uOQ@IzwMF@Sv6I~l+FWmJ`h<13 znNs1nKnxup9RJKSgq@f&>dt$`$JxT($V;vCY7^d?F=mS+8xb(s9xQTuQIK}uQy@R& zBDGa+UhFOS2tX*f*cq}lYe$zCCHKRMef-idskRwi!hZtBve+3}S6h)BpaaC`Kym6W zBZg19iMTB|teokfd8%ZyIY=%zC9%wL5e)c{9*>U8Bq64Tl)!2*4+o3jk9~I(ne^!ZB&xH8n4`3d0tqolrW~0{Ke)fQwBuDqf>qhfYEW zPOU|U%Sz0kOhH*Jm{9v3nA?kz6X+*HL$DlAFnpt6P>R8&O>b=$t=U_>EU^J|>rnkN zPCeR&ZglD&L(yRFjAZ`;Dm2qQ$y@takf520Vkm7T)ltatZ;A=x-GloIy=RFum52a! zo$|KG|ME&D|HWOX(*&`nwiXEmmCKaOUTUV|*#vE<_URuMq2Hl-49e`CiM{}YNQDcC zkF;4qjhwxvH1sMx$OBs4XtI$)oif8%L$+XwQ99%@B!W(^bRKH~W?zuA+j~ePvX|{f zfm;Fd8~8y)gE&ba0CGp;Jt3-DJc%`+OnBhkus3~rp!X|~3_U~??M7l`Z|W~-G>Lw; z-HE_9cENgtBoP?<P%K@yoF*G_f;$4h+?&10(8Syaye{XqxV%PKTHpmEx)5hDoO25bhu`>0MJL4SHikU9TjUDMP991X^xG?Fs`8JgfRhZlK3 zu!7cjl7DLlr>8sJsQO;zIwS|Ab0<&wWNlIkz;B+Hl2#ZPBrCp!rjT@XPY%K!@8Z(6 zC%VKSq|{rW)rZmslbs&NWB?Zdj-*;JRDb5>c2dZYYqz#8_Ix88?$6n!B3Ft|&MK@o1@43Pk}+P>-Nh`_wXBTDd_fF%Pgx8bu!B19B2OE6J) z94{su5!MXfM8-?^7fM7jCK4>sIt4%%77WqYik$ygufhQ7GJ%uYLCn@KGNRvnznInl z^&ri9f?9CVBuY#o>CiSopu(!nm{mY>C}Q5aYN3DyC+ixOUtV4g1#K#Sx*OLk_@a{~ z2~eGlbU5daz>gT29(hYNi0?l}iboWJ+0A{*P^Ul}sREa;Y_}qSAdvuXV89VkF$DG0 zl(55Fi8F7Wx2-tgiDWnm+58e;RP@!~7NDms0?Rq`*Nz>xk7DwK|KRiWzSVynK=OjI zRQr$~6$>b?gnAS22vf-|!!f~Qq??9>5-c z&%rRN>&pXN(xq;;@{DYzN{}2UP*0Up?*S55rTqCoa4<5cYjddca0Pe_6#XZX#}&cUX=LBt z&-MBT+z;izg8=Wlp!~Gwc%F&|P&q(cK{OH$*1Wk-4M2iU;J|{I3mA7vd>Y)rZlWd=tH6f3XS+ z3o9YEARZ)0(n$>_D%3rXC~d0J8M5&fAUEnZ&~};_9Z367B*D;Nm;Df2tt@W;?rDsL z@R6kokDCgkcEgfG4N5FBw{R*3flQ|Oglm|w`W}Dn2N@wiQ8IKPTQ(x(ahy=%fP9kq zT2??5qdAb90;uIfCJoIO>AMK^j5s#14e)7ILxx53gfL}=L_C&!`>%jJ@nzLz8#6&s#5vkhZL=zmv7T-%m49_>|!b}Z7SRJjBoeE_;sa` znGujy*DhwMP5FyZP5PeOM=DydNnz*U5XX;^4=kSQsIa@0nU9OB53KgqEwCM_p8wwJ z+1*vJ8y+crW2-6n(WPXiP-B|KX}9hl{*cyYb!=_+mvbSz(o)a!!;^vL8SsY*ctUV2RI zd4f`q#nWKS0}QKvIEFaP|TcoGGli9^NcZ@&445HJ)0)I@O|c1yOAJ)rZ{ z(Lo=>0}};IuVQZfmvzLi%clHG#7D}*zTo08FlUG~lh1TaLh%FE$aj%5Nti2UAJC^S z|AID7k_DxW)pF8IJ4TI^5Cm!hRp`xwqE-b2RVm;nLlDuE1g1e36&-%+i#u>DIBEZx z|59wAN$n_hx}lBoquKUvkt?t$dBZv-PcPFX8nb}~Wd8vQb`i)GkbtN;y#3-6+0-x3 zo#t#6;wTwdl%dhEUIRD;kSvV?{0_w`oCFkxh-J3B{_p#M&g1Q8>}G#V1zJ6!og&(n z4w9LC%S`w-5jhQLQBQ02k$BUdH}d%&a3yZht3I>pNjG8UOD74bgQ?Mtq)Nz4C;ot+p{qsZ5i8L4 z*cp%r879P#og$6s5P109ePjij;_64rUwXUEe5E}+nKA%P(zvQMnKvui5tB_>Ym?ZdzC}UC3lg;2RDgBLW%NKb3q88`S&Q zsN2X^1xlP)f$!AgLF~dwLlF^(joR&T`en)~RQ$xHpdPObf6es{qiUp~wREuKWX&X^=<_{I)16fK_xz_os7qZ#qqFPB zSQ8Xz-xMgC0MY5Ee}x1&R^TgG93Pr%p_%*cnSYwHe_khF{=K5t=fmO4zjo8g>G_ZG zTc)9u4&nr(B?J;{rKlN76E%g)|T|D{pECJA%qj8 zT|@PN+L*fH;oV3*{#e$Kz==354ivYOg|I;t=^sTGfYI2uOhA1U$ACeB@sI1|#*|ML z^m}6V_Bv?gD8yjM5u_M5Y*(xzb{P`Y0ysqBT9EzXS7wB2NYjy*o_^sR8)a9y0ok=G z3OO7=d`D*4U?`@Kw?BnR*X%q$C@SG9c9*&_p~~TBM(oatL$Qk$;n$l6YAh15o{89p zZ>6E(#3-RV7dkJr$`_mzrlv;fP6k@4p`j5T)NdLG?bV_GzJuJfXl^Nr!N*DJF?-9`qcy60*%#V;381A0vxRk$?zkdCCa+#QXtKiBkN%JS)G;)AINGM!+cWdH|`gPnS2>8YtS#Uds-E!Lg@%3d*J@Voma-bo)R19C+k z>G0hZ4_t+wK{#?mqU^djg+*op$PF`w1??J<`O~=fA1XC(_jgkXLZHvyVTe^YYiXJi zpg$g;7@v_!>}yo*Wu!s?m{iMy&RbGkQ7aDZDwh=1@vv4dL0pXTMYkD@=e ziwIPB3&052#9=R#+5pRJMQJM6#Sv;dCAaG6a46rH3qPOZvpdLbx}RK6peMf$RJ7|B zxI*&FqOoP$tK*@bpgBpecKiYj2CDBEGt8+ z7)G|uVe(}Xy8o#?1N&XxTM){3$iHZIu?Z?)S`R@1AE-?hx4qS-9@r{P<)YE*pxW)2 zcfC;ify;P!rr0_PswX;>V51ZcUQ@@^y+gL5BF!`avo4E*w~8x!z?0Xp+q^k#;8SCmh?j{z2nLNBn>2*c|R~v z3JbYyUb*_Beeg5F@sfUyrtlB|cj^kW9sk<6L(OPLZeWIUgqoQ+LTZO zV^4#x2$m2rRY_&(9OrXzNJsxU&91{Q zQAePH!I^msDboz(cMIGTeEW`LEBQesRo;=I)&2GVBnsUCtRZ`4St@ENThPX&J^`Up zNmLwiGPA~~=Saa~QG~u3Y=uo;xxALp@gJ2*$fCTtKbGd{V3jTFNDPvB62j)j&V8~2!$Z*->NtzdgR7{-dKq1HzJJ|)y138IRWuE1!RAnV?fZGO~QEu9<`)WF8r$%t=WeGj zRI41vayuI&<$A)8P6F*-zd>}wL_sf@Y>#tV&(iK`9JE9L|Kb;3M_14xXk9BNdU9<+x>^4_vae>^{XK-?aLTBkuBuv z>bjU)lwN2FcD#AF!m(pFY}Zx4$JtKo$L?1kQ>sXZhBKYIiIb-wq=7K~F!grXorj2s zFhdIqEIu08LrRYZ4G{c9^8i#SL0nrbv4ckKA<7U55>t%MAdpIaWHQQ8MdP7wr)$%x z3$Q2I9w`u|lhUGXWi-o}1~~)imjynSz=Jp$jJN*)79{ECaJZ0Vz+z`|?gJj#^YvLA zLnNw8vwa&1lCGxN@{A8~E1UvVC3`*xWT$iIu%#@^7VoJK!H3UaaR$`C$2c2Ja;7(@ zS<#RCtW;Tc+fz9baio`{eyW6{Q}#efNy&76W6&jaM#Ff^^hvg_zxnZ3j+2VNocg6= z{gOkys@lG_S{-5eYB9rS2E}y}W3-~Q%%TH>?L@hI-&&g|C9SN}imoz^R_#*x<|ofT z*x7%1u;cr6i@rbdyx;eP!^cxb)q00{&t=@r?|7lrl$6<>@Z_Q3xxs;b&@|3T1kHMNyi=$>Z=UAwltX`+2k_u|M3yDCki@SxB^Yzvme zFe_`yt}8LcJ?p#UnL{yFB4KFlN-p(3C(|ZhII*+C zYuhp7_s6}A(|>;(RsQ470qF1eYBvShDqZ+HrWZ(Bj;2x2jZU*pyB_ys1m9g>&UJk@ zt-L2tIT+d3hFY9p*0m-PB4Sq6kMun35m0JxA#(B~^}lWk=yFpL%wRV5Pevgj*ikx_ zy>@CDo(pHO)pID6TSG1^@q{Mn0A>Z$ddPaeK$vmrMT?^BYIO!9Xk`V5l*@VKoG@{K z-;z#$l+{oThD5=;QQYo_Qv6lDv9W~{6fPd+X~2>m3+xn%)NyN|RZQrbSY0@=2r_g~s)p%#rjrfLj{$GSTg zTUJ(74S9fQI z56fuBEM)8Yac0Ve+o|fO_crqSNmXkOT8=Gsb*oa>*1j_=J^gX#o+nBoY|G*&N3I%1 zM~9i)+3{EXy3y2Bi)@;-v_NS&7^B`V641;_PTq}bHASv3DdYWn#6<=cZpejss}JZLOV$RT~39liSy+IGk$`m@L<^Q1vjhIy1#aN@MUo= zBV%uGA03+o5B!ER`p29%wf01UM#@K+Y=uborSW+b_?LMkc7tl=fuz_v%m5d&yqD+u z#b7T)T0@GFXmI0+T0PWC+8Q>1B;UGP?@}(vg-p4o@?u$rh4*A_BBB>W?*Yc}2#?ZC zf;5n?H6HJM0C6Y}@ao-x@p7a+9H=jj28kzW+KC*ZhcC=Gr?SNvQ!>9|RY3}(eSovR zIs@s2gvhiqxHm5VlBs{clR<~3KDL<9>DSMyf$r-=-X;XIKhPUq=n0N`aYeR)m4$5X z6ib5X!}3B(2^pAy!hMu91`~Cpu1E4R$lDTao0H7?yLPa zpO(u0owkB~SE?M^r+E~uq)O86x}H7oaI|x+)Q;i;&y;sg7qsGe*=iGl&yR%3j6XB@ zp*6(7DN#ve*PFi%$r-%;Zd&yFbx)+#_|*XTIf3qpqqWY=K(er#UFo*4p)Bt=;9%FH z3(n~>hY)YrN4t3&D2XtDe(IL7piasoO_5lgRN=64Ke#&HlX1$L9QJ~6IHB}eRRl9S z+5myYBXjJ;OCMZx>7(IHUD$zVtBj)o`to`oQQlUrC$3FgkD<31?X_~f(<#yX=kgY$ z#6I=)4_p_!9Qq^b_NTl3e&*=jzw@L~#7J9PhYYJvY`gBFlE}dNLEW#PsDEY5FWzoA z?i96{IX)?U^C(WqnBUL>Jdy4x+5az%1OcKGy&AgM95o?>x?65Rt}%U6uUteNaF6VOkH& zDDQAXx%^%x9nnqb{OG&Otg}cp%PBKqznzt}ov|uip5m(l-yJcVQvd}|CFbGiD*wZ3 zC@NhLz!N7HQ~<&st+==$+x;?%{)CxL-)zONk0 zXs?R&Vnj&xvrv(5_|*<;*BTvCClib42ayX(Uu*5k^u3+w6 ztiu2Ko^a6Q0lo?9ytB{QvHn~~koQGyUS5~+x~2y~Rd$e19715}=FxrPiqJRY^Rl?u z7hR_^WTjtjyq%pL0FX6sw|s=OvRLFY;f-QIpia^7@LB+vp8YN(>5F}Q-8G(`wXTO_ z(y^-L>~wOo$O68RoO& zS@}RnSdJ_KbhbW$3{fgJt&1ts1jwodQ-ggVC0Nh?p4Rxp@RKZW{@4^=sA=k6ch@%} zabFHih3w#D=OHOiJjw@+%S!05hQiJR5q1}_sIrHwAK5*WAM)~Mj%4)bn#EE z?MP9}eNj`{2UXR^QK;MtKcN9==yPy$KRShk4|juxX#psEY!D_#e6i?Yx`jkH)D<+D zHHz|ls21yg(}<2m=`IX?u5xccK)@|gYyDSXMjHo^Hd~_)JQo$ZhLvJ;T3XuQj|>|| zo}hi*GPUjIEo?8K5fbsIsSqcD?Fo`l}O7YN-! zEU_@)HKmXgXk<9rW&BPQLoRxfCUcHx+Ep$V!fQeR9e`p&MkeR`O@Yg(RYpc85Jwyp zQMrg}N(RA>T~TKt`Y}L2F2&)G_2iBqkPj>&?D1n^kyro#@?ve*M{9~*Zg!0W1z7#C z`<8)^2eQEME_ZhgpsPBrlS8H8@2l}pL-9*+-nhGH1K^k#4H$ zDI1!H_}@fXc6_N=-Gn&@PtA}YCsE*^=2|@VwIhGl!3T-;{_d* zkpTv;j{$w#Ppo3=k(Zwv&W2ZQYyb|b6}#F4^r}E_EwIBDpopH(A2`n2iqgzyEta(q zJlqblIB<|(1_}RnHxM>?XIM4W>B(omJt{J@|x$13CUP|hz0EO}DD5uEmFo$f%J zB9Dg;AI^`nnqKqdB%?=bd%P|PoYBN<{mGah6k5UBo~jFEnFD6Nax*7F^2wa17l#iZ zT!d}PMN5gWb%BN)kh%10bAn|&@BOPQAP-D|=CzdqDK`cSc;D6kiCqX1B-*w0NcT&T z?q05%`I*QtALfWwL-`}50V`k`qj-M6OZPHbDn-r~KYjXCv@0@99}2($;IdNChoT+k z=Oz0_Usv3M%rqB_Op~z%baAE=gAr2R$fftefmNgb7OL2snIjL4A2n~r6EY|kQCQl= z#q=6=(&)r-;|=yjG|zYKT0nsA(n}?GQK8MX6Cz3(QFAB!!=vB1-^rx;u0mxHOUW+2 zySHyr3z-Fc^ABZgT6FOKw5I8c$y!vmDd@kN*>y-MBLB7qyJAI3wE@?u;%Sr9M;SFW zHP+}x%S90rY_FxwK>vz6h>h-<0sz3zT6XsQHBRRGuYlb0pfDIJk{YSEY5ilNfIE;W z$Jy7*_9*>irR8+P%)Hx@$gF)CyBi(PF+%&JmydX?$&lE!W1jK$tnq=z8e@M;?GxF> z&)@Ps{bHj20k%Y=5uK!;lc#?hlUCT~DXm~UTApjdQ(T|5cV*TSvC6(H{_tDLcZtzp zXWKuXVBFU}I(gyca_9WJ+8j>4=Az3=@2aS(<|4-m#i0sFK_B#1DX4}{v{)f$4J}o^ z9Pn5_*YhlVPxNu(0JxxGzf@}G!@Qtd(u9n%jw>nsNHvssZQ>RbvSB#+5FFyDSg8Hp zK*9{6Y9?sn#DfVkA2>jEXfVA1F74uAdjai4cMEDVFRFTx#DpQSLSz0iVB0>xUG_MZ zX*{587^13pBwmzvz)@EWrh+`E`#o_OsYRO^y_zsM_sZGXCbWN$-|67UU5Sxt1BG~r z5i^*UoJTCBk{EBR6ihvb@X5m$eowgB7>8rq2TM8+5ijh)gN+z<%t_x0ooU}-1`01O zgrIn|4AI$+f51OQk8mRF--LzC>2VKt0{tGsvWOC6l!&ESuG7_!a4i43@Mi2nf`~PW}}ptJO_(^<5uujv}UpL@}QG) z_?R(*=Sa~(cuf;7l+aEt$s5M%(5U-nFOk?runohdpEd>cvGuiF+ph)QKG>?<) zWg#`)2|6m!Q~@atT8_&+SIh!sB^;kHvu8#*1XjSu)6&fUt6le)%&lu+kw}N6cKY0m zz)1gVGh-J+yeU;^y(BfEhb(=&IEwDtHa2gl_q6I^>qT)!6jh*h`kd=$r2nVak*Fsk zB4SM`G%|lP|M$1)D5hgM6mkJkUS9N0v`0RCcy!1LTQKuOP=BJab5APxXCA_!abcfc z9^0{5WN7!pkNoeuZ>p5xvandsFOHr zk{g8pqsGI@ARqP9Dgsm1^Yce*4kcIYytjp(B}Km}xbfvt8wWS~EVD$x#k9aTzzc^Z zX|<|&k7 zCM?>wa9*S{`Z-7lojMf0^>!@Az=gFFKbxAF-5Fs1R!kFw^3eYK6Cm)TgBay=2mqA< z0W+vWVSLeE=!MioY6i#nbO*pKTPPdEE8H}Ob~OVuxC$Y}aqK;Nnnb(S&WMnNsP(^h zTm5N$?RUCSH#pPN6B17Lobgx~)yWpxlpq})a=!0Lys04V?Was?C`*=oy9o{U|Ln4Qv{>K3~ukf(o4kdH;!Rm{4(0y1YwrC;`QXQ zGKZ5WKq)5?5is@MKP!hc**Va$eyV6F*62eZTzJkbgZzl-O;;y#Y)kq zJf0utG88RJc);(E-Q6*tJqIN3vgwb((*z88jGvz#Z;E@8dDC&@$rflRd{ND)IN{72 zfVG3@cEy$KRJYEMomhn4NTQ-x9YlM?D7KGV9171Kk4FnB})S?I27Mh_4bl`TUBCF6f%Q za4z#Mx*%Bz>i^aPc@NmQ?g&c-Vxpj9 zTYA7F6Rkn_y#eBMgd&6r^&^XZZtP8v5_YkFrxScX^ZJb)NTP>V+IIwra8tM#a$#R1lEZ)i4TkA64%wxwSMN$i1GNvRj=KMv%534IWy`cmy1 zYBjDXi;qHixygF+iu4i_`xvCi@z@z}aGk}e8JeZ5bZj^vR2+_BI!Pet7)7uekzhyuo2TyFOvzJ(8 zW3jJpWl@y7yXVIIueLu--W^(W>DNN1Y{C9{C0eHITh_V?#%OEuSY;?SI<`GC+CP_k z^>9(lN<=TzQ|S?OPRb=Bekki&L7?*F;ql|mt9~`s?dl6pbK+I@?q%IPyRpYz@O<;a znfP5KH9Y}H$I`Zkj%l#&mw^TSEk%$1o5V`_6p`d{PH& z$M+v;P%P-vprPh33^%lvG;~O#HLy#8WOg_;Qcn$;>?nB1`%*2B^m5qWNa+> zj`m4qg9eeVswGZoX|C`}Sdw@0P(`x+;{B%{KPJv|^iu7^1+F5_?I%CBXQ_UE48RdR zhB0M!8Nl(Ym9N%zW#WkUum?G!tPB6KgJ7}=*yH7eTzDlsZi(~ic`Onl8JVt&W4o!x z3B1Dr9KNAeD8?4Y#!KA&K3xmFMcmGL-`04|X#oaHt-+b(KY?@v7>@@+B=O5+IRda3(1E?2EkqxUj;R%@NU3oxdD`>o1otWTv zwyPU(1c#)fZOhoOp6}7hpoB@Tk~Q2ozC4C8KII9{J<}@5uD&q5j6(qAlzQ=McTuH5 z*yBtdL9oZ=cB(WfCUhe049&7UMz0WmcKOZ~DTQLkBOy_=e)iRO8^$n{)#FqQ&f7@= zJfEAUBb}w|p_*{n`VLyDvUcQ+r=I4a2ci{}*U>>9kYt6-<+Y*hJv8y?5W*9afwG=3 zwL3{C8YZ3L0lv}HI0|_s0E1S*dFKP^XpgaS=C@4mO{V(opiQ?E4_t?=>_11I3v<^7 z$k@Xk_Y~GTJ)}D=Qi^jtq7};8LgN_9hj9^sof2)=mCODRjtH{XB2(8|Diw(#0=Y2SwYK(gPtO~v)IG_%aFY2cBJhlCXg>j#fKD%0QST~YfgYJ@BVNY0 zan-=U={mZNfM#(FoGn>q_&E82nrD>&^K#QoPreC=wZfA=3{vZ|( zwfT?>Qp-V9CKSs#2+16jYCSxWA#NRqeLV^%LQblxX$@MF?UJ1KxH8o!1cFD=o2-ZD zs5SC}2Y;zT2h4@0(=+FXqIC&b-WW@9dT74h>jc0%n@dl`>mMLIegJ7n9&7_wpa=G- zPCwSM4zO~MOoaV>fCI}RSU+ms_*Bf=0=f@mM-DlEe!slzpA&l;Y?U)MxhGZZT(^As zz2LOpf)r5cB#TqC;t8%g2i;08GQBIEQoV`gp(!b6FLi&~8*D%G&K-7Kdys0)Xm%uB zrzQw|RPIGRDUK}I-*hPiOD^v&tewdh5kS2WAv`^?MZaqS;q4B0O7b;Awy_S!@{ANu z`vVmw$b91e4gdto1-Y=1j?2fk_%m$ z2>g}b$~7?D@__{%Ke(x<8@p?foVH@d#<_`dsY&J9B;cI~ff8=hN!hVov5HV%1d0~) z!g|%~1Uu#4MS0Cn#rga=|C={A%5#iY;ElJyTI@2g5E)mzbMK4c!;>u6A6!Rwrn#$zZYt)nerQ21Y+1WcHsHX00}^;y`bNdq3DXq zyF&Uo?xfvxpgv2>%d}I`HW3EO1*b$PVM$17IZ(`o-AmwsL^+hoFQr~|_M_3O#p7~2 z+VApV_j1P+!zA*73p&_&B>FW?p4Hqo`Fic93x*mhKqqmm<%!~O_Wy~eUQytgsZc0rxEmJl0& zJ_n#jLY;a5^jE9fyd{Sv{$Bh#q3dq+%ADcbv*3QUNU^PbkWruR8n33hKvRe3FFoa^ z46@W_yrhvS`Ho3_YTPuoK_F=b=h@W!5poTS_bMSlH=sm01T@Q%F>l{Ox^CUke^%L_ zodTFw|pz|Eu`>d!MJDwI6r?)ezM|+#Oh{|RW{OK^6`xLg? z&%SA&{>sh%^FXRkfAK%u=zoVc|KCUa?>x>I7M}=**9j*-KDyk~Yqb+)WoVnKDo;?5m<~0S-u8>cO-28*5&CCtEI-X@b)JPGjEGqCEWksVc zg*d*YO)0{qXjF5W$_?`={=cljK9e=opZoy6Rn(xWE?KE>w|B1Q( zzlAyTVCaATPww;4qmeN&-biMW?KvgdP+&ku7)-+pN*b2+3wzEEg^$&4p(<|KPDr~6QI3FD?a^h^^M1Kf#9M!dV!PVs>4)g9|1P!w*~e)|NYVb{iAg5 z_;BNSa`J`WtvyyJsIcYbMX=QeRV)O9=YUMbvZ;d3Z=xj(I5pZ0=4t8z;P$&@`#B#xBp7KhRT0g?r#uHCe zkT`Qja-&fORPI2$2awF>6SM&AgDQ7?kOQ?vAXl$^{j|2Wmed}F-DrnFBR!$hVJIph zOktiV-6i(!Z3axvDYBFNEhufIi1bF3;^K=bGeT*{3!`{?OsGt}f4MNfw<6&_RC2_P zkzR@}l8Od|Y7b)3kjC5BbXPw%l!MDn4!n0m@dLEF0K1WfiSqVEC{Z9Rv9ECq9!mT4 z`rU5Mik&U0Rn-|Qpp9wb<(>J;;Hkj~FbjAmDZos^(7N4t4mi6m5ll-&U%SanLjgLI z(S!NkE!yuVCqRo;B)W(wJeT2w;9Tg;fG&kL3IP-HA`uWJ_y@ChU|U@VHdN`<3&EU1 zB2;{R96L`P%`kv(3{Td0Lw`#W>x35|{FKr$D$I%BM18F5fyW>sZtq0zs@|qJm!NtQ z_0|qSjKi_VQbz~icsxGt)cK~~O0Zg3{3~5Yv)AgAItc90!F=#Xjn?hAjq->jpl0s$ z6HrG$4R4ZRV+8#a)$21I8Tlk&;kE1iKH0UO=rh8|*7LTYmhvGyAs+3KjvYMKIqcSz zs_J}Sy8~dFxu#;k9jNYO92%Q1tzAQ87X;ZMns)La4%ayK#KY><(j09MjY1^x3Pih= zbsneDF0PclZ|`60YWRS1#babWnv15OXxwu)n6y{altps7tsCmDz{q8WU3i#KUNmu>80u;cv?`o|u~c59$RUXh7G?2hW?eKC62P&Cr;YlHTAABua_Q2axoeL9 zr1H~&pJaHFRFyX+z%S z_z(E)f-k4`=9t8DP_kD-nmGtj*L6(=bOVE|e1Ml9Tj6;B}`m;+LVZ zqrsIrcdx;b480QM%|zG3U|OKx5!TG-qkb2G;{vGeUr=UMAnGt;Bc6kQ8N^A|4TeL& z8!ZPFsM^RRfQ;eD1`9{v@xJlKNW0nc9j_x&$MVJ}B?kV5h2blQAkEzKO$wZ9v!|P- zFkcBq*uSIq8V=Q%;MUgMJUSwej`%HGwnSBE!MzYTqNVOytP6X2}I@n$7{!9he-`0FO%_mM>o}a_SXF#HQnn zD5jRW>b2nDIACJ%;G%)J?Wd;W+G$uIT5qvdiT<$}9hvj$ zh0vrv8Vm?o6J|LXrr2ZpIay3*7bm~15Low3wed^R5-jT!B>q&5o35ux)2=h zxIp>b-gF01{utJ7(&`QXl!z8IbqWAl;!rT0biu{gIQ|;A9%xS`P_DKeDKtwOe%*gv zy0kiTmFGBc!Iz6BPk1n?AAk0bTS5L2!%zSJ*``d887$vq;c<)9W)S@}we5H?Y33^}K?E# znEKzxfku7P$f)M8Te4UQU$W4jorqC12Y067+sL1Wk#{OuXf13z+Xgr$0weGHn7X-0#E#UZ`iZdKNF0F9?y zIf$z()17+1ifq)-Eer!7ahR||-f39995|ID+6^r9a)Ak&fimbSxpx6aHV_j&bZBcs z-nP5P3+tJD!67C;tpR%k8rpF^Ai%Uc8ZQa0ChLYUW*DD*F&?uJ#?W9F3q&4I*fa3c z6mpses^J8i5KIlic*WoGRYn@qFqC+ljJ9?3NLQnAa}pa~s5ptqCvVNd|=DO}nk=SvzezQtmNoJJiJAYjLGh zrwjeC#nDD$fOVm*i+LJArM8eq798R)4nV`B6|esns0-uq*lPl=n+R^X*q;<#6URaq z#pgeJ_I=h0TP`~=eJu-3z&IxFBgAhJ7A{-4VF3oakaY-+=ev0BK=U=2ARdF$1k&qU z2z)0o=>z)*_eS0u$by7k)nRaT!}li-Q0#$Nv6{^Nu%F72dqH7fxZp}kYHAeV&oQJ= zLufkTs-hLrV6q=a6BEiEQ&y~ngI@q9A@l}>bpv1C73gLMn}aSItb#RdzNZSp6f`K5 zz@Kgi2?%E_&)&0&h>c5vClTpO(ICCJcpt0{GQcWc#FQ4A5w?8e(ML{wk9+E#C;%7R zSs_k-AKaoP&ENk9=jyJSga}$X3l>Yt=xy5eiRbq{5u- zH}zjb8#_4(qY9CeMj#=m>g&M%sKkt5a;1c}amU_b1dd+$TMK{qK?$(1g~&?v zi3F8swe8syT%ZHv7(+U8gzWcdfDK3tnmtpc<1BD%%i!;zos*9X{(Rk(}?Q0^8Br0_A8EF?V@jqzx%5XUSG zAa9&`CkkCM?tT%UQU*SQS(ZC!(kv*{*1<8dp|}+B^^7^>ph`V(b-e{Yo-{J53(Di8 zQ^7vscgYX0c9NR%c9PU^wZTPeEcuJqDq|0xI`tsgs>#v}9>!)6!rSW!AHv2lbNHbc zr3jDDc9$`~4dlImY)a5>5;@{g$b=Pc+x?^odx?9nX?4Jj#J)NQH+z~ef3LX4Mh7Rf zx+-GhJ!I$Ni|SV}TtR>J(9G^3q`}YY>gt;JBEzSlJ{L2gjJsrjI5dd7t(Mj;W{Ii; zg_etHFyC#4EC6BpYK$PJXu;cPv)$cjoWV@^!)T}DoaI6X;O!4!2z!1FfXz$tGF@x3 zA%tpnu`>}tQN?}m{l%R`qYc}zO6mTaOzMnKh4r9t2Rs(4zkL(i;S#g;i$@&E^+iSo z;eQuWbbh;|SP;Emv!j|&LpP6X5INdoHFA#4SjAa1Wj37dI*tPY2a@{1w&|gqxf< z(*J8qKl4qRIQWnF;j7xp_8u`U4gKq0Z>LzSifT+WlU`@kFY-smpaT7k#lr09Itlv+ zZ;lxH3^O;-?DEH2G0DTob{Oprzp&E-2l-{+%~*&tkStENznL)?j!fE>t6;pXrSk*L zt);;{hTqua%c%_ zYU%vhqfYf7^d5J;`{}VCth?1#)ohEyi66G)en4XdEiG4#!2RR?;Ypc8)`v4yB$M(}u;@y*&UhqobX8D9QT{{axW#eBy5T z*Alj!Auz^@(97V3BS*jvy+2}Lv!ER7pnOJG$IHz^HTIKIfF8&G%D?|jRA~ONfcJ;N z!Ci?j#kWTteo^j<v>o+_yz&*!dI^oX#8`GW1 zuuHfDWBjgj=A@k+2t^uV+{xeXGK7JhSiUh9@ftl&CTt@cb4 z@8Z9ggMh$T&pah*krgq_D%Bem4>dn{wxh6MTpKqsG-O<}iycYBzoOE*K(5rV@4}!s z-Jt+Jv%z=oHjj*S+~{G!NRF&x(L-{{hg+;=hoUCuf7!uXKMzBEJ^xJ1KO5D%02;B{ z{){r?Jbf63zsQ2g3fVsoqnsiUq-gFc4p=!JCnS2(buV8=>m;3Xa!TJ@1&1><;8w77 zU3=e;N5Lr)4o`_V$<9=&u2D^jWj=3c$WZEcmHaxy&vu|C*DSe;JYr$M(hZ_ZzWnY4tQ)h!4(vUD zu^*0OzJeBfXVsG|(fbu^>T(rUK$Pp6ROg^%0Rw4T{pcR{921N?0t3vpDE1g$EWoI| z#l7z@HtR@k-;VkZmzazQ)Ma}VW;|SQ+k?RoN|>WKGO>lr;ng3feKwj|^=-av9}9Wg zVou>VOxEJ1OS8H}Hke?&je(;MJh2p$ospnb!ng}#lQH2$iwu6P#hg>Am5Kfk&W$+Y`h2rZt{_;GFO{^C)keh&m7FDjaJno z2Mhw{Yl&AeV+;Q7^hB^hEGrXV@21mJz_P{!ZfTa6#ZQxh>X&Ha)8zi+CXYw@M8Pb1-+ry{hNtpUH4RL-VhB%TNCLdoL zQz4$L0dY>~cRJyl89!V<;*)*b=T`-|gY8O;%XeX9I40d|Kw|5ELAz|IZj~y07e4<{ zot1y$FWSmzQiT(iJPu5ob5no{*E$2#!Hx@{k;1_IZIH`e%Z^8KA1(ri`$e06JfZ|Q z9-UYdI9%I*@cJ&&^ZMQ!01z$62~KV#1zI4+yTYp#6)Mu{*a0x56KIf5JOAr(YoEJe z?$P~ELc9M-Do?i1mUhDN()Pmwi~5w~zz0-ugb_|dZuanHOIqz95*!ZL82Z$LwvVV( z61r^+vmh4t6$nnTLp2WP?dxK(ZxXIgxNIxLpwC3-aeHZ96RA7V=kot+Ww&wesAnr^ zgLP^c(kw$WQ1@h_)%FN3K;*k4EJhT5yC_TPD4goF-=+q)+?NUF;R*R@q2NJ4r=cwZ`yyYdZ6kr zvkoY3b|gYA`v4c3whL{X>Qv4RB1@Nt4kCUvoaZf#{W}uK)CW?@RF!g^-YDF4YlbAo z_EKe!Vm5ggL2wYlx#(9;KViTiu3Vr}`MqX=O_x4=hCoCtat_b9^dFV&7x$lw@hDjC zI|s>bk$Ui=)_r#bF8f!>1+NoqMKI>0yghyX#S}1cQSOfJ?r7NkNjJeIMA3A+AN-$V zP(C05sOhUkGX+o4Im}~S+>3I(3w;iPL;C>Fm!bus;Nc-ZoWsVHD=A>R3D`6u%U-d1 zwHd3wM{L9R$Q_)WgWtq^K3vF>M~5j@VbV>I?hT>fxG7$2nq&%JHgU(n$a~Y+#}48V zqc7}GGs%yby(lIFRg5}RF_rYA*|5NI3iRWv88#Iim`w&$x4LAHLEfTtIw(h*&kB)~}H$eRLI1duUF z!upUO-VqHXkc8HJW@9;_WkjOK3dc#qTLl{72z|v3j05YG!X!BY9I04a?0UTdUTw$^ zt7{2y!c+`ECwNP6Hbe*PJV1K|dWtVilf)NayM8_k$r^#JFx=ja`sn59VtU9`fUFlq zEL@2pIOvKf18YZ!R(3Ve?cVBmX>z`VsM;c>ifTub^1Qdso=DTN!onb;HN#%l%5ur* z9ehZE)ub$E$8L;*fL!@P_HU+ekA(1dVFvn~(Q|t%+exA>4(sI+Ht#9&@1`IRRbC}T zbc+lK{sbW5FTa!j4B$SlT8gE>G`Qz?AxID}VUJ5)2* zwEql=3IoOhJ~Xxy@MX;yef0U!)4+F?VQP&MPYHIF>n2q1qv-(#g@MhsVSa#N7xe-U zAf!8a7=UItIZ^oCCyWUZfyUhSj$wpn;`xtf08~WbS21q@*_0xZ$mTae*bD@Q;k-&3 zy0Q0OR0@#TPLD4O%bo%roLMTxPl~ut3 zXbZ&~JYAZt3S&!TAz_ev1?@dowj&F0E0B@hfBr>djR0xZHPC!YM2nr@9HjTOa5?He zW4JV}II$`=2E|7=D#}Wf5sRd{k@y%R4f$cg0!0)L8@7muRO&5D0C48mo5YgMPyk*{ z=$_do*o+}1uD9I%==h!ax<=;DUA zSlmDwuiUva+5gS9VwafbU*z@mw?^Y!XE$@~VU&{(0#x{TRE)&M&1O_9g(sSsgBINMlM+>i$W3v6GK(P-1_-Jy5{Ujlh*i1Cb z85uIyAKLgPK=FS9)_8zfV%bmes>Gi9zBsvc=za>I+3YRl8JEF!H`L&y|~mZCEA%vfa7W058yAqf?e7AiG}sFX!@DteC_6R@6&j8Fk=Y!%GMA4u@ z^H>d&Df$2`vIh9@Gyx3Db(s}ij0z;HN?@bV~u{}P*P1BdOLze6hw)rJo5TDdy+$|<4%=T>Z?%|L}se6?`^sR zbi}O87G~$Bdka~`j988m8B&ge=-Sfa;=$j1bNZC&A~C)7>qt zGGOmLZsmUr5Pfbs>5AP$<}ZLnI#TiFC!dH>Z`KjLE}IqGHZM4mcHUnwc-lzAH9J^= z7lzIsBy##RNT|m4U=-m92Q2sUK2%Imqi~7a6cQ35ae{vRRAQpTxNs^qc~}QLo7aU{ z>2t*2V)HyFQs3F1v(NzNRsoUS>BYrS5Uc7j#^Sd)ongAa>YW*}I;r zA0K`t&c0Gw6@rOU2?UGQpSA`;qSa3F@}+?g_XrMrB5d{(9NdzCrE!=>j#$BLdwD1T_Iiq%MeN0Pt_ zj$Z{^%Eaa$m+&+wgVB5onApVTlU9t_EIJFZS51eKS!OH3iqDCUd5G<9>!gKC-+_tj zbdFv)WLf=b+x9s)DU9ATSx`}+?e%gGKmH=~C(`vdD9!z~tYTuC^id53=cKWL1JVmfK@0vcHA z;g@#MGy{~K17s+kYCNZIN=o;i9y$07McP=*j!_voA;+rsunn!8$Bo;Ywhb?pA>S}p7hw^To_(IGfsl$!aaq>Q}$hlitGPPZK2>symio_wCVA!t+Xkts{&+bW+L+J4#r@9}n_x#?)Vbr5bKHsWGeBFK*!@Kyr*)Xv? zx)yiSS6!m^VNPo3hm(4J7QAz7W>Z4ykPka--08V_=+J*X`&=!w>+VCzg|+ITZ(Lu( zwf=wiT+zJTc21_&&fd!#3vYG3d%^60MN{*@gU81naGck};aNxbYk3U|VYhB9*H~k{ zJ;+==Ykt%eUG>o3+73ler6-UxpNXzFa>n={vGduy5DMwW@}X<2cLw#z%`u8`I$@}n{g;2g1*@36OJ`m*-I?XR<~&i15tE2T)T*kfiFn(Wo=0_HUX z#33dsggTdToWozKU+_L!d^aC(NY(IJQUqBk#aHwT5@Shi-=27Y_TIn!8xy~64gP1H zAjyZ{VulY|XLzT7@T=V@##}kevs(AvRKCb%92QOhJj4+2qNS!>=|> zfTe4(HgP~tePaW_y- zVE)5v!`$gyyh1DCIz(H^AU9iq%LkE8R>1Z(oHc9K*$SGrg|$d_sZW?-K_gqkX(e?f zgP4cK83J`#jY9a5W|kYOe?!>kB-lW%xmLinI9RH+^mNh+3zPCQ=N7vqL~vUl@jfw8 zgCLerGzzjU+yV`YgcUto(1!UQAj8nn@l22>HF5(pXQ5C+wa@uczDT2*ap)P$ehrBn zv%3&DP1C)>5&I$S1PhpDSkF$Gqf`Cs26y+gs28<&BUn%n!J?RGJH(rmVO15peKkxp z(J>|QwxAkud^v&AF&h9>n#`iP{ltxARX7qp+8k9+*w0v?H2n2p8-H~v+px2MCy@7r_A*z+5xtr+id z=s8e*ZFSK!pzB#MsdYX89lZkZM!LJuPEvKQ9=}rC+=SAAIGM|;pIJM@fDsj#((X*}xyTcr8FUimKvSBj z8nxpEEZ948nV0Z{z%{cTx_M#oV2jwD*ihg(Zd`sF3id0MH*Nx~9i(?>&Yae;x>u=@ zmUs)-Lh2u^sv4Z`#Q3@(S7FAdCo^Z3C7wRJt}b}e@b(e>^|p<1FAnz1U@&lv4fiW6 zE59=aQ8^*(IJWb$a9tLc7|?1Qgpea~hLTqMi!IE~8V^r+`b)u2I;w1aTNHAjLDB{n zlH*>j)goiLd5{vo#%zQ_)oPYTvOOyv`dLP|(hFI&0-7E-i98zC!p9bJ8qH$U@;FuV zsyf4b&Ie8GM?w@+rc807ti6-RaCG$Ny8$$Wk0~|ZtXaPk`6_d^`uK)o1%e+7K4*wT6BZlogFN0<)oAJ)di2qk3)GG8goOxLaZGK{bm0x^(Jxy9Js= zE*D3cKO1M)fE$8kkyZ};(x;`_BLU5&a_Hac4h2Q<%+PSvlr9!tFBE-XVW4g48ngVl zb%66WJTr~LnQDa|4CE#VUnG8#3Ggi5!d6fr^rQk_3AKzP~gkgI=!G`2Z#R!sGGAjkljn@L*tk zEbH~I{qY+#5u*CHhdO)>R@(Wbn_fg&S(TIfa&&RbX=>Tc6fv z?*=h`b*flMaXXW-%qT%~R*03cj_aw5>zA_Pkuha7AhOmUG72mvHvrXcJ$tTLKS(%& z&u`6fD!$UX3OKJEOxNpmt4{O^(6rrCx%68oeRAqS$dcgm^9LUb;l=EjJq3RQ(Q%c7 zuC8t^x~ZWOr0VLlP6hz-L(l(-VT#wS_}PaF%p>YLmVM*v|w6@_1^;5=|rZ)wcf0~gcM^OYfz zrEoEU^*~%g7!bI1Usv4KSenWW@PlNx9ivCX0lCw0UEy%c#6@%)=Yt&IAAi0ZfZ6B` zF8ViVogEee>5UsVqR%1oaS(N}6^s;ass{cra&?UrI(@uj(zC}3aqdpu+GSH|KW;Aa zX)Ei;Qq$M~Ply)`YOCLy$!LS{%C$Szy*=kPj(WRZek30lh+nPrX<`bT0{~?P=;1*a zhmROeKOvw-y-Ltq)I-y-lVUsvKCfO;)=1bsNffyKq4I!G^iOEn{4wFx^Z7hfvlb3S zaXK5Huo?Eo=RTtBBn;ih?=QRgaK4w|T8v^>K}m|dZ(7}9fgW^ze^P*ztFYgZbt9+I zu(<=xJ&ZUhZj$o|Ob6A@K&k4)kwfzp3}AN>LKLUKEpg)%4mpT+Lz-pt66JXMofq@z ze%L@-g^;Z$@dfJUlmg~WHZ>E?&o1bZ%6&S@<(EN*#N%K$tN>L#;2*J8!f9KF1VXu_ zi8FY%;>4EBLc}O)k*&$iV|G?BwE1&%AAXlm3~7vk^)iB`0a+8D#mNf=1O<(x6=mDD zZ_kk{4>Qv;J2)$@DO|SWho5(+1DJl_Eu)EcpExMthwDEYR=1d7vBAFE_p}TyDBTQ1 zQfA}Nmaup~Lnt%ob!Zga%6_#M&NsC9^J6k0G|*!ai@Ac#8(0UE8{ZJ?Fye)oP-H!t zs;4U~+NoQF@vc+e9-*kz8`GinxmCdj5`Vc>_q#oMHbe_UOBZbqB1~)cEPhXbw`mtI z9?lvkbOIQ9y&9ScB;!@$f4VjB&Q;x=U2&^Ky6t#Y?Y0BKOJg4}T7bV-lybn19YL@r zuU^-($r?Ou`t;vh0D}nGDIAg zNKTVl;#`RXf)=JlogV?Y9_HB9#rA)0frDUyTiFy#oTG2mStk2p^cY5*{&w)sRXOr?+MpJ{sJ)T!%Frp8F4j?FgI z;Rz6`RZqMHLReYxj+R%WUhUnnWAP{F(SiAazRPeYbMn;qs^py^r{39B>Tk2he*x-2 zvkXaJBP{+~N4`?-T8Z2yM>*v$Y=%oVRif(wwSz@LrPUo zc)-8@p?@o3okRTm^@OQ>a(7_YHQKO;+~9?>&R$h~c@m&RxXy(60mZ-_v`r`c1m07q2d&v&pX9T@dTl z(8Phs^I{#(+4;qw`@X@@jGZ;d8$5FiBYn@^zqVusK`R^QJ3kww)3@PXyV7DMXJZJW_MLRS_E|AkC3Fwqy`>1X?~I*seSuCS*y#kmGBXS?aC_W2Pm|LfvvVSQQ=CF zDwICcWyXVywxj#@U;3!dsSx^KS{;PHNLVmwDn|3F(staji@z4UhC_Dbnay-i=-~T~ ztzvR6OF}S73#N93!Rvz@RXuYuTxp^1-{ZG`|0=EULlZpl1KE_=^lT>6bgLy-rek&g znXcC_E2gPb8lK3uaVaD`BldlEba=7L1$w>}&xTmU>ES@rMhX>0q84LC0~^ zJrf9}R8^&;w-_aJUNgvV#Ik3)L;|Xx&D$>X#-eu?83kONPEJZn%5{ytKMeWPR-rK^ z-gV;zL)B84hw)j}(Ec<6e<4dcPFR}_ARP6#AQ*ir5e3s zs1SRl*79n56X7OXHtpeQ)j6Xbx+d%E>5VwUpE(J2z|v$*OCc@cRL#4%Bhu27Diond zIkNj;Tu!TvvvA+Vv8sWpl)d2Pn-X^pit-JHbpzMNJk(09!kxIRcvvf@Zx~Z1+keGS zPoiW_gS4dll!H(V{3zz)9rcw{t88AJ_||Z7((>iY%lU)w7Y%t}(vkp_ufPXZtmPl{ zaj)8~Gd4oVpn-oG4}klxvDD}$nN-J0KCumQ1P|lp=qcQi=ypX)BM{T^$C*_y9MJD8Rdbl?!U-xY)7DYeQhHPMjGWj8a2g!oI z;8v}s z2M^iNtrEtC7*sMIV{V$JP#95%W|8%(9Qb8HG3DJR=XD<}+gbeJ!TgdxygV$Yr8eqt zcuRSNc<5oNkBAHN34|9j|34wxaXf%q96$^WW&zk1m%<6hheF!RX_fY~2PMO*MN^MBu1W` zZ!b0Vmnz}^6{W9co3tlkv-osYd(EDv9?rcq(k%3+$0+Wa<%LN}pfgfqLo;KUn9oki zoV`yw2w^H|S27n;>J-|}fCGSQ(P(E?P12+k`f)|$%a@Ocqe&%o6jR>#j&k;J{M}d; zQ*acfA7D)zgR#7UN^Da!U=>`bVhYc--khk*wuc_8a|+`K3x)k9Qz#HOosc$_fgef= z;Z+n|od-RAAu29etoYO+Jv$j@l@8a?$eqqNh$j(g?u7B>E3_b;%FMEU$DQ4Ve72>V(Z~h&VZs8dQ4we4 zxRA^XyXK*oobl-ARaLmLYsL1Nx>U#^aDd9lJg-A9>-gTj-#%}#T<_8^VA;Ci_Dx2E zZ!Y=%GyTsa5B_u2FMUpZn!HM5s7Amzy*<5lsUG+__I#(=U#SGFEI0mf@RFR^!Ttkd zzga$f%=WXXKmWCSzFNd8`G4-+t%=A>$ z<_|Yo{Um<$zgAbRotL|;#30}E<1hRE&MG^3R?+wN;HtU5S`^C1b!NGkW7S zGZh2^r^a&y6)Q7qOpy{|l0D=16UUF=?J+OcUnI()?8@Z3NOh@b+kXur^_3`VEe`LdtsBi$z9B4Td5@M$y|n#iw7=J*#~) zDX$+FCQWQr!V~8(C~|fVf$x3rQL?zO(DP5*TbJ-UU+9q<&?@!)^Ox&&gA7v*VgZmz?8)f&Xy((|pb84>fV_!q0w2%_R2;J*g(z&!WIM83;a zo-K{(_31IAU*DE9o3(r;ZJ%^OLCZ4*ihm&gF_L{;UEn8$?h=CEzsZ*3u;FueXwVRLjr zxRqIeX4m|V8 z;*5~U2*stISTP{mu=Fw+|29T6qZ^jTjM5xEIvEi-KTVgkVKI)kEz84fBFJ#@sqOq&cNpknKm z*dj0WB3sKw)0}*f5{2uzy2gL-@Zmm4%UNIJuo?mB?T*uv7ji-#Hj8O#T)h{1(PdHI z98-4(0^p4aNyI3OG@9R$jqnlYpVvD7n5$dIa;bX)4~jLe1cnHBRy zFkihMAj(JlJ%(*Z%}_)&0+dW2>riOukcvnK zSW_gFVWPa1@Uyf8A;aAMm+wEjo?BN)X8JAEnb!Lz&l!v}x7-b3#;@oscuTkp`)DaU zIc!sql9G?kvl%&eKKxQeyE8nc)xu*w_e~hjUX;zL(|;E#mfJ}O#8lTW;TSh@VDfat zx@W~=-icS#DMY|U(;uFTNey+KH^I!}`q?`pQ2`O*>zsUXB4J8+JItJz+lfIz-(Wh_ zx3K=QRuQ~@6Whl8(g}(n+t(S=g!;92?zS%5`h$(gL-#_3LevA(^GPGDNFrNRsm0hy z)Jd`wiWD}U>0{&%ltn6?>F&hr2XcOHr|P!8%%>cIu%Ro$D^_6XH)y^bi-_={u9}W| zB{XX5Xi5dw4aEKJ4A?F$A*abM4_Ft@suDps%8ScnBz=@xJ`E8a6ND)!)5SRkOa0*hf|yu_B<(Wx52L{k0r%@exzdwFHLh^H{t)l+0SkmML;+rpVT z)yc^TjT_BTF)R^0#*|I>RS;^t0hp5Mx{O*Qj;V)${zczigj|`aL@Vf;*Dk`{%W`gK1+K z(LWta0vwAD)b2U7r6nPB1>L-PCMLNy2MtIGgk+o9cyu3Yc`Oj~j_&s_QcFpM7u^!$ zBoFt;`DaIU*-fV2DsV`ew;lSYb!7}-mBRs{hE@Yr>cDu*E{X)*N#-w_{ns*)Nfd|6 zQk|0Aag7}8?DZ(u4-BS)rw&>|@Zu7BA!4yYX}1^{gXEbN_@zy^F7yQbu&wPY!b=oh z$s!^LhM4|YD)?UOQUp~@W~w^!#rVRShR`|gYT^bb_Je%Ee1~8a81)-JZi_C*aiiHgm_&Sj3^A2QzF(3QFTUlj~N4AEu!3 zYr0{frDZ>)R^)ogpX`)&GomyA^r9<0+lt-o-OYY054FzjeQiaFNJXJUzsu!$<(;~O zJDpHkWZJq}bbIolKd$QADE41e+G3lw;cg}Q^aF~wR*TmUzSQX~cENKHEm-r=*Jyck zE;=AtSy}sPdO~{^Y4SgG*E@^ZQ<$Cj@v=s?p)ufB{W8-t@mg)n(NXLbRK>mJKGB0v zmeHbZPs>(PaR278oL34`gFi-D&(?u3x$2{t;+O+uHc?U|WJ9-XX_b{W zxg&ABx3Y5Py5CuQDXTqd0>tvdBO~H41wl31tz(e~2zHoos}$&>pO~|us3KZccQ#ZQ zgRF{tr>Oi)n>X*qhk;d-Tg7eBcya~oH}g)**v-eFDK~=S&h=5SU(w-yuU3SFzvjr) z-%I_;qe{`-np>q~tb0o2*oL#9xuh`EZU- z6SFdwpeRSR)Sdpa&$zCLI-DkD)TWU|A5T|d6L3@Lgi4rQ!z@H>`SBBnER#H1wJAB_{ldaV8S0oRua8Lz z=v7)gRp)8e$2DQI2}G5-k%Powh~RSL`t{Bt$b>!t8bFoTtq1o%#Ni-6MSjVLJ^lm5 zn7E_R&HVGi&20>$gu2pST>>p&4N!;nx@SvqOPos1BgO_|hS6!f;y2RmZFoVZ*QF30 zf+oc+Q&#%h1wFJH;JdCRCLFFZy(8I$?^VTot=%54Q^E&F@bT}j*8 z;<02!kMK4}m~oriAZ?9F`ty1v6b5(oeb4@77vFOex3s3c9cE063AbnqIqm=U+un|X zw|(d9Z`$@n`_DK2`uca{^5Pi{M;A! zhF%NP*>k|KgGLXJO$j@AlkJ#14$hE5|=gn&dOl|KMw|(nho!fHX_80v922_WC z-^6$BfnD!r{SF;I*rzx3`vre5s`h=-;qQ&r;qQCEYWNRW2QMw121oSmto%bO8mc#K L_SEpnKmPLHBQ*?p literal 0 HcmV?d00001 diff --git a/module/move/assistant/design/scenarios.md b/module/move/assistant/design/scenarios.md new file mode 100644 index 0000000000..a32543fbc9 --- /dev/null +++ b/module/move/assistant/design/scenarios.md @@ -0,0 +1,32 @@ +# Scenarios + +## OpenAI + +### Assistants + +#### Make new assistant + +```shell +assistant openai assistants create gpt-4o-mini CoolBot 'CoolBot is a helpful assistant.' 'You are a helpful assistant.' +``` + +This command will return assistant ID. + +#### Chat with the assistant + +To chat with OpenAI assistant, one should do this: + +1. Create a thread. Thread is like a chat. +2. Write a message in thread (e.g. a question). +3. Run the assistant in the thread. + +```shell +assistant openai threads create +``` + +This command will return the new thread ID (referred as `thread_id`). To call an assistant, you need to know its ID. + +```shell +assistant openai messages create user '2 + 2 = ?' +assistant openai runs create +``` diff --git a/module/move/assistant/src/actions.rs b/module/move/assistant/src/actions.rs new file mode 100644 index 0000000000..826bf1603e --- /dev/null +++ b/module/move/assistant/src/actions.rs @@ -0,0 +1,13 @@ +//! +//! CLI actions of the tool. +//! + +mod private {} + +crate::mod_interface! +{ + layer openai; + layer openai_assistants_list; + layer openai_files_list; + layer openai_runs_list; +} diff --git a/module/move/assistant/src/actions/openai.rs b/module/move/assistant/src/actions/openai.rs new file mode 100644 index 0000000000..82053c4d47 --- /dev/null +++ b/module/move/assistant/src/actions/openai.rs @@ -0,0 +1,44 @@ +//! +//! OpenAI API actions. +//! +//! This module also contains the definition of OpenAI Error. +//! + +mod private +{ + + use error_tools::typed::Error; + use derive_tools::{ AsRefStr }; + + use crate::*; + use ser::DisplayFromStr; + + /// Collective enum for errors in OpenAI actions. + #[ ser::serde_as ] + #[ derive( Debug, Error, AsRefStr, ser::Serialize ) ] + #[ serde( tag = "type", content = "data" ) ] + pub enum Error + { + /// API error from the underlying implementation crate. + #[ error( "OpenAI API returned error:\n{0}" ) ] + ApiError + ( + #[ from ] + #[ serde_as( as = "DisplayFromStr" ) ] + openai_api_rs::v1::error::APIError + ) + } + + /// Shorthand for `Result` in OpenAI actions. + pub type Result< T > = core::result::Result< T, Error >; + +} + +crate::mod_interface! +{ + own use + { + Error, + Result + }; +} \ No newline at end of file diff --git a/module/move/assistant/src/actions/openai_assistants_list.rs b/module/move/assistant/src/actions/openai_assistants_list.rs new file mode 100644 index 0000000000..9859761c3b --- /dev/null +++ b/module/move/assistant/src/actions/openai_assistants_list.rs @@ -0,0 +1,68 @@ +//! +//! List assistants in OpenAI API (action part). +//! + +mod private +{ + + use std::fmt; + + use format_tools:: + { + AsTable, + TableFormatter, + output_format, + }; + + use crate::*; + use client::Client; + use debug::AssistantObjectWrap; + use actions::openai::Result; + + /// Report for `openai assistants list`. + #[ derive( Debug ) ] + pub struct ListReport + { + /// Show records as separate tables. + pub show_records_as_tables : bool, + + /// OpenAI assistants. + pub assistants: Vec< AssistantObjectWrap > + } + + impl fmt::Display for ListReport + { + fn fmt + ( + &self, + f : &mut fmt::Formatter< '_ > + ) -> fmt::Result + { + if self.show_records_as_tables + { + writeln!(f, "{}", AsTable::new( &self.assistants ).table_to_string_with_format( &output_format::Records::default() ) ) + } + else + { + writeln!(f, "{}", AsTable::new( &self.assistants ).table_to_string_with_format( &output_format::Table::default() ) ) + } + } + } + + /// List OpenAI assistants action. + pub async fn action + ( + client : &Client, + show_records_as_tables : bool, + ) -> Result < ListReport > + { + let response = client.list_assistant( None, None, None, None ).await?; + let assistants = response.data.into_iter().map( AssistantObjectWrap ).collect(); + Ok( ListReport { show_records_as_tables, assistants } ) + } +} + +crate::mod_interface! +{ + own use action; +} \ No newline at end of file diff --git a/module/move/assistant/src/actions/openai_files_list.rs b/module/move/assistant/src/actions/openai_files_list.rs new file mode 100644 index 0000000000..8a3f371a92 --- /dev/null +++ b/module/move/assistant/src/actions/openai_files_list.rs @@ -0,0 +1,69 @@ +//! +//! List files in OpenAI API (action part). +//! + +mod private +{ + + use std::fmt; + + use format_tools:: + { + AsTable, + TableFormatter, + output_format, + }; + + use crate::*; + use client::Client; + use debug::FileDataWrap; + use actions::openai::Result; + + /// Report for `openai files list`. + #[ derive( Debug ) ] + pub struct ListReport + { + /// Show records as separate tables. + pub show_records_as_tables : bool, + + /// Files in OpenAI. + pub files : Vec< FileDataWrap > + } + + impl fmt::Display for ListReport + { + fn fmt + ( + &self, + f : &mut fmt::Formatter< '_ > + ) -> fmt::Result + { + if self.show_records_as_tables + { + writeln!(f, "{}", AsTable::new( &self.files ).table_to_string_with_format( &output_format::Records::default() ) ) + } + else + { + writeln!(f, "{}", AsTable::new( &self.files ).table_to_string_with_format( &output_format::Table::default() ) ) + } + } + } + + /// List OpenAI files action. + pub async fn action + ( + client : &Client, + show_records_as_tables : bool, + ) -> Result < ListReport > + { + let response = client.file_list().await?; + let files = response.data.into_iter().map( FileDataWrap ).collect(); + Ok( ListReport { show_records_as_tables, files } ) + } + +} + +crate::mod_interface! +{ + own use action; +} \ No newline at end of file diff --git a/module/move/assistant/src/actions/openai_runs_list.rs b/module/move/assistant/src/actions/openai_runs_list.rs new file mode 100644 index 0000000000..631b6ed4cb --- /dev/null +++ b/module/move/assistant/src/actions/openai_runs_list.rs @@ -0,0 +1,70 @@ +//! +//! List runs in OpenAI API (action part). +//! + +mod private +{ + + use std::fmt; + + use format_tools:: + { + AsTable, + TableFormatter, + output_format, + }; + + use crate::*; + use client::Client; + use debug::RunObjectWrap; + use actions::openai::Result; + + /// Report for `openai runs list`. + #[ derive( Debug ) ] + pub struct ListReport + { + /// Show records as separate tables. + pub show_records_as_tables : bool, + + /// Current OpenAI runs. + pub runs : Vec< RunObjectWrap > + } + + impl fmt::Display for ListReport + { + fn fmt + ( + &self, + f : &mut fmt::Formatter< '_ > + ) -> fmt::Result + { + if self.show_records_as_tables + { + writeln!(f, "{}", AsTable::new( &self.runs ).table_to_string_with_format( &output_format::Records::default() ) ) + } + else + { + writeln!(f, "{}", AsTable::new( &self.runs ).table_to_string_with_format( &output_format::Table::default() ) ) + } + } + } + + /// List OpenAI runs action. + pub async fn action + ( + client : &Client, + thread_id : String, + show_records_as_tables : bool, + ) -> Result < ListReport > + { + let response = client.list_run( thread_id, None, None, None, None ).await?; + let runs = response.data.into_iter().map( RunObjectWrap ).collect(); + Ok( ListReport { show_records_as_tables, runs } ) + } + +} + +crate::mod_interface! +{ + own use action; +} \ No newline at end of file diff --git a/module/move/assistant/src/main.rs b/module/move/assistant/src/bin/list_resources.rs similarity index 94% rename from module/move/assistant/src/main.rs rename to module/move/assistant/src/bin/list_resources.rs index d8a93d1956..5c3a4a85bb 100644 --- a/module/move/assistant/src/main.rs +++ b/module/move/assistant/src/bin/list_resources.rs @@ -20,6 +20,7 @@ use dotenv::dotenv; use assistant:: { client, + Secret }; #[ tokio::main ] @@ -27,7 +28,9 @@ async fn main() -> Result< (), Box< dyn Error > > { dotenv().ok(); - let client = client()?; + let secret = Secret::load()?; + + let client = client( &secret )?; let response = client.file_list().await?; // println!( "Files: {:?}", response.data ); @@ -49,4 +52,4 @@ async fn main() -> Result< (), Box< dyn Error > > ); Ok( () ) -} +} \ No newline at end of file diff --git a/module/move/assistant/src/bin/main.rs b/module/move/assistant/src/bin/main.rs new file mode 100644 index 0000000000..69843c1ed4 --- /dev/null +++ b/module/move/assistant/src/bin/main.rs @@ -0,0 +1,42 @@ +#![ doc( html_logo_url = "https://raw.githubusercontent.com/Wandalen/wTools/master/asset/img/logo_v3_trans_square.png" ) ] +#![ doc( html_favicon_url = "https://raw.githubusercontent.com/Wandalen/wTools/alpha/asset/img/logo_v3_trans_square_icon_small_v2.ico" ) ] +#![ doc( html_root_url = "https://docs.rs/assistant/latest/assistant/" ) ] +#![ doc = include_str!( concat!( env!( "CARGO_MANIFEST_DIR" ), "/", "Readme.md" ) ) ] + +use std:: +{ + env, + error::Error, +}; + +use dotenv::dotenv; +use clap::Parser; + +use assistant:: +{ + client, + commands::{ Cli, CliCommand, self }, + Secret +}; + +#[ tokio::main ] +async fn main() -> Result< (), Box< dyn Error > > +{ + dotenv().ok(); + + let secret = Secret::load()?; + + let client = client( &secret )?; + + let cli = Cli::parse(); + + match cli.command + { + CliCommand::OpenAi( openai_command ) => + { + commands::openai::command( &client, openai_command ).await; + } + } + + Ok( () ) +} diff --git a/module/move/assistant/src/client.rs b/module/move/assistant/src/client.rs index 3276edc7a9..1c9fd0bbee 100644 --- a/module/move/assistant/src/client.rs +++ b/module/move/assistant/src/client.rs @@ -6,36 +6,24 @@ mod private { - pub use openai_api_rs::v1:: { api::OpenAIClient as Client, - // api::Client, assistant::AssistantObject, }; use std:: { - env, error::Error, }; - use former::Former; - - /// Options for configuring the OpenAI API client. - #[ derive( Former, Debug ) ] - pub struct ClientOptions - { - /// The API key for authenticating with the OpenAI API. - pub api_key : Option< String >, - } + use crate::*; + use secret::Secret; - /// Creates a new OpenAI API client using the API key from the environment variable `OPENAI_API_KEY`. - pub fn client() -> Result< Client, Box< dyn Error > > + /// Creates a new OpenAI API client using the secrets. + pub fn client( secrets : &Secret ) -> Result< Client, Box< dyn Error > > { - let api_key = env::var( "OPENAI_API_KEY" )?; - println!( "api_key : {}", api_key ); - Ok( Client::new( api_key ) ) + Ok( Client::new( secrets.OPENAI_API_KEY.clone() ) ) } } @@ -45,8 +33,7 @@ crate::mod_interface! exposed use { Client, - ClientOptions, AssistantObject, - client, + client }; } diff --git a/module/move/assistant/src/commands.rs b/module/move/assistant/src/commands.rs new file mode 100644 index 0000000000..7a8f56c76c --- /dev/null +++ b/module/move/assistant/src/commands.rs @@ -0,0 +1,49 @@ +//! +//! CLI commands of the tool. +//! + +/// Internal namespace. +mod private +{ + + use clap::{ Parser, Subcommand }; + + use crate::*; + use commands::openai; + + /// CLI commands of the tool. + #[ derive ( Debug, Parser ) ] + pub struct Cli + { + /// Root of the CLI commands. + #[ command ( subcommand ) ] + pub command : CliCommand, + } + + /// Root of the CLI commands. + #[ derive ( Debug, Subcommand ) ] + pub enum CliCommand + { + /// OpenAI API commands. + #[ command ( subcommand, name = "openai" ) ] + OpenAi( openai::Command ), + } + +} + +crate::mod_interface! +{ + layer openai; + layer openai_assistants; + layer openai_assistants_list; + layer openai_runs; + layer openai_runs_list; + layer openai_files; + layer openai_files_list; + + own use + { + Cli, + CliCommand, + }; +} diff --git a/module/move/assistant/src/commands/openai.rs b/module/move/assistant/src/commands/openai.rs new file mode 100644 index 0000000000..42c7ea5595 --- /dev/null +++ b/module/move/assistant/src/commands/openai.rs @@ -0,0 +1,75 @@ +//! +//! Collection of OpenAI API commands. +//! + +mod private +{ + + use clap::Subcommand; + + use crate::*; + use client::Client; + use commands::{ openai_assistants, openai_files, openai_runs }; + + /// OpenAI API commands. + #[ derive ( Debug, Subcommand ) ] + pub enum Command + { + /// OpenAI assistants. + #[ command ( subcommand ) ] + Assistants + ( + openai_assistants::Command + ), + + /// OpenAI files. + #[ command ( subcommand ) ] + Files + ( + openai_files::Command + ), + + /// OpenAI runs. + #[ command ( subcommand ) ] + Runs + ( + openai_runs::Command + ), + } + + /// Execute OpenAI command. + pub async fn command + ( + client : &Client, + command : Command, + ) + { + match command + { + Command::Assistants( assistants_command ) => + { + openai_assistants::command( client, assistants_command ).await; + } + + Command::Files( files_command ) => + { + openai_files::command( client, files_command ).await; + } + + Command::Runs( runs_command ) => + { + openai_runs::command( client, runs_command ).await; + } + } + } + +} + +crate::mod_interface! +{ + own use + { + Command, + command, + }; +} \ No newline at end of file diff --git a/module/move/assistant/src/commands/openai_assistants.rs b/module/move/assistant/src/commands/openai_assistants.rs new file mode 100644 index 0000000000..66e2d057e8 --- /dev/null +++ b/module/move/assistant/src/commands/openai_assistants.rs @@ -0,0 +1,52 @@ +//! +//! Collection of assistants commands for OpenAI API. +//! + +mod private +{ + + use clap::Subcommand; + + use crate::*; + use client::Client; + use commands::openai_assistants_list; + + /// OpenAI assistants. + #[ derive ( Debug, Subcommand ) ] + pub enum Command + { + /// List OpenAI assistants. + List + { + /// Show records as separate tables. + #[ arg( long, default_value_t = false ) ] + show_records_as_tables : bool + }, + } + + /// Execute OpenAI command related to assistants. + pub async fn command + ( + client : &Client, + command : Command, + ) + { + match command + { + Command::List{ show_records_as_tables } => + { + openai_assistants_list::command( client, show_records_as_tables ).await; + } + } + } + +} + +crate::mod_interface! +{ + own use + { + Command, + command, + }; +} \ No newline at end of file diff --git a/module/move/assistant/src/commands/openai_assistants_list.rs b/module/move/assistant/src/commands/openai_assistants_list.rs new file mode 100644 index 0000000000..95e1ffb62c --- /dev/null +++ b/module/move/assistant/src/commands/openai_assistants_list.rs @@ -0,0 +1,33 @@ +//! +//! List assistants in OpenAI API (command part). +//! + +mod private +{ + + use crate::*; + use client::Client; + use actions; + + /// List OpenAI assistants command. + pub async fn command + ( + client : &Client, + show_records_as_tables : bool, + ) + { + let result = actions::openai_assistants_list::action( client, show_records_as_tables ).await; + + match result + { + Ok ( report ) => println!( "{}", report ), + Err ( error ) => println!( "{}", error ) + } + } + +} + +crate::mod_interface! +{ + own use command; +} \ No newline at end of file diff --git a/module/move/assistant/src/commands/openai_files.rs b/module/move/assistant/src/commands/openai_files.rs new file mode 100644 index 0000000000..33e18fea0a --- /dev/null +++ b/module/move/assistant/src/commands/openai_files.rs @@ -0,0 +1,52 @@ +//! +//! Collection of files commands for OpenAI API. +//! + +mod private +{ + + use clap::Subcommand; + + use crate::*; + use client::Client; + use commands::openai_files_list; + + /// OpenAI files. + #[ derive ( Debug, Subcommand ) ] + pub enum Command + { + /// List OpenAI files. + List + { + /// Show records as separate tables. + #[ arg( long, default_value_t = false ) ] + show_records_as_tables : bool + }, + } + + /// Execute OpenAI commands related to files. + pub async fn command + ( + client : &Client, + command : Command, + ) + { + match command + { + Command::List{ show_records_as_tables } => + { + openai_files_list::command( client, show_records_as_tables ).await; + } + } + } + +} + +crate::mod_interface! +{ + own use + { + Command, + command, + }; +} \ No newline at end of file diff --git a/module/move/assistant/src/commands/openai_files_list.rs b/module/move/assistant/src/commands/openai_files_list.rs new file mode 100644 index 0000000000..04bc83c0c4 --- /dev/null +++ b/module/move/assistant/src/commands/openai_files_list.rs @@ -0,0 +1,33 @@ +//! +//! List files in OpenAI API (command part). +//! + +mod private +{ + + use crate::*; + use client::Client; + use actions; + + /// List files in your OpenAI API. + pub async fn command + ( + client : &Client, + show_records_as_tables : bool, + ) + { + let result = actions::openai_files_list::action( client, show_records_as_tables ).await; + + match result + { + Ok ( report ) => println!( "{}", report ), + Err ( error ) => println!( "{}", error ) + } + } + +} + +crate::mod_interface! +{ + own use command; +} \ No newline at end of file diff --git a/module/move/assistant/src/commands/openai_runs.rs b/module/move/assistant/src/commands/openai_runs.rs new file mode 100644 index 0000000000..e5e183b33e --- /dev/null +++ b/module/move/assistant/src/commands/openai_runs.rs @@ -0,0 +1,55 @@ +//! +//! Collection of runs commands for OpenAI API. +//! + +mod private +{ + + use clap::Subcommand; + + use crate::*; + use client::Client; + use commands::openai_runs_list; + + /// OpenAI runs. + #[ derive ( Debug, Subcommand ) ] + pub enum Command + { + /// List OpenAI runs in a thread. + List + { + /// Thread ID. + thread_id : String, + + /// Show records as separate tables. + #[ arg( long, default_value_t = false ) ] + show_records_as_tables : bool + }, + } + + /// Execute OpenAI commands related to runs. + pub async fn command + ( + client : &Client, + command : Command, + ) + { + match command + { + Command::List { thread_id, show_records_as_tables } => + { + openai_runs_list::command( client, thread_id, show_records_as_tables ).await; + } + } + } + +} + +crate::mod_interface! +{ + own use + { + Command, + command, + }; +} \ No newline at end of file diff --git a/module/move/assistant/src/commands/openai_runs_list.rs b/module/move/assistant/src/commands/openai_runs_list.rs new file mode 100644 index 0000000000..928e0b473a --- /dev/null +++ b/module/move/assistant/src/commands/openai_runs_list.rs @@ -0,0 +1,34 @@ +//! +//! List runs in OpenAI API (command part). +//! + +mod private +{ + + use crate::*; + use client::Client; + use actions; + + /// List runs in the thread in OpenAI API. + pub async fn command + ( + client : &Client, + thread_id : String, + show_records_as_tables : bool, + ) + { + let result = actions::openai_runs_list::action( client, thread_id, show_records_as_tables ).await; + + match result + { + Ok ( report ) => println!( "{}", report ), + Err ( error ) => println!( "{}", error ) + } + } + +} + +crate::mod_interface! +{ + own use command; +} \ No newline at end of file diff --git a/module/move/assistant/src/debug.rs b/module/move/assistant/src/debug.rs index d35c02699a..294333abf0 100644 --- a/module/move/assistant/src/debug.rs +++ b/module/move/assistant/src/debug.rs @@ -17,6 +17,7 @@ use std::borrow::Cow; mod assistant_object; mod file_data; +mod run_object; crate::mod_interface! { @@ -24,5 +25,6 @@ crate::mod_interface! { assistant_object::AssistantObjectWrap, file_data::FileDataWrap, + run_object::RunObjectWrap, }; } diff --git a/module/move/assistant/src/debug/assistant_object.rs b/module/move/assistant/src/debug/assistant_object.rs index 1535245f67..9ebcead56e 100644 --- a/module/move/assistant/src/debug/assistant_object.rs +++ b/module/move/assistant/src/debug/assistant_object.rs @@ -5,6 +5,7 @@ use openai_api_rs::v1::assistant; #[ derive( Debug ) ] pub struct AssistantObjectWrap( pub assistant::AssistantObject ); +/// Manually implemented `Clone`, as `FileData` does not implement it. impl Clone for AssistantObjectWrap { fn clone( &self ) -> Self diff --git a/module/move/assistant/src/debug/file_data.rs b/module/move/assistant/src/debug/file_data.rs index ca1fb242d4..b8029949c7 100644 --- a/module/move/assistant/src/debug/file_data.rs +++ b/module/move/assistant/src/debug/file_data.rs @@ -8,6 +8,7 @@ use openai_api_rs::v1::file::FileData; #[ derive( Debug ) ] pub struct FileDataWrap( pub FileData ); +/// Manually implemented `Clone`, as `FileData` does not implement it. impl Clone for FileDataWrap { fn clone( &self ) -> Self diff --git a/module/move/assistant/src/debug/run_object.rs b/module/move/assistant/src/debug/run_object.rs new file mode 100644 index 0000000000..efe2ce1e02 --- /dev/null +++ b/module/move/assistant/src/debug/run_object.rs @@ -0,0 +1,73 @@ + +use super::*; +use openai_api_rs::v1::run::RunObject; + +// Assuming the `format_tools` module and `field!` macro are defined elsewhere + +/// A wrapper for `RunObject` to make pretty print. +#[ derive( Debug ) ] +pub struct RunObjectWrap( pub RunObject ); + +/// Manually implemented `Clone`, as `RunObject` does not implement it. +impl Clone for RunObjectWrap { + fn clone(&self) -> Self { + RunObjectWrap(RunObject { + id : self.0.id.clone(), + object : self.0.object.clone(), + created_at : self.0.created_at, + thread_id : self.0.thread_id.clone(), + assistant_id : self.0.assistant_id.clone(), + status : self.0.status.clone(), + required_action : self.0.required_action.clone(), + last_error : self.0.last_error.clone(), + expires_at : self.0.expires_at, + started_at : self.0.started_at, + cancelled_at : self.0.cancelled_at, + failed_at : self.0.failed_at, + completed_at : self.0.completed_at, + model : self.0.model.clone(), + instructions : self.0.instructions.clone(), + tools : self.0.tools.clone(), + metadata : self.0.metadata.clone(), + headers : self.0.headers.clone(), + }) + } +} + +impl TableWithFields for RunObjectWrap {} +impl Fields< &'_ str, Option< Cow< '_, str > > > +for RunObjectWrap +{ + type Key< 'k > = &'k str; + type Val< 'v > = Option< Cow< 'v, str > >; + + fn fields( &self ) -> impl format_tools::IteratorTrait< Item = ( &'_ str, Option< Cow< '_, str > > ) > + { + use format_tools::ref_or_display_or_debug_multiline::field; + let mut dst = Vec::new(); + + dst.push( field!( &self.0.id ) ); + dst.push( field!( &self.0.object ) ); + dst.push( ( "created_at", Some( Cow::Owned( self.0.created_at.to_string() ) ) ) ); + dst.push( field!( &self.0.thread_id ) ); + dst.push( field!( &self.0.assistant_id ) ); + dst.push( field!( &self.0.status ) ); + + dst.push( ( "required_action", self.0.required_action.as_ref().map( |ra| Cow::Owned( format!( "{:?}", ra ) ) ) ) ); + dst.push( ( "last_error", self.0.last_error.as_ref().map( |le| Cow::Owned( format!( "{:?}", le ) ) ) ) ); + dst.push( ( "expires_at", self.0.expires_at.map( |ea| Cow::Owned( ea.to_string() ) ) ) ); + dst.push( ( "started_at", self.0.started_at.map( |sa| Cow::Owned( sa.to_string() ) ) ) ); + dst.push( ( "cancelled_at", self.0.cancelled_at.map( |ca| Cow::Owned( ca.to_string() ) ) ) ); + dst.push( ( "failed_at", self.0.failed_at.map( |fa| Cow::Owned( fa.to_string() ) ) ) ); + dst.push( ( "completed_at", self.0.completed_at.map( |ca| Cow::Owned( ca.to_string() ) ) ) ); + + dst.push( field!( &self.0.model ) ); + dst.push( ( "instructions", self.0.instructions.as_ref().map( |i| Cow::Owned( i.clone() ) ) ) ); + + dst.push( ( "tools", Some( Cow::Owned( format!( "{:?}", self.0.tools ) ) ) ) ); + dst.push( ( "metadata", Some( Cow::Owned( format!( "{:?}", self.0.metadata ) ) ) ) ); + dst.push( ( "headers", self.0.headers.as_ref().map( |h| Cow::Owned( format!( "{:?}", h ) ) ) ) ); + + dst.into_iter() + } +} diff --git a/module/move/assistant/src/lib.rs b/module/move/assistant/src/lib.rs index dd2ca047ba..c064213715 100644 --- a/module/move/assistant/src/lib.rs +++ b/module/move/assistant/src/lib.rs @@ -4,12 +4,24 @@ #![ doc = include_str!( concat!( env!( "CARGO_MANIFEST_DIR" ), "/", "Readme.md" ) ) ] use mod_interface::mod_interface; +use error_tools::thiserror; /// Define a private namespace for all its items. mod private { } +/// Serde-related exports. +pub mod ser +{ + pub use serde:: + { + Serialize, + Deserialize, + }; + pub use serde_with::*; +} + // pub mod client; crate::mod_interface! @@ -17,6 +29,9 @@ crate::mod_interface! layer client; layer debug; + layer commands; + layer actions; + layer secret; exposed use ::reflect_tools:: { diff --git a/module/move/assistant/src/secret.rs b/module/move/assistant/src/secret.rs new file mode 100644 index 0000000000..aa90da77bc --- /dev/null +++ b/module/move/assistant/src/secret.rs @@ -0,0 +1,219 @@ +//! +//! Tool's secrets. +//! + +/// Internal namespace. +mod private +{ + use crate::*; + use std:: + { + env, + sync::OnceLock, + }; + + use error_tools::typed::Error; + use ser::DisplayFromStr; + + /// Typed secret error. + #[ ser::serde_as ] + #[ derive( Debug, Error, ser::Serialize ) ] + #[ serde( tag = "type", content = "data" ) ] + pub enum Error + { + + /// Secret file is illformed. + #[ error( "Secret file is illformed\n{0}" ) ] + SecretFileIllformed + ( + #[ from ] + #[ serde_as( as = "DisplayFromStr" ) ] + dotenv::Error + ), + + /// Some variable in the secrets is missing. + #[ error( "Secret misssing the variable {0}" ) ] + VariableMissing( &'static str ), + + /// Some variable in the secrets is illformed. + #[ error( "Secret error processing the variable {0}\n{1}" ) ] + VariableIllformed( &'static str, String ), + + } + + /// Result type for `Secret` methods. + pub type Result< R > = core::result::Result< R, Error >; + + /// Represents the application secrets loaded from environment variables. + #[ derive( Debug ) ] + #[ allow( non_snake_case ) ] + pub struct Secret + { + /// OpenAI API key. + pub OPENAI_API_KEY : String, + } + + impl Secret + { + + /// Loads secrets from environment variables. + /// + /// # Returns + /// + /// * `Result< Self >` - On success, returns a `Secret` instance with values from environment variables. + /// * On failure, returns an error indicating which environment variable is missing or invalid. + #[ allow( non_snake_case ) ] + pub fn load() -> Result< Self > + { + let path = "./.key/-env.sh"; + + // Attempt to load environment variables from the specified file + let r = dotenv::from_filename( path ); + if let Err( ref err ) = r + { + // Only return an error if it's not an Io error, and include the file path in the error message + if !matches!( err, dotenv::Error::Io( _ ) ) + { + return Err( r.expect_err( &format!( "Failed to load {path}" ) ).into() ); + } + } + + let config = Self + { + OPENAI_API_KEY : var( "OPENAI_API_KEY", None )?, + }; + Ok( config ) + } + + /// Reads the secrets, panicking with an explanation if loading fails. + /// + /// # Returns + /// + /// * `Secret` - The loaded secrets. + /// + /// # Panics + /// + /// * Panics with a detailed explanation if the secrets cannot be loaded. + + pub fn read() -> Secret + { + Self::load().unwrap_or_else( | err | + { + let example = include_str!( "../.key/readme.md" ); + let explanation = format! + ( +r#" = Lack of secrets + +Failed to load secret or some its parameters. +{err} + + = Fix + +Either define missing environment variable or make sure `./.key/-env.toml` file has it defined. + + = More information + +{example} +"# + ); + panic!( "{}", explanation ); + }) + } + + /// Retrieves a static reference to the secrets, initializing it if necessary. + /// + /// # Returns + /// + /// * `&'static Secret` - A static reference to the secrets. + /// + /// # Warning + /// + /// * Do not use this function unless absolutely necessary. + /// * Avoid using it in `lib.rs`. + pub fn get() -> &'static Secret + { + static INSTANCE : OnceLock< Secret > = OnceLock::new(); + INSTANCE.get_or_init( || Self::read() ) + } + + } + + /// Retrieves the value of an environment variable as a `String`. + /// + /// This function attempts to fetch the value of the specified environment variable. + /// If the variable is not set, it returns a provided default value if available, or an error if not. + /// + /// # Arguments + /// + /// * `name` - The name of the environment variable to retrieve. + /// * `default` - An optional default value to return if the environment variable is not set. + /// + /// # Returns + /// + /// * `Result` - On success, returns the value of the environment variable or the default value. + /// * On failure, returns an error indicating the missing environment variable. + fn var + ( + name : &'static str, + default : Option< &'static str >, + ) -> Result< String > + { + match env::var( name ) + { + Ok( value ) => Ok( value ), + Err( _ ) => + { + if let Some( default_value ) = default + { + Ok( default_value.to_string() ) + } + else + { + Err( Error::VariableMissing( name ) ) + } + } + } + } + + /// Retrieves the value of an environment variable as an `AbsolutePath`. + /// + /// This function attempts to fetch the value of the specified environment variable and convert it into an `AbsolutePath`. + /// If the variable is not set, it returns a provided default value if available, or an error if not. + /// + /// # Arguments + /// + /// * `name` - The name of the environment variable to retrieve. + /// * `default` - An optional default value to return if the environment variable is not set. + /// + /// # Returns + /// + /// * `Result` - On success, returns the parsed `AbsolutePath`. + /// * On failure, returns an error indicating the missing or ill-formed environment variable. + fn _var_path + ( + name : &'static str, + default : Option< &'static str >, + ) -> Result< pth::AbsolutePath > + { + let p = var( name, default )?; + pth::AbsolutePath::from_paths( ( pth::CurrentPath, p ) ) + .map_err( |e| Error::VariableIllformed( name, e.to_string() ) ) + } + +} + +crate::mod_interface! +{ + + own use + { + Error, + Result, + }; + + orphan use + { + Secret, + }; + +} \ No newline at end of file From 4c17fa5a56f0f2707ec59c4355aac1ee88ee12a8 Mon Sep 17 00:00:00 2001 From: wandalen Date: Sun, 10 Nov 2024 12:44:44 +0200 Subject: [PATCH 43/67] assistant : newer version of dependency --- module/move/assistant/Cargo.toml | 2 +- module/move/assistant/src/bin/main.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/module/move/assistant/Cargo.toml b/module/move/assistant/Cargo.toml index 479871b6f7..64396a499c 100644 --- a/module/move/assistant/Cargo.toml +++ b/module/move/assistant/Cargo.toml @@ -47,7 +47,7 @@ mod_interface = { workspace = true, features = [ "full" ] } former = { workspace = true, features = [ "full" ] } format_tools = { workspace = true, features = [ "full" ] } reflect_tools = { workspace = true, features = [ "full" ] } -openai-api-rs = { version = "=5.0.11" } +openai-api-rs = { version = "=5.0.14" } tokio = { version = "1", features = ["full"] } dotenv = "0.15" clap = { version = "4.5.20", features = ["derive"] } diff --git a/module/move/assistant/src/bin/main.rs b/module/move/assistant/src/bin/main.rs index 69843c1ed4..345e98f8c2 100644 --- a/module/move/assistant/src/bin/main.rs +++ b/module/move/assistant/src/bin/main.rs @@ -26,7 +26,7 @@ async fn main() -> Result< (), Box< dyn Error > > let secret = Secret::load()?; - let client = client( &secret )?; + let client = client::client( &secret )?; let cli = Cli::parse(); From 9beca47a838a0435389afa836d8ed6a0a5b985dd Mon Sep 17 00:00:00 2001 From: wandalen Date: Sun, 10 Nov 2024 23:54:00 +0200 Subject: [PATCH 44/67] test_tools : standalone mem_tools --- module/core/diagnostics_tools/src/diag/rta.rs | 1 - module/core/diagnostics_tools/src/lib.rs | 3 --- module/core/mem_tools/Cargo.toml | 1 - module/core/mem_tools/src/lib.rs | 3 --- module/core/mem_tools/src/mem.rs | 13 +++++----- module/core/test_tools/Cargo.toml | 5 ++-- module/core/test_tools/src/lib.rs | 15 +++++++---- .../{basic_test.rs => impls_index_test.rs} | 0 module/core/test_tools/tests/inc/mem_test.rs | 26 +++++++++++++++++++ module/core/test_tools/tests/inc/mod.rs | 22 ++++++++++++++-- module/core/test_tools/tests/tests.rs | 11 ++++---- 11 files changed, 71 insertions(+), 29 deletions(-) rename module/core/test_tools/tests/inc/{basic_test.rs => impls_index_test.rs} (100%) create mode 100644 module/core/test_tools/tests/inc/mem_test.rs diff --git a/module/core/diagnostics_tools/src/diag/rta.rs b/module/core/diagnostics_tools/src/diag/rta.rs index 46b21050a2..4bd27b3bba 100644 --- a/module/core/diagnostics_tools/src/diag/rta.rs +++ b/module/core/diagnostics_tools/src/diag/rta.rs @@ -289,4 +289,3 @@ pub mod prelude }; } - diff --git a/module/core/diagnostics_tools/src/lib.rs b/module/core/diagnostics_tools/src/lib.rs index a3415c710e..8ed4ccb486 100644 --- a/module/core/diagnostics_tools/src/lib.rs +++ b/module/core/diagnostics_tools/src/lib.rs @@ -31,7 +31,6 @@ pub mod own #[ doc( inline ) ] pub use orphan::*; #[ doc( inline ) ] - #[ allow( unused_imports ) ] pub use super::diag::orphan::*; } @@ -54,7 +53,6 @@ pub mod exposed #[ doc( inline ) ] pub use prelude::*; #[ doc( inline ) ] - #[ allow( unused_imports ) ] pub use super::diag::exposed::*; } @@ -65,6 +63,5 @@ pub mod prelude { use super::*; #[ doc( inline ) ] - #[ allow( unused_imports ) ] pub use super::diag::prelude::*; } diff --git a/module/core/mem_tools/Cargo.toml b/module/core/mem_tools/Cargo.toml index cec41724d4..ba71b5759a 100644 --- a/module/core/mem_tools/Cargo.toml +++ b/module/core/mem_tools/Cargo.toml @@ -24,7 +24,6 @@ workspace = true features = [ "full" ] all-features = false - include = [ "/rust/impl/mem", "/Cargo.toml", diff --git a/module/core/mem_tools/src/lib.rs b/module/core/mem_tools/src/lib.rs index 03270e0a05..141da61a9d 100644 --- a/module/core/mem_tools/src/lib.rs +++ b/module/core/mem_tools/src/lib.rs @@ -37,7 +37,6 @@ pub mod own #[ doc( inline ) ] pub use orphan::*; #[ doc( inline ) ] - #[ allow( unused_imports ) ] pub use super::mem::orphan::*; } @@ -60,7 +59,6 @@ pub mod exposed #[ doc( inline ) ] pub use prelude::*; #[ doc( inline ) ] - #[ allow( unused_imports ) ] pub use super::mem::exposed::*; } @@ -71,6 +69,5 @@ pub mod prelude { use super::*; #[ doc( inline ) ] - #[ allow( unused_imports ) ] pub use super::mem::prelude::*; } diff --git a/module/core/mem_tools/src/mem.rs b/module/core/mem_tools/src/mem.rs index 8a540d97e2..3a48ddad8b 100644 --- a/module/core/mem_tools/src/mem.rs +++ b/module/core/mem_tools/src/mem.rs @@ -64,30 +64,28 @@ mod private } +#[ doc( inline ) ] +#[ allow( unused_imports ) ] +pub use own::*; + /// Own namespace of the module. #[ allow( unused_imports ) ] pub mod own { use super::*; #[ doc( inline ) ] - #[ allow( unused_imports ) ] pub use super:: { orphan::*, }; } -#[ doc( inline ) ] -#[ allow( unused_imports ) ] -pub use own::*; - /// Orphan namespace of the module. #[ allow( unused_imports ) ] pub mod orphan { use super::*; #[ doc( inline ) ] - #[ allow( unused_imports ) ] pub use super:: { exposed::*, @@ -103,6 +101,9 @@ pub mod orphan pub mod exposed { use super::*; + // Expose itself. + pub use super::super::mem; + #[ doc( inline ) ] pub use prelude::*; } diff --git a/module/core/test_tools/Cargo.toml b/module/core/test_tools/Cargo.toml index 01ee46f740..4f17161640 100644 --- a/module/core/test_tools/Cargo.toml +++ b/module/core/test_tools/Cargo.toml @@ -80,7 +80,7 @@ standalone = [ "standalone_collection_tools", "standalone_impls_index", "standalone_mem_tools", - "dep:mem_tools", + # "dep:mem_tools", "dep:typing_tools", "dep:diagnostics_tools", "dep:process_tools", @@ -112,8 +112,8 @@ rand = { workspace = true } error_tools = { workspace = true, features = [ "full" ], optional = true } collection_tools = { workspace = true, features = [ "full" ], optional = true } impls_index = { workspace = true, features = [ "full" ], optional = true } - mem_tools = { workspace = true, features = [ "full" ], optional = true } + typing_tools = { workspace = true, features = [ "full" ], optional = true } diagnostics_tools = { workspace = true, features = [ "full" ], optional = true } process_tools = { workspace = true, features = [ "full" ], optional = true } @@ -129,7 +129,6 @@ thiserror = { workspace = true, optional = true } hashbrown = { workspace = true, optional = true } # impls_index impls_index_meta = { workspace = true, optional = true } -# mem_tools [build-dependencies] rustc_version = "0.4" diff --git a/module/core/test_tools/src/lib.rs b/module/core/test_tools/src/lib.rs index cb0d390392..4d1b25124e 100644 --- a/module/core/test_tools/src/lib.rs +++ b/module/core/test_tools/src/lib.rs @@ -112,11 +112,16 @@ mod standalone pub mod collection; pub use collection as collection_tools; - /// impl index macroc. + /// impl and index macros. #[ path = "../../../../core/impls_index/src/impls_index/mod.rs" ] pub mod impls_index; + /// Memory tools. + #[ path = "../../../../core/mem_tools/src/mem.rs" ] + pub mod mem_tools; + } + #[ cfg( feature = "enabled" ) ] #[ cfg( feature = "standalone" ) ] pub use standalone::*; @@ -128,12 +133,12 @@ pub use :: error_tools, collection_tools, impls_index, + mem_tools, }; #[ cfg( feature = "enabled" ) ] pub use :: { - mem_tools, typing_tools, diagnostics_tools, process_tools, @@ -162,7 +167,7 @@ pub mod own { error_tools::orphan::*, collection_tools::orphan::*, - // meta_tools::orphan::*, + impls_index::orphan::*, mem_tools::orphan::*, typing_tools::orphan::*, diagnostics_tools::orphan::*, @@ -203,7 +208,7 @@ pub mod exposed { error_tools::exposed::*, collection_tools::exposed::*, - // meta_tools::exposed::*, + impls_index::exposed::*, mem_tools::exposed::*, typing_tools::exposed::*, diagnostics_tools::exposed::*, @@ -226,7 +231,7 @@ pub mod prelude { error_tools::prelude::*, collection_tools::prelude::*, - // meta_tools::prelude::*, + impls_index::prelude::*, mem_tools::prelude::*, typing_tools::prelude::*, diagnostics_tools::prelude::*, diff --git a/module/core/test_tools/tests/inc/basic_test.rs b/module/core/test_tools/tests/inc/impls_index_test.rs similarity index 100% rename from module/core/test_tools/tests/inc/basic_test.rs rename to module/core/test_tools/tests/inc/impls_index_test.rs diff --git a/module/core/test_tools/tests/inc/mem_test.rs b/module/core/test_tools/tests/inc/mem_test.rs new file mode 100644 index 0000000000..1cf4a2b724 --- /dev/null +++ b/module/core/test_tools/tests/inc/mem_test.rs @@ -0,0 +1,26 @@ +use super::*; + +// + +#[ allow( dead_code ) ] +#[ test ] +fn same_data() +{ + let buf = [ 0u8; 128 ]; + assert!( the_module::mem::same_data( &buf, &buf ) ); + + let x = [ 0u8; 1 ]; + let y = 0u8; + + assert!( the_module::mem::same_data( &x, &y ) ); + + assert!( !the_module::mem::same_data( &buf, &x ) ); + assert!( !the_module::mem::same_data( &buf, &y ) ); + + struct H1( &'static str ); + struct H2( &'static str ); + + assert!( the_module::mem::same_data( &H1( "hello" ), &H2( "hello" ) ) ); + assert!( !the_module::mem::same_data( &H1( "qwerty" ), &H2( "hello" ) ) ); + +} diff --git a/module/core/test_tools/tests/inc/mod.rs b/module/core/test_tools/tests/inc/mod.rs index bf3d2e3d78..a6d6581d75 100644 --- a/module/core/test_tools/tests/inc/mod.rs +++ b/module/core/test_tools/tests/inc/mod.rs @@ -1,8 +1,26 @@ -#[ allow( unused_imports ) ] use super::*; -mod basic_test; +mod impls_index_test; +mod mem_test; mod try_build_test; // mod wtest_utility; // qqq : include tests of all internal dependencies + +// /// Error tools. +// #[ path = "../../../../core/error_tools/tests/inc/mod.rs" ] +// pub mod error; +// pub use error as error_tools; +// +// /// Collection tools. +// #[ path = "../../../../core/collection_tools/tests/inc/mod.rs" ] +// pub mod collection; +// pub use collection as collection_tools; + +// /// impl and index macros. +// #[ path = "../../../../core/impls_index/tests/inc/mod.rs" ] +// pub mod impls_index; + +// /// Memory tools. +// #[ path = "../../../../core/mem_tools/tests/inc/mod.rs" ] +// pub mod mem_tools; diff --git a/module/core/test_tools/tests/tests.rs b/module/core/test_tools/tests/tests.rs index 3cdbd75627..c9b1261fb2 100644 --- a/module/core/test_tools/tests/tests.rs +++ b/module/core/test_tools/tests/tests.rs @@ -1,12 +1,13 @@ +#![ allow( unused_imports ) ] + // #![ deny( rust_2018_idioms ) ] // #![ deny( missing_debug_implementations ) ] // #![ deny( missing_docs ) ] -#[ allow( unused_imports ) ] use test_tools as the_module; -#[ allow( unused_imports ) ] -#[ cfg( feature = "enabled" ) ] -#[ cfg( not( feature = "no_std" ) ) ] -use test_tools::exposed::*; + +// #[ cfg( feature = "enabled" ) ] +// #[ cfg( not( feature = "no_std" ) ) ] +// use test_tools::exposed::*; mod inc; From c05547359859117f799e069c5cdd01efaf1a7900 Mon Sep 17 00:00:00 2001 From: Ruslan Date: Mon, 11 Nov 2024 11:17:24 +0200 Subject: [PATCH 45/67] Fix assistant (#1484) * Fix assistant from merge * Log `format_tools` variables * Possible fix to `format_tools` --- .../src/format/output_format/table.rs | 17 ++++++++++++++--- module/move/assistant/src/bin/main.rs | 2 +- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/module/core/format_tools/src/format/output_format/table.rs b/module/core/format_tools/src/format/output_format/table.rs index 579ba6c783..e96af36f20 100644 --- a/module/core/format_tools/src/format/output_format/table.rs +++ b/module/core/format_tools/src/format/output_format/table.rs @@ -247,9 +247,20 @@ impl TableOutputFormat for Table write!( c.buf, "{}", cell_prefix )?; - // println!( "icol : {icol} | irow : {irow} | width : {width} | cell_width : {cell_width}" ); - 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} | slice.len() : {}", slice.len() ); + + let lspaces = if cell_width > width { + 0 + } else { + ( width - cell_width ) / 2 + }; + + let rspaces = if (cell_width > width) || (slice.len() > cell_width) { + 0 + } else { + ( width - cell_width + 1 ) / 2 + cell_width - slice.len() + }; + // println!( "icol : {icol} | irow : {irow} | width : {width} | cell_width : {cell_width} | lspaces : {lspaces} | rspaces : {rspaces}" ); if lspaces > 0 diff --git a/module/move/assistant/src/bin/main.rs b/module/move/assistant/src/bin/main.rs index 345e98f8c2..adeeaf150f 100644 --- a/module/move/assistant/src/bin/main.rs +++ b/module/move/assistant/src/bin/main.rs @@ -14,7 +14,7 @@ use clap::Parser; use assistant:: { - client, + client::client, commands::{ Cli, CliCommand, self }, Secret }; From 9370621db77f3ba83f445bd4f80b65093dafad20 Mon Sep 17 00:00:00 2001 From: Ruslan Date: Thu, 14 Nov 2024 19:28:08 +0200 Subject: [PATCH 46/67] Fix for `format_tools` for overflow subtract (#1490) * Refert previous fix * Add test * Add test * Add fix * Revert wrong fix * Add correct test * Add fix --- .../src/format/output_format/table.rs | 15 +++---------- module/core/format_tools/src/format/print.rs | 2 ++ .../tests/inc/format_table_test.rs | 22 +++++++++++++++++++ .../format_tools/tests/inc/test_object.rs | 14 ++++++++++++ .../tests/inc/to_string_with_fallback_test.rs | 4 ++-- 5 files changed, 43 insertions(+), 14 deletions(-) diff --git a/module/core/format_tools/src/format/output_format/table.rs b/module/core/format_tools/src/format/output_format/table.rs index e96af36f20..81a16fb0f2 100644 --- a/module/core/format_tools/src/format/output_format/table.rs +++ b/module/core/format_tools/src/format/output_format/table.rs @@ -247,19 +247,10 @@ impl TableOutputFormat for Table write!( c.buf, "{}", cell_prefix )?; - println!( "icol : {icol} | irow : {irow} | width : {width} | cell_width : {cell_width} | slice.len() : {}", slice.len() ); + // println!( "icol : {icol} | irow : {irow} | width : {width} | cell_width : {cell_width} | slice.len() : {}", slice.len() ); - let lspaces = if cell_width > width { - 0 - } else { - ( width - cell_width ) / 2 - }; - - let rspaces = if (cell_width > width) || (slice.len() > cell_width) { - 0 - } else { - ( width - cell_width + 1 ) / 2 + cell_width - slice.len() - }; + let lspaces = ( width - cell_width ) / 2; + let rspaces = ( width - cell_width + 1 ) / 2 + cell_width - slice.chars().count(); // println!( "icol : {icol} | irow : {irow} | width : {width} | cell_width : {cell_width} | lspaces : {lspaces} | rspaces : {rspaces}" ); diff --git a/module/core/format_tools/src/format/print.rs b/module/core/format_tools/src/format/print.rs index 78ac91b294..f3c5d2acc6 100644 --- a/module/core/format_tools/src/format/print.rs +++ b/module/core/format_tools/src/format/print.rs @@ -617,6 +617,8 @@ pub mod own Context, Printer, InputExtract, + RowDescriptor, + ColDescriptor, }; } diff --git a/module/core/format_tools/tests/inc/format_table_test.rs b/module/core/format_tools/tests/inc/format_table_test.rs index eb8a3b17dd..4f4d6694d2 100644 --- a/module/core/format_tools/tests/inc/format_table_test.rs +++ b/module/core/format_tools/tests/inc/format_table_test.rs @@ -8,6 +8,9 @@ use the_module:: filter, print, output_format, + print::{ InputExtract, RowDescriptor, ColDescriptor }, + TableOutputFormat, + filter::LineType }; use std:: @@ -326,3 +329,22 @@ fn filter_row_callback() // // xxx : implement test for vector of vectors + +// + +#[ test ] +fn no_subtract_with_overflow() +{ + let test_objects = test_object::test_objects_gen_with_unicode(); + + let format = output_format::Table::default(); + let printer = print::Printer::with_format( &format ); + + let as_table = AsTable::new( &test_objects ); + let mut output = String::new(); + let mut context = print::Context::new( &mut output, printer ); + + let result = the_module::TableFormatter::fmt( &as_table, &mut context ); + + assert!( result.is_ok() ); +} \ No newline at end of file diff --git a/module/core/format_tools/tests/inc/test_object.rs b/module/core/format_tools/tests/inc/test_object.rs index f710266a4d..70c702d035 100644 --- a/module/core/format_tools/tests/inc/test_object.rs +++ b/module/core/format_tools/tests/inc/test_object.rs @@ -200,3 +200,17 @@ pub fn test_objects_gen() -> Vec< TestObject > ] } + +pub fn test_objects_gen_with_unicode() -> Vec< TestObject > +{ + vec! + [ + TestObject + { + id : "Юнікод".to_string(), + created_at : 100, + file_ids : vec![], + tools : None, + } + ] +} \ No newline at end of file diff --git a/module/core/format_tools/tests/inc/to_string_with_fallback_test.rs b/module/core/format_tools/tests/inc/to_string_with_fallback_test.rs index bd9947cd71..e0c39527c3 100644 --- a/module/core/format_tools/tests/inc/to_string_with_fallback_test.rs +++ b/module/core/format_tools/tests/inc/to_string_with_fallback_test.rs @@ -9,12 +9,12 @@ use the_module:: WithDebug, WithDisplay, // the_module::to_string_with_fallback::Ref, - to_string_with_fallback, + to_string_with_fallback }; use std:: { - // fmt, + fmt, // collections::HashMap, borrow::Cow, }; From 5b52e5fff1f1b1e0279d7264e74a6e1ad28a610b Mon Sep 17 00:00:00 2001 From: wandalen Date: Fri, 15 Nov 2024 16:44:17 +0200 Subject: [PATCH 47/67] publishing deterministic_rand --- Cargo.toml | 2 +- module/move/deterministic_rand/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 666e522707..0e22e76fa4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -492,7 +492,7 @@ version = "~0.2.0" path = "module/move/sqlx_query" [workspace.dependencies.deterministic_rand] -version = "~0.5.0" +version = "~0.6.0" path = "module/move/deterministic_rand" [workspace.dependencies.crates_tools] diff --git a/module/move/deterministic_rand/Cargo.toml b/module/move/deterministic_rand/Cargo.toml index 1a469f1249..ae667e3e41 100644 --- a/module/move/deterministic_rand/Cargo.toml +++ b/module/move/deterministic_rand/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "deterministic_rand" -version = "0.5.0" +version = "0.6.0" edition = "2021" authors = [ "Kostiantyn Wandalen ", From d3bf2670e6701e3728325e93fba9cd3a1ec4af6e Mon Sep 17 00:00:00 2001 From: Ruslan Date: Mon, 18 Nov 2024 13:20:19 +0200 Subject: [PATCH 48/67] READY : Continue work on assistant (#1489) * Refactor table formatting * Partial refactoring * Filter columns * Improve commands --- module/move/assistant/src/actions/openai.rs | 26 ++++- .../src/actions/openai_assistants_list.rs | 33 +++--- .../src/actions/openai_files_list.rs | 33 +++--- .../assistant/src/actions/openai_runs_list.rs | 35 +++--- module/move/assistant/src/bin/main.rs | 2 +- module/move/assistant/src/commands.rs | 30 ++++++ .../src/commands/openai_assistants.rs | 12 +-- .../src/commands/openai_assistants_list.rs | 5 +- .../assistant/src/commands/openai_files.rs | 12 +-- .../src/commands/openai_files_list.rs | 5 +- .../assistant/src/commands/openai_runs.rs | 14 +-- .../src/commands/openai_runs_list.rs | 5 +- module/move/assistant/src/lib.rs | 1 + module/move/assistant/src/util.rs | 10 ++ .../move/assistant/src/util/display_table.rs | 101 ++++++++++++++++++ 15 files changed, 238 insertions(+), 86 deletions(-) create mode 100644 module/move/assistant/src/util.rs create mode 100644 module/move/assistant/src/util/display_table.rs diff --git a/module/move/assistant/src/actions/openai.rs b/module/move/assistant/src/actions/openai.rs index 82053c4d47..da8be32030 100644 --- a/module/move/assistant/src/actions/openai.rs +++ b/module/move/assistant/src/actions/openai.rs @@ -13,6 +13,8 @@ mod private use crate::*; use ser::DisplayFromStr; + use commands::TableConfig; + /// Collective enum for errors in OpenAI actions. #[ ser::serde_as ] #[ derive( Debug, Error, AsRefStr, ser::Serialize ) ] @@ -26,12 +28,31 @@ mod private #[ from ] #[ serde_as( as = "DisplayFromStr" ) ] openai_api_rs::v1::error::APIError - ) + ), + + /// User chosen a mix of table styles instead of a single one. + /// E.g.: both `--as-table` and `--as-records` were set, however only one style must be chosen + #[ error( "Select only one table style: either `--as-table`, `--as-records`, or `--columns`" ) ] + WrongTableStyle, } /// Shorthand for `Result` in OpenAI actions. pub type Result< T > = core::result::Result< T, Error >; + /// Check the CLI arguments for table style. + /// There are 3 arguments: `--as-table`, `--as-records`, `--columns`. Only one argument + /// should be active a time. + pub fn check_table_style( table_config: &TableConfig ) -> Result< () > + { + if table_config.as_table && ( table_config.as_records || table_config.columns ) + || table_config.as_records && ( table_config.as_table || table_config.columns ) + || table_config.columns && ( table_config.as_records || table_config.as_table ) + { + return Err( Error::WrongTableStyle ) + } + + Ok( () ) + } } crate::mod_interface! @@ -39,6 +60,7 @@ crate::mod_interface! own use { Error, - Result + Result, + check_table_style, }; } \ No newline at end of file diff --git a/module/move/assistant/src/actions/openai_assistants_list.rs b/module/move/assistant/src/actions/openai_assistants_list.rs index 9859761c3b..2326ac2a6a 100644 --- a/module/move/assistant/src/actions/openai_assistants_list.rs +++ b/module/move/assistant/src/actions/openai_assistants_list.rs @@ -7,24 +7,24 @@ mod private use std::fmt; - use format_tools:: - { - AsTable, - TableFormatter, - output_format, - }; + use format_tools::AsTable; use crate::*; use client::Client; + use debug::AssistantObjectWrap; - use actions::openai::Result; + + use actions::openai::{ Result, check_table_style }; + + use commands::TableConfig; + use util::display_table::display_tabular_data; /// Report for `openai assistants list`. #[ derive( Debug ) ] pub struct ListReport { - /// Show records as separate tables. - pub show_records_as_tables : bool, + /// Configure table formatting. + pub table_config : TableConfig, /// OpenAI assistants. pub assistants: Vec< AssistantObjectWrap > @@ -38,14 +38,7 @@ mod private f : &mut fmt::Formatter< '_ > ) -> fmt::Result { - if self.show_records_as_tables - { - writeln!(f, "{}", AsTable::new( &self.assistants ).table_to_string_with_format( &output_format::Records::default() ) ) - } - else - { - writeln!(f, "{}", AsTable::new( &self.assistants ).table_to_string_with_format( &output_format::Table::default() ) ) - } + display_tabular_data( &AsTable::new( &self.assistants ), f, &self.table_config ) } } @@ -53,12 +46,14 @@ mod private pub async fn action ( client : &Client, - show_records_as_tables : bool, + table_config : TableConfig, ) -> Result < ListReport > { + check_table_style( &table_config )?; + let response = client.list_assistant( None, None, None, None ).await?; let assistants = response.data.into_iter().map( AssistantObjectWrap ).collect(); - Ok( ListReport { show_records_as_tables, assistants } ) + Ok( ListReport { table_config, assistants } ) } } diff --git a/module/move/assistant/src/actions/openai_files_list.rs b/module/move/assistant/src/actions/openai_files_list.rs index 8a3f371a92..12f6ab7bd0 100644 --- a/module/move/assistant/src/actions/openai_files_list.rs +++ b/module/move/assistant/src/actions/openai_files_list.rs @@ -7,24 +7,24 @@ mod private use std::fmt; - use format_tools:: - { - AsTable, - TableFormatter, - output_format, - }; + use format_tools::AsTable; use crate::*; use client::Client; + use debug::FileDataWrap; - use actions::openai::Result; + + use actions::openai::{ Result, check_table_style }; + + use commands::TableConfig; + use util::display_table::display_tabular_data; /// Report for `openai files list`. #[ derive( Debug ) ] pub struct ListReport { - /// Show records as separate tables. - pub show_records_as_tables : bool, + /// Configure table formatting. + pub table_config : TableConfig, /// Files in OpenAI. pub files : Vec< FileDataWrap > @@ -38,14 +38,7 @@ mod private f : &mut fmt::Formatter< '_ > ) -> fmt::Result { - if self.show_records_as_tables - { - writeln!(f, "{}", AsTable::new( &self.files ).table_to_string_with_format( &output_format::Records::default() ) ) - } - else - { - writeln!(f, "{}", AsTable::new( &self.files ).table_to_string_with_format( &output_format::Table::default() ) ) - } + display_tabular_data( &AsTable::new( &self.files ), f, &self.table_config ) } } @@ -53,12 +46,14 @@ mod private pub async fn action ( client : &Client, - show_records_as_tables : bool, + table_config : TableConfig, ) -> Result < ListReport > { + check_table_style( &table_config )?; + let response = client.file_list().await?; let files = response.data.into_iter().map( FileDataWrap ).collect(); - Ok( ListReport { show_records_as_tables, files } ) + Ok( ListReport { table_config, files } ) } } diff --git a/module/move/assistant/src/actions/openai_runs_list.rs b/module/move/assistant/src/actions/openai_runs_list.rs index 631b6ed4cb..d5bf8098c6 100644 --- a/module/move/assistant/src/actions/openai_runs_list.rs +++ b/module/move/assistant/src/actions/openai_runs_list.rs @@ -7,27 +7,27 @@ mod private use std::fmt; - use format_tools:: - { - AsTable, - TableFormatter, - output_format, - }; + use format_tools::AsTable; use crate::*; use client::Client; + use debug::RunObjectWrap; - use actions::openai::Result; + + use actions::openai::{ Result, check_table_style }; + + use commands::TableConfig; + use util::display_table::display_tabular_data; /// Report for `openai runs list`. #[ derive( Debug ) ] pub struct ListReport { - /// Show records as separate tables. - pub show_records_as_tables : bool, + /// Configure table formatting. + pub table_config : TableConfig, /// Current OpenAI runs. - pub runs : Vec< RunObjectWrap > + pub runs : Vec< RunObjectWrap >, } impl fmt::Display for ListReport @@ -38,14 +38,7 @@ mod private f : &mut fmt::Formatter< '_ > ) -> fmt::Result { - if self.show_records_as_tables - { - writeln!(f, "{}", AsTable::new( &self.runs ).table_to_string_with_format( &output_format::Records::default() ) ) - } - else - { - writeln!(f, "{}", AsTable::new( &self.runs ).table_to_string_with_format( &output_format::Table::default() ) ) - } + display_tabular_data( &AsTable::new( &self.runs ), f, &self.table_config ) } } @@ -54,12 +47,14 @@ mod private ( client : &Client, thread_id : String, - show_records_as_tables : bool, + table_config : TableConfig, ) -> Result < ListReport > { + check_table_style( &table_config )?; + let response = client.list_run( thread_id, None, None, None, None ).await?; let runs = response.data.into_iter().map( RunObjectWrap ).collect(); - Ok( ListReport { show_records_as_tables, runs } ) + Ok( ListReport { table_config, runs } ) } } diff --git a/module/move/assistant/src/bin/main.rs b/module/move/assistant/src/bin/main.rs index adeeaf150f..419030d03b 100644 --- a/module/move/assistant/src/bin/main.rs +++ b/module/move/assistant/src/bin/main.rs @@ -26,7 +26,7 @@ async fn main() -> Result< (), Box< dyn Error > > let secret = Secret::load()?; - let client = client::client( &secret )?; + let client = client( &secret )?; let cli = Cli::parse(); diff --git a/module/move/assistant/src/commands.rs b/module/move/assistant/src/commands.rs index 7a8f56c76c..7f54ec320b 100644 --- a/module/move/assistant/src/commands.rs +++ b/module/move/assistant/src/commands.rs @@ -29,6 +29,35 @@ mod private OpenAi( openai::Command ), } + // const DEFAULT_MAX_TABLE_WIDTH: usize = 130; + // Commented out as not yet implemented in `format_tools`. + + /// Common collection of arguments for formatting tabular data. + #[ derive( Debug, Parser ) ] + pub struct TableConfig + { + /// Limit table widht. + // #[ arg( long, default_value_t = DEFAULT_MAX_TABLE_WIDTH ) ] + // pub max_table_width : usize, + // Commented out as not yet implemented in `format_tools`. + + /// Show tabular data as an ordinary table. + #[ arg( long ) ] + pub as_table : bool, + + /// Show each record of a tabular data as a separate table. + #[ arg( long ) ] + pub as_records : bool, + + /// Show only keys (columns) of tabular data. + #[ arg( long ) ] + pub columns : bool, + + /// Filter columns of tabular data. + #[ arg( long, value_delimiter( ',' ) ) ] + pub filter_columns : Vec< String >, + } + } crate::mod_interface! @@ -45,5 +74,6 @@ crate::mod_interface! { Cli, CliCommand, + TableConfig, }; } diff --git a/module/move/assistant/src/commands/openai_assistants.rs b/module/move/assistant/src/commands/openai_assistants.rs index 66e2d057e8..0d941c94ba 100644 --- a/module/move/assistant/src/commands/openai_assistants.rs +++ b/module/move/assistant/src/commands/openai_assistants.rs @@ -9,7 +9,7 @@ mod private use crate::*; use client::Client; - use commands::openai_assistants_list; + use commands::{ openai_assistants_list, TableConfig }; /// OpenAI assistants. #[ derive ( Debug, Subcommand ) ] @@ -18,9 +18,9 @@ mod private /// List OpenAI assistants. List { - /// Show records as separate tables. - #[ arg( long, default_value_t = false ) ] - show_records_as_tables : bool + /// Configure table formatting. + #[ clap( flatten ) ] + table_config : TableConfig, }, } @@ -33,9 +33,9 @@ mod private { match command { - Command::List{ show_records_as_tables } => + Command::List{ table_config } => { - openai_assistants_list::command( client, show_records_as_tables ).await; + openai_assistants_list::command( client, table_config ).await; } } } diff --git a/module/move/assistant/src/commands/openai_assistants_list.rs b/module/move/assistant/src/commands/openai_assistants_list.rs index 95e1ffb62c..6ce7a80ac4 100644 --- a/module/move/assistant/src/commands/openai_assistants_list.rs +++ b/module/move/assistant/src/commands/openai_assistants_list.rs @@ -8,15 +8,16 @@ mod private use crate::*; use client::Client; use actions; + use commands::TableConfig; /// List OpenAI assistants command. pub async fn command ( client : &Client, - show_records_as_tables : bool, + table_config : TableConfig, ) { - let result = actions::openai_assistants_list::action( client, show_records_as_tables ).await; + let result = actions::openai_assistants_list::action( client, table_config ).await; match result { diff --git a/module/move/assistant/src/commands/openai_files.rs b/module/move/assistant/src/commands/openai_files.rs index 33e18fea0a..ea72a42ad1 100644 --- a/module/move/assistant/src/commands/openai_files.rs +++ b/module/move/assistant/src/commands/openai_files.rs @@ -9,7 +9,7 @@ mod private use crate::*; use client::Client; - use commands::openai_files_list; + use commands::{ openai_files_list, TableConfig }; /// OpenAI files. #[ derive ( Debug, Subcommand ) ] @@ -18,9 +18,9 @@ mod private /// List OpenAI files. List { - /// Show records as separate tables. - #[ arg( long, default_value_t = false ) ] - show_records_as_tables : bool + /// Configure table formatting. + #[ clap( flatten ) ] + table_config : TableConfig, }, } @@ -33,9 +33,9 @@ mod private { match command { - Command::List{ show_records_as_tables } => + Command::List{ table_config } => { - openai_files_list::command( client, show_records_as_tables ).await; + openai_files_list::command( client, table_config ).await; } } } diff --git a/module/move/assistant/src/commands/openai_files_list.rs b/module/move/assistant/src/commands/openai_files_list.rs index 04bc83c0c4..6225b9faf2 100644 --- a/module/move/assistant/src/commands/openai_files_list.rs +++ b/module/move/assistant/src/commands/openai_files_list.rs @@ -8,15 +8,16 @@ mod private use crate::*; use client::Client; use actions; + use commands::TableConfig; /// List files in your OpenAI API. pub async fn command ( client : &Client, - show_records_as_tables : bool, + table_config : TableConfig, ) { - let result = actions::openai_files_list::action( client, show_records_as_tables ).await; + let result = actions::openai_files_list::action( client, table_config ).await; match result { diff --git a/module/move/assistant/src/commands/openai_runs.rs b/module/move/assistant/src/commands/openai_runs.rs index e5e183b33e..2cf7812000 100644 --- a/module/move/assistant/src/commands/openai_runs.rs +++ b/module/move/assistant/src/commands/openai_runs.rs @@ -9,7 +9,7 @@ mod private use crate::*; use client::Client; - use commands::openai_runs_list; + use commands::{ openai_runs_list, TableConfig }; /// OpenAI runs. #[ derive ( Debug, Subcommand ) ] @@ -21,10 +21,10 @@ mod private /// Thread ID. thread_id : String, - /// Show records as separate tables. - #[ arg( long, default_value_t = false ) ] - show_records_as_tables : bool - }, + /// Configure table formatting. + #[ clap( flatten ) ] + table_config : TableConfig, + } } /// Execute OpenAI commands related to runs. @@ -36,9 +36,9 @@ mod private { match command { - Command::List { thread_id, show_records_as_tables } => + Command::List { thread_id, table_config } => { - openai_runs_list::command( client, thread_id, show_records_as_tables ).await; + openai_runs_list::command( client, thread_id, table_config ).await; } } } diff --git a/module/move/assistant/src/commands/openai_runs_list.rs b/module/move/assistant/src/commands/openai_runs_list.rs index 928e0b473a..6d08d64ed3 100644 --- a/module/move/assistant/src/commands/openai_runs_list.rs +++ b/module/move/assistant/src/commands/openai_runs_list.rs @@ -8,16 +8,17 @@ mod private use crate::*; use client::Client; use actions; + use commands::TableConfig; /// List runs in the thread in OpenAI API. pub async fn command ( client : &Client, thread_id : String, - show_records_as_tables : bool, + table_config : TableConfig, ) { - let result = actions::openai_runs_list::action( client, thread_id, show_records_as_tables ).await; + let result = actions::openai_runs_list::action( client, thread_id, table_config ).await; match result { diff --git a/module/move/assistant/src/lib.rs b/module/move/assistant/src/lib.rs index c064213715..5a33e41692 100644 --- a/module/move/assistant/src/lib.rs +++ b/module/move/assistant/src/lib.rs @@ -32,6 +32,7 @@ crate::mod_interface! layer commands; layer actions; layer secret; + layer util; exposed use ::reflect_tools:: { diff --git a/module/move/assistant/src/util.rs b/module/move/assistant/src/util.rs new file mode 100644 index 0000000000..7e34c0fd16 --- /dev/null +++ b/module/move/assistant/src/util.rs @@ -0,0 +1,10 @@ +//! +//! Collection of utility functions for this crate. +//! + +mod private {} + +crate::mod_interface! +{ + layer display_table; +} \ No newline at end of file diff --git a/module/move/assistant/src/util/display_table.rs b/module/move/assistant/src/util/display_table.rs new file mode 100644 index 0000000000..edced5fbdd --- /dev/null +++ b/module/move/assistant/src/util/display_table.rs @@ -0,0 +1,101 @@ +//! +//! Function for displaying tabular data according to `TableConfig`. +//! + +mod private +{ + + use std::fmt; + + use format_tools:: + { + TableFormatter, + output_format, + print, + TableOutputFormat, + }; + + use crate::*; + use commands::{ TableConfig }; + + /// Function for displaying tabular data according to `TableConfig`. + pub fn display_tabular_data<'a> + ( + data : &'a impl TableFormatter< 'a >, + f : &mut fmt::Formatter< '_ >, + table_config : &'a TableConfig, + ) -> fmt::Result + { + if table_config.as_table + { + display_table( data, f, &table_config.filter_columns ) + } + else if table_config.as_records + { + display_records( data, f, &table_config.filter_columns ) + } + else if table_config.columns + { + display_columns( data, f, &table_config.filter_columns ) + } + else + { + display_table( data, f, &table_config.filter_columns ) + } + } + + fn display_table<'a> + ( + data : &'a impl TableFormatter< 'a >, + f : &mut fmt::Formatter< '_ >, + filter_columns : &'a Vec< String >, + ) -> fmt::Result + { + display_data( data, f, filter_columns, output_format::Table::default() ) + } + + fn display_records<'a> + ( + data : &'a impl TableFormatter< 'a >, + f : &mut fmt::Formatter< '_ >, + filter_columns : &'a Vec< String >, + ) -> fmt::Result + { + display_data( data, f, filter_columns, output_format::Records::default() ) + } + + fn display_columns<'a> + ( + data : &'a impl TableFormatter< 'a >, + f : &mut fmt::Formatter< '_ >, + filter_columns : &'a Vec< String >, + ) -> fmt::Result + { + display_data( data, f, filter_columns, output_format::Keys::default() ) + } + + fn display_data<'a> + ( + data : &'a impl TableFormatter< 'a >, + f : &mut fmt::Formatter< '_ >, + filter_columns : &'a Vec< String >, + format : impl TableOutputFormat, + ) -> fmt::Result + { + let mut printer = print::Printer::with_format( &format ); + let binding = | title : &str | + { + filter_columns.is_empty() || filter_columns.iter().any( |c| c.as_str() == title ) + }; + printer.filter_col = &binding; + + let mut context = print::Context::new( f, printer ); + TableFormatter::fmt( data, &mut context ) + } + +} + +crate::mod_interface! +{ + own use display_tabular_data; +} \ No newline at end of file From ea863247c18a103a29ad4b1bac7b500c079eac0c Mon Sep 17 00:00:00 2001 From: Ruslan Date: Fri, 22 Nov 2024 10:49:29 +0200 Subject: [PATCH 49/67] READY : `format_tools`: limit table width (#1494) * Remove slices * Start moving * Tests are passing * Not ideal * Fix test * Fix algorithm and add tests * Fix for Records * Fix from the feedback * Rework documentation and naming * Fix docs * Move out `text_wrap` from `table` * Make `Records` use general `text_wrap` --- module/core/format_tools/src/format.rs | 4 + .../src/format/output_format/records.rs | 122 ++++++--- .../src/format/output_format/table.rs | 166 ++++++------ module/core/format_tools/src/format/print.rs | 70 +---- module/core/format_tools/src/format/string.rs | 108 ++++++++ .../core/format_tools/src/format/text_wrap.rs | 256 ++++++++++++++++++ .../tests/inc/format_records_test.rs | 134 ++++++++- .../tests/inc/format_table_test.rs | 129 ++++++++- module/core/format_tools/tests/tests.rs | 2 +- 9 files changed, 806 insertions(+), 185 deletions(-) create mode 100644 module/core/format_tools/src/format/text_wrap.rs diff --git a/module/core/format_tools/src/format.rs b/module/core/format_tools/src/format.rs index fb207181b3..6200a4f5d8 100644 --- a/module/core/format_tools/src/format.rs +++ b/module/core/format_tools/src/format.rs @@ -289,6 +289,7 @@ pub mod string; pub mod table; pub mod to_string; pub mod to_string_with_fallback; +pub mod text_wrap; /// A strucutre for diagnostic and demonstration purpose. #[ doc( hidden ) ] @@ -317,6 +318,7 @@ pub mod own table::orphan::*, to_string::orphan::*, to_string_with_fallback::orphan::*, + text_wrap::orphan::*, }; } @@ -369,6 +371,7 @@ pub mod exposed table::exposed::*, to_string::exposed::*, to_string_with_fallback::exposed::*, + text_wrap::exposed::*, }; } @@ -391,6 +394,7 @@ pub mod prelude table::prelude::*, to_string::prelude::*, to_string_with_fallback::prelude::*, + text_wrap::prelude::*, }; } diff --git a/module/core/format_tools/src/format/output_format/records.rs b/module/core/format_tools/src/format/output_format/records.rs index 45a1206e41..1c89f34038 100644 --- a/module/core/format_tools/src/format/output_format/records.rs +++ b/module/core/format_tools/src/format/output_format/records.rs @@ -22,12 +22,12 @@ //! use crate::*; -use md_math::MdOffset; use print:: { InputExtract, Context, }; +use std::borrow::Cow; use core:: { fmt, @@ -59,6 +59,8 @@ pub struct Records pub cell_postfix : String, /// Separator used between table columns. pub cell_separator : String, + /// Limit table width. If the value is zero, then no limitation. + pub max_width: usize, // /// Horizontal line character. // pub h : char, // /// Vertical line character. @@ -91,6 +93,25 @@ impl Records static INSTANCE : OnceLock< Records > = OnceLock::new(); INSTANCE.get_or_init( || Records::default() ) } + + /// Calculate how much space is minimally needed in order to generate an output with this output formatter. + /// It will be impossible to render tables smaller than the result of `min_width()`. + /// + /// This function is similar to `output_format::Table::min_width`, but it does not contain a + /// `column_count` as it always equal to 2, and it aslo uses the `output_format::Records` + /// style parameters. + pub fn min_width + ( + &self, + ) -> usize + { + // 2 is used here, because `Records` displays 2 columns: keys and values. + self.row_prefix.chars().count() + + self.row_postfix.chars().count() + + 2 * ( self.cell_postfix.chars().count() + self.cell_prefix.chars().count() ) + + self.cell_separator.chars().count() + + 2 + } } impl Default for Records @@ -108,6 +129,8 @@ impl Default for Records let table_postfix = "".to_string(); let table_separator = "\n".to_string(); + let max_width = 0; + // let h = '─'; // let v = '|'; // let t_l = '├'; @@ -131,6 +154,7 @@ impl Default for Records cell_prefix, cell_postfix, cell_separator, + max_width, // h, // v, // t_l, @@ -155,70 +179,88 @@ impl TableOutputFormat for Records c : & mut Context< 'buf >, ) -> fmt::Result { + use format::text_wrap::{ text_wrap, width_calculate }; + + if self.max_width != 0 && self.max_width < self.min_width() + { + return Err( fmt::Error ); + } + + // 2 because there are only 2 columns: key and value. + let columns_max_width = if self.max_width == 0 { 0 } else { self.max_width - self.min_width() + 2 }; - let label_width = x.header().fold( 0, | acc, cell | acc.max( cell.1[ 0 ] ) ); + let keys : Vec< ( Cow< 'data, str >, [ usize; 2 ] ) > = x.header().collect(); + let keys_width = width_calculate( &keys ); write!( c.buf, "{}", self.table_prefix )?; - let mut first = true; - // Write each record - for ( irow, row ) in x.rows() - { + let mut printed_tables_count = 0; - if !row.vis + for ( itable_descriptor, table_descriptor ) in x.row_descriptors.iter().enumerate() + { + if !table_descriptor.vis || ( x.has_header && itable_descriptor == 0 ) { continue; } - if first - { - first = false; - } - else + if printed_tables_count > 0 { write!( c.buf, "{}", self.table_separator )?; } - let slice_width = x.data[ irow ].iter().fold( 0, | acc, cell | acc.max( cell.1[ 0 ] ) ); + printed_tables_count += 1; + + writeln!( c.buf, " = {}", table_descriptor.irow )?; - writeln!( c.buf, " = {}", irow )?; + let values = &x.data[ itable_descriptor ]; + let values_width = width_calculate( &values ); - for ( icol, _col ) in x.col_descriptors.iter().enumerate() + let table_for_wrapping : Vec< Vec< ( Cow< 'data, str >, [ usize; 2] ) > > = + keys.iter().enumerate().map( | ( ikey, key ) | { - let cell = &x.data[ irow ][ icol ]; - let height = cell.1[ 1 ]; + vec![ key.clone(), values[ ikey ].clone() ] + }).collect(); - for islice in 0..height + let wrapped_text = text_wrap + ( + table_for_wrapping.iter(), + &[ keys_width, values_width ], + columns_max_width, + keys_width + values_width, + ); + + for ( irow, cols ) in wrapped_text.data.into_iter().enumerate() + { + if irow != 0 { - let label = x.header_slice( islice, icol ); - let md_index = [ islice, icol, irow ]; - let slice = x.slices[ x.slices_dim.md_offset( md_index ) ]; - - if icol > 0 || islice > 0 - { - write!( c.buf, "{}", self.row_separator )?; - } - - write!( c.buf, "{}", self.row_prefix )?; - - write!( c.buf, "{}", self.cell_prefix )?; - write!( c.buf, "{: usize + { + self.row_prefix.chars().count() + + self.row_postfix.chars().count() + + column_count * ( self.cell_postfix.chars().count() + self.cell_prefix.chars().count() ) + + if column_count == 0 { 0 } else { ( column_count - 1 ) * self.cell_separator.chars().count() } + + column_count + } } impl TableOutputFormat for Table { fn extract_write< 'buf, 'data >( &self, x : &InputExtract< 'data >, c : &mut Context< 'buf > ) -> fmt::Result { - use md_math::MdOffset; + use format::text_wrap::text_wrap; let cell_prefix = &self.cell_prefix; let cell_postfix = &self.cell_postfix; @@ -173,105 +196,92 @@ impl TableOutputFormat for Table let row_separator = &self.row_separator; let h = self.h.to_string(); - let mut delimitting_header = self.delimitting_header; - let row_width = if delimitting_header + let column_count = x.col_descriptors.len(); + + if self.max_width != 0 && ( self.min_width( column_count ) > self.max_width ) { - let mut grid_width = x.mcells_vis[ 0 ] * ( cell_prefix.chars().count() + cell_postfix.chars().count() ); - grid_width += row_prefix.chars().count() + row_postfix.chars().count(); - if x.mcells_vis[ 0 ] > 0 - { - grid_width += ( x.mcells_vis[ 0 ] - 1 ) * ( cell_separator.chars().count() ); - } - x.mchars[ 0 ] + grid_width + return Err( fmt::Error ); } - else - { - 0 - }; - let mut prev_typ : Option< LineType > = None; - // dbg!( x.row_descriptors.len() ); - - for ( irow, row ) in x.row_descriptors.iter().enumerate() + let columns_nowrap_width = x.col_descriptors.iter().map( |c| c.width ).sum::(); + let visual_elements_width = self.min_width( column_count ) - column_count; + + let filtered_data = x.row_descriptors.iter().filter_map( | r | { - let height = row.height; - - if delimitting_header + if r.vis { - if let Some( prev_typ ) = prev_typ - { - if prev_typ == LineType::Header && row.typ == LineType::Regular - { - write!( c.buf, "{}", row_separator )?; - write!( c.buf, "{}", h.repeat( row_width ) )?; - delimitting_header = false - } - } - if row.vis - { - prev_typ = Some( row.typ ); - } + Some( &x.data[ r.irow ] ) } - - if !row.vis + else { - continue; + None + } + }); + + let wrapped_text = text_wrap + ( + filtered_data, + x.col_descriptors.iter().map( | c | c.width ).collect::< Vec< usize > >(), + if self.max_width == 0 { 0 } else { self.max_width - visual_elements_width }, + columns_nowrap_width + ); + + let new_columns_widthes = wrapped_text.column_widthes.iter().sum::(); + let new_row_width = new_columns_widthes + visual_elements_width; + + let mut printed_row_count = 0; + + for row in wrapped_text.data.iter() + { + if printed_row_count == wrapped_text.first_row_height && x.has_header && self.delimitting_header + { + write!( c.buf, "{}", row_separator )?; + write!( c.buf, "{}", h.repeat( new_row_width ) )?; + } + + if printed_row_count > 0 + { + write!( c.buf, "{}", row_separator )?; } - // dbg!( row.height ); + printed_row_count += 1; - for islice in 0..height - { + write!( c.buf, "{}", row_prefix )?; - if irow > 0 + for ( icol, col ) in row.iter().enumerate() + { + let cell_wrapped_width = col.wrap_width; + let column_width = wrapped_text.column_widthes[ icol ]; + let slice_width = col.content.chars().count(); + + if icol > 0 { - write!( c.buf, "{}", row_separator )?; + write!( c.buf, "{}", cell_separator )?; } - write!( c.buf, "{}", row_prefix )?; + write!( c.buf, "{}", cell_prefix )?; + + let lspaces = ( column_width - cell_wrapped_width ) / 2; + let rspaces = ( ( column_width - cell_wrapped_width ) as f32 / 2 as f32 ).round() as usize + cell_wrapped_width - slice_width; - for icol in 0 .. x.col_descriptors.len() + if lspaces > 0 { - let col = &x.col_descriptors[ icol ]; - let cell_width = x.data[ irow ][ icol ].1[0]; - let width = col.width; - let md_index = [ islice, icol, irow as usize ]; - let slice = x.slices[ x.slices_dim.md_offset( md_index ) ]; - - // println!( "md_index : {md_index:?} | md_offset : {} | slice : {slice}", x.slices_dim.md_offset( md_index ) ); - - if icol > 0 - { - write!( c.buf, "{}", cell_separator )?; - } - - write!( c.buf, "{}", cell_prefix )?; - - // println!( "icol : {icol} | irow : {irow} | width : {width} | cell_width : {cell_width} | slice.len() : {}", slice.len() ); - - let lspaces = ( width - cell_width ) / 2; - let rspaces = ( width - cell_width + 1 ) / 2 + cell_width - slice.chars().count(); - - // println!( "icol : {icol} | irow : {irow} | width : {width} | cell_width : {cell_width} | lspaces : {lspaces} | rspaces : {rspaces}" ); - - if lspaces > 0 - { - write!( c.buf, "{: 0 - { - write!( c.buf, "{:>width$}", " ", width = rspaces )?; - } + write!( c.buf, "{: 0 + { + write!( c.buf, "{:>width$}", " ", width = rspaces )?; } - write!( c.buf, "{}", row_postfix )?; + write!( c.buf, "{}", cell_postfix )?; } + write!( c.buf, "{}", row_postfix )?; } Ok(()) } -} +} \ No newline at end of file diff --git a/module/core/format_tools/src/format/print.rs b/module/core/format_tools/src/format/print.rs index f3c5d2acc6..aada1e5425 100644 --- a/module/core/format_tools/src/format/print.rs +++ b/module/core/format_tools/src/format/print.rs @@ -7,10 +7,9 @@ mod private { use crate::*; - use md_math::MdOffset; use std:: { - borrow::Cow, + borrow::{ Cow, Borrow }, collections::HashMap, }; use core:: @@ -282,11 +281,6 @@ mod private // string, size, pub data : Vec< Vec< ( Cow< 'data, str >, [ usize ; 2 ] ) > >, // xxx : use maybe flat vector - /// Dimensions of slices for retrieving data from multi-matrix. - pub slices_dim : [ usize ; 3 ], - /// Extracted slices or strings for further processing. - pub slices : Vec< &'data str >, - } // @@ -340,30 +334,26 @@ mod private /// Returns a slice from the header, or an empty string if no header is present. /// - /// This function retrieves a specific slice from the header row based on the provided indices. - /// If the table does not have a header, it returns an empty string. - /// /// # Arguments /// - /// - `islice`: The slice index within the header cell. /// - `icol`: The column index within the header row. /// /// # Returns /// - /// A string slice representing the header content at the specified indices. + /// A string slice representing the header content. /// - pub fn header_slice( & self, islice : usize, icol : usize ) -> & str + pub fn header_slice( & self, icol : usize ) -> & str { if self.has_header { - let md_index = [ islice, icol, 0 ]; - self.slices[ self.slices_dim.md_offset( md_index ) ] + self.data[ 0 ][ icol ].0.borrow() } else { "" } } + /// Extract input data from and collect it in a format consumable by output formatter. pub fn extract< 't, 'context, Table, RowKey, Row, CellKey> ( @@ -379,12 +369,10 @@ mod private Table : TableRows< RowKey = RowKey, Row = Row, CellKey = CellKey >, Table : TableHeader< CellKey = CellKey >, RowKey : table::RowKey, - Row : Cells< CellKey> + 'data, + Row : Cells< CellKey > + 'data, CellKey : table::CellKey + ?Sized + 'data, // CellRepr : table::CellRepr, { - use md_math::MdOffset; - // let mcells = table.mcells(); let mut mcells_vis = [ 0 ; 2 ]; let mut mcells = [ 0 ; 2 ]; @@ -532,22 +520,6 @@ mod private mchars[ 0 ] = col_descriptors.iter().fold( 0, | acc, col | acc + col.width ); mchars[ 1 ] = row_descriptors.iter().fold( 0, | acc, row | acc + if row.vis { row.height } else { 0 } ); - // cook slices multi-matrix - - let mut slices_dim = [ 1, mcells[ 0 ], mcells[ 1 ] ]; - slices_dim[ 0 ] = row_descriptors - .iter() - .fold( 0, | acc : usize, row | acc.max( row.height ) ) - ; - - let slices_len = slices_dim[ 0 ] * slices_dim[ 1 ] * slices_dim[ 2 ]; - let slices : Vec< &str > = vec![ "" ; slices_len ]; - - // assert_eq!( mcells, mcells, r#"Incorrect multidimensional size of table - // mcells <> mcells - // {mcells:?} <> {mcells:?}"# ); - // println!( "mcells : {mcells:?} | mcells : {mcells:?} | mcells_vis : {mcells_vis:?}" ); - let mut x = InputExtract::< '_ > { mcells, @@ -557,42 +529,16 @@ mod private row_descriptors, data, has_header, - slices_dim, - slices, }; - // extract slices - - let mut slices : Vec< &str > = vec![]; - std::mem::swap( &mut x.slices, &mut slices ); - - let mut irow : isize = -1; - for row_data in x.data.iter() + if x.data.len() > 0 { - - irow += 1; - for icol in 0 .. x.col_descriptors.len() { - let cell = &row_data[ icol ]; - string::lines( cell.0.as_ref() ) - .enumerate() - .for_each( | ( layer, s ) | - { - let md_index = [ layer, icol, irow as usize ]; - slices[ x.slices_dim.md_offset( md_index ) ] = s; - }) - ; - if irow == 0 - { - x.col_descriptors[ icol ].label = cell.0.as_ref(); - } + x.col_descriptors[ icol ].label = x.data[ 0 ][ icol ].0.as_ref(); } - } - std::mem::swap( &mut x.slices, &mut slices ); - return callback( &x ); } diff --git a/module/core/format_tools/src/format/string.rs b/module/core/format_tools/src/format/string.rs index 619d1690c2..ee34e9e718 100644 --- a/module/core/format_tools/src/format/string.rs +++ b/module/core/format_tools/src/format/string.rs @@ -114,6 +114,47 @@ mod private Lines::new( src.as_ref() ) } + /// Returns an iterator over the lines of a string slice with text wrapping. + /// + /// This function provides an iterator that yields each line of the input string slice. + /// It is based on previous iterator `lines` but it also includes text wrapping that is + /// controlled via `limit_width` argument. If the string contains a trailing new line, + /// then an empty string will be yielded in this iterator. + /// + /// # 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. + /// + /// * `limit_width` - text wrapping limit. Lines that are longer than this parameter will + // be split into smaller lines. + /// + /// # Returns + /// + /// An iterator of type `LinesWithLimit` that yields each line as a `&str`. + /// + /// # Examples + /// + /// ``` + /// let text = "Hello\nWorld\n"; + /// let mut lines = format_tools::string::lines_with_limit( text, 3 ); + /// assert_eq!( lines.next(), Some( "Hel" ) ); + /// assert_eq!( lines.next(), Some( "lo" ) ); + /// assert_eq!( lines.next(), Some( "Wor" ) ); + /// assert_eq!( lines.next(), Some( "ld" ) ); + /// assert_eq!( lines.next(), Some( "" ) ); + /// assert_eq!( lines.next(), None ); + /// ``` + pub fn lines_with_limit< S : AsRef< str > + ?Sized > + ( + src : & S, + limit_width : usize + ) + -> LinesWithLimit< '_ > + { + LinesWithLimit::new( src.as_ref(), limit_width ) + } + /// An iterator over the lines of a string slice. /// /// This struct implements the `Iterator` trait, allowing you to iterate over the lines @@ -128,6 +169,7 @@ mod private has_trailing_newline : bool, finished : bool, } + impl< 'a > Lines< 'a > { fn new( input : &'a str ) -> Self @@ -172,6 +214,70 @@ mod private } } + /// An iterator over the lines of a string slice with text wrapping. + /// + /// This struct implements the `Iterator` trait, allowing you to iterate over the parts + /// of a string. It uses `Lines` iterator and splits lines if they are longer that the + /// `limit_width` parameter. If the string contains a trailing new line, then an empty + /// string will be yielded in this iterator. + /// + /// If `limit_width` is equal to 0, then no wrapping is applied, and behaviour of this + /// iterator is equals to `Lines` iterator. + #[ derive( Debug ) ] + pub struct LinesWithLimit< 'a > + { + lines : Lines< 'a >, + limit_width : usize, + cur : Option< &'a str >, + } + + impl< 'a > LinesWithLimit< 'a > + { + fn new( input : &'a str, limit_width : usize ) -> Self + { + LinesWithLimit + { + lines : lines( input ), + limit_width, + cur : None, + } + } + } + + impl< 'a > Iterator for LinesWithLimit< 'a > + { + type Item = &'a str; + + fn next( &mut self ) -> Option< Self::Item > + { + if self.cur.is_none() || self.cur.is_some_and( str::is_empty ) + { + self.cur = self.lines.next(); + } + + match self.cur + { + None => return None, + + Some( cur ) => + { + if self.limit_width == 0 + { + self.cur = None; + Some( cur ) + } + else + { + let (chunk, rest) = cur.split_at(self.limit_width.min(cur.len())); + self.cur = Some( rest ); + + Some(chunk) + } + } + } + } + } + } #[ allow( unused_imports ) ] @@ -191,6 +297,8 @@ pub mod own size, lines, Lines, + lines_with_limit, + LinesWithLimit, }; } diff --git a/module/core/format_tools/src/format/text_wrap.rs b/module/core/format_tools/src/format/text_wrap.rs new file mode 100644 index 0000000000..695ac287cd --- /dev/null +++ b/module/core/format_tools/src/format/text_wrap.rs @@ -0,0 +1,256 @@ +//! +//! Text wrapping function. +//! + +/// Define a private namespace for all its items. +mod private +{ + + use std::borrow::Cow; + + use crate::*; + + /// Struct that represents a wrapped tabular data. It is similar to `InputExtract`, + /// but we cannot use it as it does not wrap the text and it contains wrong column + /// widthes and heights (as they are dependent on wrapping too). + #[ derive( Debug ) ] + pub struct WrappedInputExtract< 'data > + { + /// Tabular data of rows and columns. + /// Note: these cells does not represent the actual information cells in the + /// original table. These cells are wrapped and used only for displaying. This also + /// means that one row in original table can be represented here with one or more + /// rows. + pub data: Vec< Vec< WrappedCell< 'data > > >, + + /// New widthes of columns that include wrapping. + pub column_widthes : Vec< usize >, + + /// Size of the first row of the table. + /// This parameter is used in case header of the table should be displayed. + pub first_row_height : usize, + } + + /// Struct that represents a content of a wrapped cell. + /// It contains the slice of the cell as well as its original width. + /// + /// Parameter `wrap_width` is needed as text in `output_format::Table` is centered. + /// However it is centered according to whole cell size and not the size of wrapped + /// text slice. + /// + /// Example that depicts the importance of `wrap_width` parameter: + /// + /// 1) | [ | 2) | [ | + /// | line1, | | line1, | + /// | line2 | | line2 | + /// | ] | | ] | + /// + /// The first case seems to be properly formatted, while the second case took centering + /// too literally. That is why `wrap_width` is introduced, and additional spaces to the + /// right side should be included by the output formatter. + #[ derive( Debug ) ] + pub struct WrappedCell< 'data > + { + /// Width of the cell. In calculations use this width instead of slice length in order + /// to properly center the text. See example in the doc string of the parent struct. + pub wrap_width : usize, + + /// Actual content of the cell. + pub content : Cow< 'data, str > + } + + /// Wrap table cells. + /// + /// `InputExtract` contains cells with full content, so it represents the logical + /// structure of the table. `WrappedInputExtract` wraps original cells to smaller + /// cells. The resulting data is more low-level and corresponds to the table that + /// will be actually printed to the console (or other output type). + /// + /// `InputExtract` is not directly passed to this function, as it made to be general. + /// Instead you pass table cells in `data` argument and pass a vector of column widthes + /// in `columns_width_list` generated by `InputExtract`. + /// + /// `columns_width_list` is a slice, this is more effective and general than just a `Vec`. + /// In table style, there could be many columns, but in records style there will be + /// always 2 columns - this number is known at compile time, so we can use a slice object. + /// + /// Notice: + /// 1. Data passed to this function should contain only visible rows and columns. + /// It does not perform additional filtering. + /// 2. `data` parameters is **vector of rows of columns** (like and ordinary table). + /// This means that in styles like `Records` where headers and rows turned into columns + /// You have to transpose your data before passing it to this function. + /// + /// Wrapping is controlled by `columns_max_width` and `columns_nowrap_width` parameters. + /// + /// - `columns_max_width` is the size that is allowed to be occupied by columns. + /// It equals to maximum table width minus lengthes of visual elements (prefixes, + /// postfixes, separators, etc.). + /// + /// - `columns_nowrap_width` is the sum of column widthes of cells without wrapping (basically, + /// the sum of widthes of column descriptors in `InputExtract`). + /// + /// The function will perform wrapping and shrink the columns so that they occupy not + /// more than `columns_max_width`. + /// + /// If `columns_max_width` is equal to 0, then no wrapping will be performed. + pub fn text_wrap< 'data > + ( + data : impl Iterator< Item = &'data Vec< ( Cow< 'data, str >, [ usize; 2 ] ) > >, + columns_width_list : impl AsRef< [ usize ] >, + columns_max_width : usize, + columns_nowrap_width : usize, + ) + -> WrappedInputExtract< 'data > + { + let columns_width_list = columns_width_list.as_ref(); + + let mut first_row_height = 0; + let mut new_data = Vec::new(); + let mut column_widthes = Vec::new(); + + if columns_max_width == 0 || columns_max_width >= columns_nowrap_width + { + column_widthes.extend( columns_width_list.iter() ); + } + else + { + let shrink_factor : f32 = ( columns_max_width as f32 ) / ( columns_nowrap_width as f32 ); + + for ( icol, col_width ) in columns_width_list.iter().enumerate() + { + let col_limit_float = ( *col_width as f32 ) * shrink_factor; + let col_limit = col_limit_float.floor() as usize; + + let col_width_to_put = if icol == columns_width_list.len() - 1 + { + columns_max_width - column_widthes.iter().sum::() + } + else + { + col_limit.max(1) + }; + + column_widthes.push( col_width_to_put ); + } + } + + for ( irow, row ) in data.enumerate() + { + let mut wrapped_rows : Vec< Vec< Cow< 'data, str > > > = vec![]; + + for ( icol, col ) in row.iter().enumerate() + { + let col_limit = column_widthes[ icol ]; + let wrapped_col = string::lines_with_limit( col.0.as_ref(), col_limit ).map( Cow::from ).collect(); + wrapped_rows.push( wrapped_col ); + } + + let max_rows = wrapped_rows.iter().map( Vec::len ).max().unwrap_or(0); + + let mut transposed : Vec< Vec< WrappedCell< 'data > > > = Vec::new(); + + if max_rows == 0 + { + transposed.push( vec![] ); + } + + for i in 0..max_rows + { + let mut row_vec : Vec< WrappedCell< 'data > > = Vec::new(); + + for col_lines in &wrapped_rows + { + if col_lines.len() > i + { + let wrap_width = col_lines.iter().map( |c| c.len() ).max().unwrap_or(0); + row_vec.push( WrappedCell { wrap_width , content : col_lines[ i ].clone() } ); + } + else + { + row_vec.push( WrappedCell { wrap_width : 0, content : Cow::from( "" ) } ); + } + } + + transposed.push( row_vec ); + } + + if irow == 0 + { + first_row_height += transposed.len(); + } + + new_data.extend( transposed ); + } + + WrappedInputExtract + { + data: new_data, + first_row_height, + column_widthes + } + } + + /// Calculate width of the column without wrapping. + pub fn width_calculate< 'data > + ( + column : &'data Vec< ( Cow< 'data, str >, [ usize; 2 ] ) > + ) + -> usize + { + column.iter().map( |k| + { + string::lines( k.0.as_ref() ).map( |l| l.chars().count() ).max().unwrap_or( 0 ) + } ).max().unwrap_or( 0 ) + } + +} + +#[ 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 + { + }; + + #[ doc( inline ) ] + pub use private:: + { + text_wrap, + width_calculate, + }; + +} + +/// 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::output_format; +} + +/// Prelude to use essentials: `use my_module::prelude::*`. +#[ allow( unused_imports ) ] +pub mod prelude +{ + use super::*; +} diff --git a/module/core/format_tools/tests/inc/format_records_test.rs b/module/core/format_tools/tests/inc/format_records_test.rs index 72f23a5ff5..77b8de7364 100644 --- a/module/core/format_tools/tests/inc/format_records_test.rs +++ b/module/core/format_tools/tests/inc/format_records_test.rs @@ -316,4 +316,136 @@ fn filter_row_callback() // -// xxx : enable \ No newline at end of file +// xxx : enable + +#[ test ] +fn test_width_limiting() +{ + use the_module::string; + + for width in min_width()..max_width() + { + println!("width: {}", width); + + let test_objects = test_object::test_objects_gen(); + let as_table = AsTable::new( &test_objects ); + + let mut format = output_format::Records::default(); + format.max_width = width; + + let mut output = String::new(); + let printer = print::Printer::with_format( &format ); + let mut context = print::Context::new( &mut output, printer ); + + let got = the_module::TableFormatter::fmt( &as_table, &mut context ); + + assert!( got.is_ok() ); + + for line in string::lines( &output ) + { + if line.starts_with(" = ") + { + continue; + } + + if line.chars().count() > width + { + println!("{}", output); + } + + assert!( line.chars().count() <= width ); + } + } +} + +#[ test ] +fn test_error_on_unsatisfiable_limit() +{ + // 0 is a special value that signifies no limit. + for width in 1..( min_width() ) + { + println!( "width: {}", width ); + + let test_objects = test_object::test_objects_gen(); + let as_table = AsTable::new( &test_objects ); + + let mut format = output_format::Records::default(); + format.max_width = width; + + let mut output = String::new(); + let printer = print::Printer::with_format( &format ); + let mut context = print::Context::new( &mut output, printer ); + + let got = the_module::TableFormatter::fmt( &as_table, &mut context ); + + assert!( got.is_err() ); + } +} + +#[ test ] +fn test_table_not_grows() +{ + use the_module::string; + + let expected_width = max_width(); + + // The upper bound was chosen arbitrarily. + for width in ( expected_width + 1 )..500 + { + println!( "width: {}", width ); + + let test_objects = test_object::test_objects_gen(); + let as_table = AsTable::new( &test_objects ); + + let mut format = output_format::Records::default(); + format.max_width = width; + + let mut output = String::new(); + let printer = print::Printer::with_format( &format ); + let mut context = print::Context::new( &mut output, printer ); + + let got = the_module::TableFormatter::fmt( &as_table, &mut context ); + + assert!( got.is_ok() ); + println!("{}", output); + + for line in string::lines( &output ) + { + if line.starts_with(" = ") + { + continue; + } + + assert!( line.chars().count() <= expected_width ); + } + } +} + +/// Utility function for calculating minimum table width with `test_objects_gen()` with +/// the default table style. +fn min_width() -> usize +{ + let format = output_format::Records::default(); + format.min_width() +} + +/// Utility function for calculating default table width with `test_objects_gen()` with +/// the default table style with table width limit equals to 0. +fn max_width() -> usize +{ + use the_module::string; + + let test_objects = test_object::test_objects_gen(); + let as_table = AsTable::new( &test_objects ); + + let format = output_format::Records::default(); + + let mut output = String::new(); + let printer = print::Printer::with_format( &format ); + let mut context = print::Context::new( &mut output, printer ); + + let got = the_module::TableFormatter::fmt( &as_table, &mut context ); + assert!( got.is_ok() ); + + string::lines( &output ).map( |s| s.chars().count() ).max().unwrap_or(0) +} \ No newline at end of file diff --git a/module/core/format_tools/tests/inc/format_table_test.rs b/module/core/format_tools/tests/inc/format_table_test.rs index 4f4d6694d2..945696f572 100644 --- a/module/core/format_tools/tests/inc/format_table_test.rs +++ b/module/core/format_tools/tests/inc/format_table_test.rs @@ -8,9 +8,6 @@ use the_module:: filter, print, output_format, - print::{ InputExtract, RowDescriptor, ColDescriptor }, - TableOutputFormat, - filter::LineType }; use std:: @@ -347,4 +344,130 @@ fn no_subtract_with_overflow() let result = the_module::TableFormatter::fmt( &as_table, &mut context ); assert!( result.is_ok() ); +} + +#[ test ] +fn test_width_limiting() +{ + use the_module::string; + + for max_width in min_width()..max_width() + { + println!("max_width: {}", max_width); + + let test_objects = test_object::test_objects_gen(); + let as_table = AsTable::new( &test_objects ); + + let mut format = output_format::Table::default(); + format.max_width = max_width; + + let mut output = String::new(); + let printer = print::Printer::with_format( &format ); + let mut context = print::Context::new( &mut output, printer ); + + let got = the_module::TableFormatter::fmt( &as_table, &mut context ); + + assert!( got.is_ok() ); + + for line in string::lines( &output ) + { + assert_eq!( max_width, line.chars().count() ); + } + } +} + +#[ test ] +fn test_error_on_unsatisfiable_limit() +{ + // 0 is a special value that signifies no limit. Therefore, the lower bound is 1. + for max_width in 1..( min_width() ) + { + println!( "max_width: {}", max_width ); + + let test_objects = test_object::test_objects_gen(); + let as_table = AsTable::new( &test_objects ); + + let mut format = output_format::Table::default(); + format.max_width = max_width; + + let mut output = String::new(); + let printer = print::Printer::with_format( &format ); + let mut context = print::Context::new( &mut output, printer ); + + let got = the_module::TableFormatter::fmt( &as_table, &mut context ); + + assert!( got.is_err() ); + } +} + +#[ test ] +fn test_table_not_grows() +{ + use the_module::string; + + let expected_width = max_width(); + + // The upper bound was chosen arbitrarily. + for max_width in ( expected_width + 1 )..500 + { + println!( "max_width: {}", max_width ); + + let test_objects = test_object::test_objects_gen(); + let as_table = AsTable::new( &test_objects ); + + let mut format = output_format::Table::default(); + format.max_width = max_width; + + let mut output = String::new(); + let printer = print::Printer::with_format( &format ); + let mut context = print::Context::new( &mut output, printer ); + + let got = the_module::TableFormatter::fmt( &as_table, &mut context ); + + assert!( got.is_ok() ); + + for line in string::lines( &output ) + { + assert_eq!( expected_width, line.chars().count() ); + } + } +} + +/// Utility function for calculating minimum table width with `test_objects_gen()` with +/// the default table style. +fn min_width() -> usize +{ + use the_module::Fields; + + let format = output_format::Table::default(); + let test_objects = test_object::test_objects_gen(); + let col_count = test_objects[0].fields().count(); + + format.min_width( col_count ) +} + +/// Utility function for calculating default table width with `test_objects_gen()` with +/// the default table style without any maximum width. +fn max_width() -> usize +{ + use the_module::string; + + let test_objects = test_object::test_objects_gen(); + let as_table = AsTable::new( &test_objects ); + + let format = output_format::Table::default(); + + let mut output = String::new(); + let printer = print::Printer::with_format( &format ); + let mut context = print::Context::new( &mut output, printer ); + + let got = the_module::TableFormatter::fmt( &as_table, &mut context ); + assert!( got.is_ok() ); + + for line in string::lines( &output ) + { + return line.chars().count(); + } + + 0 } \ No newline at end of file diff --git a/module/core/format_tools/tests/tests.rs b/module/core/format_tools/tests/tests.rs index 4fca6dbc07..c8e636300b 100644 --- a/module/core/format_tools/tests/tests.rs +++ b/module/core/format_tools/tests/tests.rs @@ -1,6 +1,6 @@ //! Primary tests. -#![ feature( trace_macros ) ] +// #![ feature( trace_macros ) ] #![ allow( unused_imports ) ] use format_tools as the_module; From 3620512d0c405b9e9c94d3ea00814b6e3e3e6523 Mon Sep 17 00:00:00 2001 From: Ruslan Date: Fri, 22 Nov 2024 18:39:57 +0200 Subject: [PATCH 50/67] READY : `format_tools` - alternative API function (#1496) * Continue to work on * Improvements * Make test * Remove unnecessary fields * Fix from merge --- .../format_tools/src/format/output_format.rs | 33 ++++++ module/core/format_tools/src/format/print.rs | 112 ++++++++++-------- .../core/format_tools/tests/inc/table_test.rs | 59 +++++++++ 3 files changed, 156 insertions(+), 48 deletions(-) diff --git a/module/core/format_tools/src/format/output_format.rs b/module/core/format_tools/src/format/output_format.rs index 1b3e13c5b9..1bf58b75e6 100644 --- a/module/core/format_tools/src/format/output_format.rs +++ b/module/core/format_tools/src/format/output_format.rs @@ -32,6 +32,8 @@ mod private { + use std::borrow::Cow; + use crate::*; use print:: { @@ -78,6 +80,36 @@ mod private } } + /// Print table, which is constructed with vectors and `Cow`s, with the + /// specified output formatter. + /// + /// This function is useful when you do not want to use `AsTable`, or implement `Fields`, and + /// other traits, but you just have string slices in vectors. + /// + /// `rows` should not contain header of the table, it will be automatically added if `has_header` + /// is true. + pub fn vector_table_write< 'data, 'context > + ( + column_names : Vec< Cow< 'data, str > >, + has_header : bool, + rows : Vec< Vec< Cow< 'data, str > > >, + c : &mut Context< 'context >, + ) -> fmt::Result + { + InputExtract::extract_from_raw_table + ( + column_names, + has_header, + rows, + c.printer.filter_col, + c.printer.filter_row, + | x | + { + c.printer.output_format.extract_write( x, c ) + } + ) + } + } mod table; @@ -106,6 +138,7 @@ pub mod own #[ doc( inline ) ] pub use private:: { + vector_table_write, }; } diff --git a/module/core/format_tools/src/format/print.rs b/module/core/format_tools/src/format/print.rs index aada1e5425..b4c1b8d6f3 100644 --- a/module/core/format_tools/src/format/print.rs +++ b/module/core/format_tools/src/format/print.rs @@ -354,24 +354,71 @@ mod private } } + /// Extract input data from and collect it in a format consumable by output formatter. - pub fn extract< 't, 'context, Table, RowKey, Row, CellKey> + pub fn extract< 'context, Table, RowKey, Row, CellKey> ( - table : &'t Table, + table : &'data Table, filter_col : &'context ( dyn FilterCol + 'context ), filter_row : &'context ( dyn FilterRow + 'context ), callback : impl for< 'a2 > FnOnce( &'a2 InputExtract< 'a2 > ) -> fmt::Result, ) -> fmt::Result where - 'data : 't, - // 't : 'data, Table : TableRows< RowKey = RowKey, Row = Row, CellKey = CellKey >, Table : TableHeader< CellKey = CellKey >, RowKey : table::RowKey, Row : Cells< CellKey > + 'data, + Row : Cells< CellKey > + 'data, CellKey : table::CellKey + ?Sized + 'data, // CellRepr : table::CellRepr, + { + let rows = table.rows().map( | r | r.cells().map( | ( _, c ) | { + match c + { + Some( c ) => c, + None => Cow::from( "" ), + } + }).collect()).collect(); + + let has_header = table.header().is_some(); + + let column_names = match table.header() + { + Some( header ) => header.map( | ( k, _ ) | Cow::from( k.borrow() ) ).collect(), + + None => match table.rows().next() + { + Some( r ) => r.cells().map( | ( k, _ ) | Cow::from( k.borrow() ) ).collect(), + None => Vec::new() + } + }; + + Self::extract_from_raw_table + ( + column_names, + has_header, + rows, + filter_col, + filter_row, + callback, + ) + } + + /// Extract input data from a table that is constructed with vectors and `Cow`s and collect + /// it in a format consumable by output formatter. + /// + /// `rows` should not contain header of the table, it will be automatically added if `has_header` + /// is true. + pub fn extract_from_raw_table< 'context > + ( + column_names : Vec< Cow< 'data, str > >, + has_header : bool, + rows : Vec< Vec< Cow< 'data, str > > >, + filter_col : &'context ( dyn FilterCol + 'context ), + filter_row : &'context ( dyn FilterRow + 'context ), + callback : impl for< 'a2 > FnOnce( &'a2 InputExtract< 'a2 > ) -> fmt::Result, + ) -> fmt::Result { // let mcells = table.mcells(); let mut mcells_vis = [ 0 ; 2 ]; @@ -379,19 +426,17 @@ mod private let mut mchars = [ 0 ; 2 ]; // key width, index - let mut key_to_ikey : HashMap< &'t CellKey, usize > = HashMap::new(); + let mut key_to_ikey : HashMap< Cow< 'data, str >, usize > = HashMap::new(); let mut col_descriptors : Vec< ColDescriptor< '_ > > = Vec::with_capacity( mcells[ 0 ] ); let mut row_descriptors : Vec< RowDescriptor > = Vec::with_capacity( mcells[ 1 ] ); - let mut has_header = false; - let mut data : Vec< Vec< ( Cow< 't, str >, [ usize ; 2 ] ) > > = Vec::new(); - let rows = table.rows(); + let mut data : Vec< Vec< ( Cow< 'data, str >, [ usize ; 2 ] ) > > = Vec::new(); let mut irow : usize = 0; let filter_col_need_args = filter_col.need_args(); // let filter_row_need_args = filter_row.need_args(); - let mut row_add = | row_iter : &'_ mut dyn _IteratorTrait< Item = ( &'t CellKey, Cow< 't, str > ) >, typ : LineType | + let mut row_add = | row_data : Vec< Cow< 'data, str > >, typ : LineType | { irow = row_descriptors.len(); @@ -401,18 +446,21 @@ mod private let mut ncol = 0; let mut ncol_vis = 0; - let fields : Vec< ( Cow< 't, str >, [ usize ; 2 ] ) > = row_iter + let fields : Vec< ( Cow< 'data, str >, [ usize ; 2 ] ) > = row_data + .into_iter() + .enumerate() .filter_map ( - | ( key, val ) | + | ( ikey, val ) | { + let key = &column_names[ ikey ]; let l = col_descriptors.len(); ncol += 1; if filter_col_need_args { - if !filter_col.filter_col( key.borrow() ) + if !filter_col.filter_col( key.as_ref() ) { return None; } @@ -430,7 +478,7 @@ mod private let sz = string::size( &val ); key_to_ikey - .entry( key ) + .entry( key.clone() ) .and_modify( | icol | { let col = &mut col_descriptors[ *icol ]; @@ -469,18 +517,9 @@ mod private // process header first - if let Some( header ) = table.header() + if has_header { - rows.len().checked_add( 1 ).expect( "Table has too many rows" ); - // assert!( header.len() <= usize::MAX, "Header of a table has too many cells" ); - has_header = true; - - let mut row2 = header.map( | ( key, title ) | - { - ( key, Cow::Borrowed( title ) ) - }); - - row_add( &mut row2, LineType::Header ); + row_add( column_names.clone(), LineType::Header ); } // Collect rows @@ -489,30 +528,7 @@ mod private { // assert!( row.cells().len() <= usize::MAX, "Row of a table has too many cells" ); - let mut row2 = row - .cells() - .map - ( - | ( key, val ) | - { - - let val = match val - { - Some( val ) => - { - val - } - None => - { - Cow::Borrowed( "" ) - } - }; - - return ( key, val ); - } - ); - - row_add( &mut row2, LineType::Regular ); + row_add( row, LineType::Regular ); } // calculate size in chars diff --git a/module/core/format_tools/tests/inc/table_test.rs b/module/core/format_tools/tests/inc/table_test.rs index c5fd38a8e9..af57655085 100644 --- a/module/core/format_tools/tests/inc/table_test.rs +++ b/module/core/format_tools/tests/inc/table_test.rs @@ -298,3 +298,62 @@ fn iterator_over_strings() // assert!( got.contains( "│ 1627845583 │ [ │ │" ) ); } + +#[ test ] +fn test_vector_table() +{ + let column_names : Vec< Cow< 'static, str > > = vec![ + "id".into(), + "created_at".into(), + "file_ids".into(), + "tools".into(), + ]; + + let rows : Vec< Vec< Cow< 'static, str > > > = vec! + [ + vec! + [ + "1".into(), + "1627845583".into(), + "[ file1, file2 ]".into(), + "".into(), + ], + + vec! + [ + "2".into(), + "13".into(), + "[ file3, file4 ]".into(), + "[ tool1 ]".into(), + ], + ]; + + use the_module:: + { + output_format, + filter, + print, + }; + + let mut output = String::new(); + let mut context = print::Context::new( &mut output, Default::default() ); + + let res = output_format::vector_table_write + ( + column_names, + true, + rows, + &mut context, + ); + + assert!( res.is_ok() ); + + println!( "{}", output ); + + let exp = r#"│ id │ created_at │ file_ids │ tools │ +────────────────────────────────────────────────── +│ 1 │ 1627845583 │ [ file1, file2 ] │ │ +│ 2 │ 13 │ [ file3, file4 ] │ [ tool1 ] │"#; + + a_id!( output.as_str(), exp ); +} \ No newline at end of file From 9f6ec87dc9542200e4496b0004794925ffebb3f7 Mon Sep 17 00:00:00 2001 From: Ruslan Date: Fri, 22 Nov 2024 18:45:11 +0200 Subject: [PATCH 51/67] READY : Agents framework design draft (#1488) * Agents framework design draft * Update from the review * Add SQL example * Redesign sql.yaml * Fix from the call * Remove other examples * Update agents_design.md * Update agents_design.md --- module/move/assistant/design/agents_design.md | 37 +++++++++++++++++++ .../assistant/design/agents_examples/sql.yaml | 23 ++++++++++++ 2 files changed, 60 insertions(+) create mode 100644 module/move/assistant/design/agents_design.md create mode 100644 module/move/assistant/design/agents_examples/sql.yaml diff --git a/module/move/assistant/design/agents_design.md b/module/move/assistant/design/agents_design.md new file mode 100644 index 0000000000..a4f9901294 --- /dev/null +++ b/module/move/assistant/design/agents_design.md @@ -0,0 +1,37 @@ +# Agents + +## YAML description structure + +Please refer to `examples/` directory. + +## Paths + +- Used in node types, templates. +- Parts are delimited with `::`. +- Absolute path has a leading `::`. +- All paths (expect absolute) **are subject to absolutization**. Absolutization also depends on the context: in `next` fields paths are absolutized to `::nodes` dir, in templates - to `::output` and so on. + +## Execution + +- YAML file contains section about `nodes:`. +- Next node is encoded in `next:` field. +- Output of the nodes are stored in `::output` dir. + +## Builtin scenarios + +- `::scenario::entry` +- `::scenario::termination` + +## Node types + +- Input nodes: + - `trigger::stdin` + - `trigger::file` +- Processing nodes: + - `script` + - `agent::completion` +- Output nodes: + - `event::stdout` + - `event::file` + +Refer to examples in `examples/` to see fields of nodes. \ No newline at end of file diff --git a/module/move/assistant/design/agents_examples/sql.yaml b/module/move/assistant/design/agents_examples/sql.yaml new file mode 100644 index 0000000000..a465756357 --- /dev/null +++ b/module/move/assistant/design/agents_examples/sql.yaml @@ -0,0 +1,23 @@ + nodes: + - id: input + type: trigger::stdin + prompt: 'Your query: ' + next: node::translator + + - id: sql_generator_stage1 + type: agent::completion + system_message: 'Your task is to think about how to translate the user query in natural langauge in SQL langauge. Think step by steps.' + user_message: '{{input}}' + next: node::sql_generator_stage2 + + - id: sql_generator_stage2 + type: agent::completion + system_message: 'Your task to make an SQL code based on user query in natural language and the results of thinking on that query'. + user_message: '{{sql_generator_stage1}}' + agent_reuse: node::sql_generator_stage2 + next: node::output + + - id: output + type: event::stdout + output: '{{sql_generator_stage2}}' + next: scenario::termination \ No newline at end of file From d7628659f11d9f3cdc8f66f1411fc6e12b3caa27 Mon Sep 17 00:00:00 2001 From: Ruslan Date: Fri, 22 Nov 2024 19:47:46 +0200 Subject: [PATCH 52/67] READY : `format_tools` test for dyn. container of dyn. struct (#1497) * Preliminary * Working on * Add test and fix bug in `extract` * Fix comment * Fix bug --- .../core/format_tools/src/format/as_table.rs | 14 +++--- module/core/format_tools/src/format/print.rs | 27 +++++++++--- module/core/format_tools/src/format/table.rs | 22 ++++++++-- .../format_tools/tests/inc/collection_test.rs | 43 +++++++++++++++++++ 4 files changed, 88 insertions(+), 18 deletions(-) diff --git a/module/core/format_tools/src/format/as_table.rs b/module/core/format_tools/src/format/as_table.rs index 7409b42952..d269556525 100644 --- a/module/core/format_tools/src/format/as_table.rs +++ b/module/core/format_tools/src/format/as_table.rs @@ -32,7 +32,7 @@ mod private ) where RowKey : table::RowKey, - Row : Cells< CellKey>, + Row : Cells< CellKey >, CellKey : table::CellKey + ?Sized, // CellRepr : table::CellRepr ; @@ -41,7 +41,7 @@ mod private AsTable< 'table, Table, RowKey, Row, CellKey> where RowKey : table::RowKey, - Row : Cells< CellKey>, + Row : Cells< CellKey >, CellKey : table::CellKey + ?Sized, // CellRepr : table::CellRepr, { @@ -56,7 +56,7 @@ mod private for AsTable< 'table, Table, RowKey, Row, CellKey> where RowKey : table::RowKey, - Row : Cells< CellKey>, + Row : Cells< CellKey >, CellKey : table::CellKey + ?Sized, // CellRepr : table::CellRepr, { @@ -70,7 +70,7 @@ mod private for AsTable< 'table, Table, RowKey, Row, CellKey> where RowKey : table::RowKey, - Row : Cells< CellKey>, + Row : Cells< CellKey >, CellKey : table::CellKey + ?Sized, // CellRepr : table::CellRepr, { @@ -86,7 +86,7 @@ mod private for AsTable< 'table, Table, RowKey, Row, CellKey> where RowKey : table::RowKey, - Row : Cells< CellKey>, + Row : Cells< CellKey >, CellKey : table::CellKey + ?Sized, // CellRepr : table::CellRepr, { @@ -101,7 +101,7 @@ mod private where Table : fmt::Debug, RowKey : table::RowKey, - Row : Cells< CellKey>, + Row : Cells< CellKey >, CellKey : table::CellKey + ?Sized, // CellRepr : table::CellRepr, { @@ -146,7 +146,7 @@ mod private for AsTable< 'table, Table, RowKey, Row, CellKey> where RowKey : table::RowKey, - Row : Cells< CellKey>, + Row : Cells< CellKey >, CellKey : table::CellKey + ?Sized, // CellRepr : table::CellRepr, Self : Copy, diff --git a/module/core/format_tools/src/format/print.rs b/module/core/format_tools/src/format/print.rs index b4c1b8d6f3..e102eb5372 100644 --- a/module/core/format_tools/src/format/print.rs +++ b/module/core/format_tools/src/format/print.rs @@ -373,13 +373,26 @@ mod private CellKey : table::CellKey + ?Sized + 'data, // CellRepr : table::CellRepr, { - let rows = table.rows().map( | r | r.cells().map( | ( _, c ) | { - match c + let mut key_to_ikey : HashMap< Cow< 'data, str >, usize > = HashMap::new(); + let mut keys_count = 0; + + let rows = table.rows().map( | r | + { + let mut unsorted : Vec< ( usize, Cow< 'data, str > ) > = r.cells().map( | ( key, c ) | { - Some( c ) => c, - None => Cow::from( "" ), - } - }).collect()).collect(); + if !key_to_ikey.contains_key( key.borrow() ) + { + key_to_ikey.insert( key.borrow().into(), keys_count ); + keys_count += 1; + } + + ( key_to_ikey[ key.borrow() ], c.unwrap_or( Cow::from( "" ) ) ) + } ).collect(); + + unsorted.sort_by( | ( i1, _ ), ( i2, _ ) | i1.cmp(i2) ); + + unsorted.into_iter().map( | ( _, c ) | c).collect() + } ).collect(); let has_header = table.header().is_some(); @@ -535,7 +548,7 @@ mod private mchars[ 0 ] = col_descriptors.iter().fold( 0, | acc, col | acc + col.width ); mchars[ 1 ] = row_descriptors.iter().fold( 0, | acc, row | acc + if row.vis { row.height } else { 0 } ); - + let mut x = InputExtract::< '_ > { mcells, diff --git a/module/core/format_tools/src/format/table.rs b/module/core/format_tools/src/format/table.rs index 27e44ca0e5..1fab2ab744 100644 --- a/module/core/format_tools/src/format/table.rs +++ b/module/core/format_tools/src/format/table.rs @@ -12,7 +12,11 @@ mod private // fmt, borrow::Borrow, }; - use std::borrow::Cow; + use std:: + { + borrow::Cow, + collections::HashMap, + }; use reflect_tools:: { IteratorTrait, @@ -72,7 +76,7 @@ mod private // = - /// Marker trait to tag structures for whcih table trait deducing should be done from trait Fields, which is reflection. + /// Marker trait to tag structures for which table trait deducing should be done from trait Fields, which is reflection. pub trait TableWithFields {} // = @@ -92,6 +96,16 @@ mod private ; } + impl Cells< str > for HashMap< String, String > + { + fn cells< 'a, 'b >( &'a self ) -> impl IteratorTrait< Item = ( &'b str, Option< Cow< 'b, str > > ) > + where + 'a : 'b, + { + self.iter().map( | ( k, v ) | ( k.as_str(), Some( Cow::from( v ) ) ) ) + } + } + impl< Row, CellKey > Cells< CellKey > for Row where @@ -188,7 +202,7 @@ mod private > + 'k + 'v, RowKey : table::RowKey, - Row : TableWithFields + Cells< CellKey >, + Row : Cells< CellKey >, CellKey : table::CellKey + ?Sized, // CellRepr : table::CellRepr, { @@ -264,7 +278,7 @@ mod private where Self : TableRows< RowKey = RowKey, Row = Row, CellKey = CellKey >, RowKey : table::RowKey, - Row : TableWithFields + Cells< CellKey >, + Row : Cells< CellKey >, CellKey : table::CellKey + ?Sized, // CellRepr : table::CellRepr, { diff --git a/module/core/format_tools/tests/inc/collection_test.rs b/module/core/format_tools/tests/inc/collection_test.rs index 6b17ce0975..0d066004e2 100644 --- a/module/core/format_tools/tests/inc/collection_test.rs +++ b/module/core/format_tools/tests/inc/collection_test.rs @@ -401,3 +401,46 @@ fn llist_basic() } // qqq : xxx : implement for other containers + +#[ test ] +fn vec_of_hashmap() +{ + let data : Vec< HashMap< String, String > > = vec! + [ + { + let mut map = HashMap::new(); + map.insert( "id".to_string(), "1".to_string() ); + map.insert( "created_at".to_string(), "1627845583".to_string() ); + map + }, + { + let mut map = HashMap::new(); + map.insert( "id".to_string(), "2".to_string() ); + map.insert( "created_at".to_string(), "13".to_string() ); + map + }, + ]; + + use std::borrow::Cow; + + use the_module::TableFormatter; + + let _as_table : AsTable< '_, Vec< HashMap< String, String > >, &str, HashMap< String, String >, str> = AsTable::new( &data ); + let as_table = AsTable::new( &data ); + + let rows = TableRows::rows( &as_table ); + assert_eq!( rows.len(), 2 ); + + let mut output = String::new(); + let mut context = the_module::print::Context::new( &mut output, Default::default() ); + + let _got = the_module::TableFormatter::fmt( &as_table, &mut context ); + + let got = as_table.table_to_string(); + + println!("{}", got); + + assert!( got.contains( "│ id │ created_at │" ) || got.contains( "│ created_at │ id │" ) ); + assert!( got.contains( "│ 1 │ 1627845583 │" ) || got.contains( "│ 1627845583 │ 1 │" ) ); + assert!( got.contains( "│ 2 │ 13 │" ) || got.contains( "│ 13 │ 2 │" ) ); +} \ No newline at end of file From 039445d208559fe22c956859b4791bd3cb3fd374 Mon Sep 17 00:00:00 2001 From: Ruslan Date: Mon, 2 Dec 2024 15:13:56 +0200 Subject: [PATCH 53/67] Add maximum table width to assistants CLI (#1499) --- .../move/assistant/src/bin/list_resources.rs | 2 +- module/move/assistant/src/commands.rs | 8 ++- .../move/assistant/src/util/display_table.rs | 49 ++++++++++++++----- 3 files changed, 42 insertions(+), 17 deletions(-) diff --git a/module/move/assistant/src/bin/list_resources.rs b/module/move/assistant/src/bin/list_resources.rs index 5c3a4a85bb..d85d524ceb 100644 --- a/module/move/assistant/src/bin/list_resources.rs +++ b/module/move/assistant/src/bin/list_resources.rs @@ -19,7 +19,7 @@ use dotenv::dotenv; use assistant:: { - client, + client::client, Secret }; diff --git a/module/move/assistant/src/commands.rs b/module/move/assistant/src/commands.rs index 7f54ec320b..480b13d8d5 100644 --- a/module/move/assistant/src/commands.rs +++ b/module/move/assistant/src/commands.rs @@ -29,17 +29,15 @@ mod private OpenAi( openai::Command ), } - // const DEFAULT_MAX_TABLE_WIDTH: usize = 130; - // Commented out as not yet implemented in `format_tools`. + const DEFAULT_MAX_TABLE_WIDTH : usize = 130; /// Common collection of arguments for formatting tabular data. #[ derive( Debug, Parser ) ] pub struct TableConfig { /// Limit table widht. - // #[ arg( long, default_value_t = DEFAULT_MAX_TABLE_WIDTH ) ] - // pub max_table_width : usize, - // Commented out as not yet implemented in `format_tools`. + #[ arg( long, default_value_t = DEFAULT_MAX_TABLE_WIDTH ) ] + pub max_table_width : usize, /// Show tabular data as an ordinary table. #[ arg( long ) ] diff --git a/module/move/assistant/src/util/display_table.rs b/module/move/assistant/src/util/display_table.rs index edced5fbdd..c4e7ddcd28 100644 --- a/module/move/assistant/src/util/display_table.rs +++ b/module/move/assistant/src/util/display_table.rs @@ -28,19 +28,19 @@ mod private { if table_config.as_table { - display_table( data, f, &table_config.filter_columns ) + display_table( data, f, table_config ) } else if table_config.as_records { - display_records( data, f, &table_config.filter_columns ) + display_records( data, f, table_config ) } else if table_config.columns { - display_columns( data, f, &table_config.filter_columns ) + display_columns( data, f, table_config ) } else { - display_table( data, f, &table_config.filter_columns ) + display_table( data, f, table_config ) } } @@ -48,38 +48,65 @@ mod private ( data : &'a impl TableFormatter< 'a >, f : &mut fmt::Formatter< '_ >, - filter_columns : &'a Vec< String >, + table_config : &'a TableConfig, ) -> fmt::Result { - display_data( data, f, filter_columns, output_format::Table::default() ) + let mut format = output_format::Table::default(); + format.max_width = table_config.max_table_width; + + display_data + ( + data, + f, + format, + &table_config.filter_columns, + ) } fn display_records<'a> ( data : &'a impl TableFormatter< 'a >, f : &mut fmt::Formatter< '_ >, - filter_columns : &'a Vec< String >, + table_config : &'a TableConfig, ) -> fmt::Result { - display_data( data, f, filter_columns, output_format::Records::default() ) + let mut format = output_format::Records::default(); + format.max_width = table_config.max_table_width; + + display_data + ( + data, + f, + format, + &table_config.filter_columns, + ) } fn display_columns<'a> ( data : &'a impl TableFormatter< 'a >, f : &mut fmt::Formatter< '_ >, - filter_columns : &'a Vec< String >, + table_config : &'a TableConfig, ) -> fmt::Result { - display_data( data, f, filter_columns, output_format::Keys::default() ) + let mut format = output_format::Records::default(); + format.max_width = table_config.max_table_width; + + display_data + ( + data, + f, + format, + &table_config.filter_columns, + ) } fn display_data<'a> ( data : &'a impl TableFormatter< 'a >, f : &mut fmt::Formatter< '_ >, - filter_columns : &'a Vec< String >, format : impl TableOutputFormat, + filter_columns : &'a Vec< String >, ) -> fmt::Result { let mut printer = print::Printer::with_format( &format ); From 4330d03877c5c024b753244bbf55e17eae9bc3a8 Mon Sep 17 00:00:00 2001 From: Ruslan Date: Tue, 3 Dec 2024 09:25:05 +0200 Subject: [PATCH 54/67] NOT READY : Agents implementation (#1498) * `Path` blank implementation and tests * `Context` struct * Implement `ScenarioRaw` * Implement `Path` * Add `Context` tests * Start implementing `Context` * Implement `Context` * Start working on tests and formatters * Improve tests * Implement formatters --- module/move/assistant/Cargo.toml | 3 + .../assistant/design/agents_examples/sql.yaml | 2 +- module/move/assistant/src/agents.rs | 15 + module/move/assistant/src/agents/context.rs | 194 ++++++++++ module/move/assistant/src/agents/path.rs | 309 ++++++++++++++++ .../move/assistant/src/agents/scenario_raw.rs | 71 ++++ .../src/agents/scenario_raw_processors.rs | 13 + .../plantuml_formatter.rs | 76 ++++ .../scenario_raw_processors/yaml_formatter.rs | 26 ++ module/move/assistant/src/lib.rs | 1 + .../tests/inc/agents_tests/context_test.rs | 139 ++++++++ .../assistant/tests/inc/agents_tests/mod.rs | 8 + .../tests/inc/agents_tests/path_test.rs | 330 ++++++++++++++++++ .../scenario_raw_processors/mod.rs | 4 + .../plantuml_formatter_test.rs | 33 ++ .../yaml_formatter_test.rs | 33 ++ .../inc/agents_tests/scenario_raw_test.rs | 49 +++ .../tests/inc/agents_tests/test_scenarios.rs | 41 +++ module/move/assistant/tests/inc/mod.rs | 3 +- 19 files changed, 1348 insertions(+), 2 deletions(-) create mode 100644 module/move/assistant/src/agents.rs create mode 100644 module/move/assistant/src/agents/context.rs create mode 100644 module/move/assistant/src/agents/path.rs create mode 100644 module/move/assistant/src/agents/scenario_raw.rs create mode 100644 module/move/assistant/src/agents/scenario_raw_processors.rs create mode 100644 module/move/assistant/src/agents/scenario_raw_processors/plantuml_formatter.rs create mode 100644 module/move/assistant/src/agents/scenario_raw_processors/yaml_formatter.rs create mode 100644 module/move/assistant/tests/inc/agents_tests/context_test.rs create mode 100644 module/move/assistant/tests/inc/agents_tests/mod.rs create mode 100644 module/move/assistant/tests/inc/agents_tests/path_test.rs create mode 100644 module/move/assistant/tests/inc/agents_tests/scenario_raw_processors/mod.rs create mode 100644 module/move/assistant/tests/inc/agents_tests/scenario_raw_processors/plantuml_formatter_test.rs create mode 100644 module/move/assistant/tests/inc/agents_tests/scenario_raw_processors/yaml_formatter_test.rs create mode 100644 module/move/assistant/tests/inc/agents_tests/scenario_raw_test.rs create mode 100644 module/move/assistant/tests/inc/agents_tests/test_scenarios.rs diff --git a/module/move/assistant/Cargo.toml b/module/move/assistant/Cargo.toml index 64396a499c..1031eed817 100644 --- a/module/move/assistant/Cargo.toml +++ b/module/move/assistant/Cargo.toml @@ -56,6 +56,9 @@ serde = { version = "1.0.213", features = ["derive"] } serde_with = "3.11.0" error_tools = "0.17.0" derive_tools = { version = "0.32.0", features = ["full"] } +regex = { version = "1.10.3" } +itertools = "0.13.0" +serde_yaml = "0.9" [dev-dependencies] test_tools = { workspace = true } diff --git a/module/move/assistant/design/agents_examples/sql.yaml b/module/move/assistant/design/agents_examples/sql.yaml index a465756357..5149f07a29 100644 --- a/module/move/assistant/design/agents_examples/sql.yaml +++ b/module/move/assistant/design/agents_examples/sql.yaml @@ -1,4 +1,4 @@ - nodes: +nodes: - id: input type: trigger::stdin prompt: 'Your query: ' diff --git a/module/move/assistant/src/agents.rs b/module/move/assistant/src/agents.rs new file mode 100644 index 0000000000..c902864e0a --- /dev/null +++ b/module/move/assistant/src/agents.rs @@ -0,0 +1,15 @@ +//! +//! Main module for agents framework. +//! + +mod private {} + +crate::mod_interface! +{ + + layer path; + layer context; + layer scenario_raw; + layer scenario_raw_processors; + +} \ No newline at end of file diff --git a/module/move/assistant/src/agents/context.rs b/module/move/assistant/src/agents/context.rs new file mode 100644 index 0000000000..27969f0ef4 --- /dev/null +++ b/module/move/assistant/src/agents/context.rs @@ -0,0 +1,194 @@ +//! +//! Context representation. Can be used as compile-time context and as a runtime-context. +//! +//! Represents a simplistic "filesystem" with directories and terminal objects. +//! + +mod private +{ + use std::collections::HashMap; + + use crate::*; + use agents::path::Path; + + /// Simplistic in-memory "filesystem". Represents the root of the filesystem. + /// + /// `T` is the type of terminal object. + #[ derive( Debug, Default ) ] + pub struct Context< T > + { + root : ContextDir< T >, + } + + impl< T > Context< T > + { + /// Create an empty `Context`. + pub fn new() -> Self + { + Self + { + root : ContextDir::new() + } + } + + /// Add new entry to the directory. + /// + /// Returns `true` if entry was successfully added. + /// Returns `false` if there is already and entry with such name. + /// Old entry will not be overriden. + pub fn add( &mut self, name : impl Into< String >, entry : ContextEntry< T > ) -> bool + { + self.root.add( name, entry ) + } + + /// Get an entry by its name. Returns `None` is there is no such entry. + /// + /// `name` must be a valid path item. Refer to `path::PATH_ITEM_REGEX_STR` for syntax. + /// + /// This method is useful for quickly getting an entry only by its name. + /// For complex paths, where your object is located in several consecutives directories, + /// you can use `Path` type and use method `Context::get_by_path`. + pub fn get( &self, name : impl AsRef< str > ) -> Option< &ContextEntry< T > > + { + self.root.get( name ) + } + + /// Get an entry by its path. Returns `None` is there is no such entry. + /// + /// This function can accept absolute `Path`s as `Context` represents the root of the + /// filesystem. + pub fn get_by_path( &self, path : &Path ) -> Option< &ContextEntry< T > > + { + self.root.get_by_path( &path.remove_absolute() ) + } + } + + /// Represents a directory in `Context` with other directories and + /// terminal objects. + /// + /// `T` is the type of terminal object. + #[ derive( Debug, PartialEq, Clone, Default ) ] + pub struct ContextDir< T > + { + /// Internal map of entry names and entries data (a directory or a terminal object). + map : HashMap< String, ContextEntry< T > >, + } + + impl< T > ContextDir< T > + { + /// Create an empty `ContextDir`. + pub fn new() -> Self + { + Self + { + map : HashMap::new() + } + } + + /// Add new entry to the directory. + /// + /// Returns `true` if entry was successfully added. + /// Returns `false` if there is already and entry with such name. + /// Old entry will not be overriden. + pub fn add( &mut self, name : impl Into< String >, entry : ContextEntry< T > ) -> bool + { + let name = name.into(); + + if self.map.contains_key( name.as_str() ) + { + false + } + else + { + self.map.insert( name, entry ); + true + } + } + + /// Get an entry by its name. Returns `None` is there is no such entry. + /// + /// `name` must be a valid path item. Refer to `path::PATH_ITEM_REGEX_STR` for syntax. + /// + /// This method is useful for quickly getting an entry only by its name. + /// For complex paths, where your object is located in several consecutives directories, + /// you can use `Path` type and use method `ContextDir::get_by_path`. + pub fn get( &self, name : impl AsRef< str > ) -> Option< &ContextEntry< T > > + { + self.map.get( name.as_ref() ) + } + + /// Get an entry by its path. Returns `None` is there is no such entry. + /// + /// This function does not accept absolute `Path`, as `ContextDir` does not know + /// whether it is root or not. For absolute `Path`s use `Context::get_by_path`. + pub fn get_by_path( &self, path : &Path ) -> Option< &ContextEntry< T > > + { + let mut cur : Option< &ContextEntry< T > > = None; + + for component in path.components() + { + match cur + { + None => + { + cur = self.get( component ); + }, + + Some( entry ) => + { + match entry + { + ContextEntry::Terminal( _ ) => + { + return None; + }, + + ContextEntry::Dir( dir ) => + { + cur = dir.get( component ); + } + } + } + } + + if cur.is_none() + { + return None; + } + } + + cur + } + } + + /// Entry in `Context`: either a directory or a terminal object `T`. + /// + /// Notice, this struct does not store the name of the entry. + #[ derive( Debug, PartialEq, Clone ) ] + pub enum ContextEntry< T > + { + /// Directory in context. + Dir( ContextDir< T > ), + + /// Terminal object. + Terminal( T ), + } + + impl< T > Into< ContextEntry< T > > for ContextDir< T > + { + fn into( self ) -> ContextEntry< T > + { + ContextEntry::Dir( self ) + } + } +} + +crate::mod_interface! +{ + own use + { + Context, + ContextDir, + ContextEntry, + }; +} \ No newline at end of file diff --git a/module/move/assistant/src/agents/path.rs b/module/move/assistant/src/agents/path.rs new file mode 100644 index 0000000000..2959e94ea0 --- /dev/null +++ b/module/move/assistant/src/agents/path.rs @@ -0,0 +1,309 @@ +//! +//! Paths in agents graph. +//! + +mod private +{ + use std:: + { + io, + fmt, + ops::Deref, + sync::LazyLock, + }; + + use itertools::Itertools; + use regex::Regex; + + /// Path separator string. + pub const PATH_SEPARATOR : &str = "::"; + + /// Regular expression for `Path` items. Represented in `&str`. + /// It is not anchored to start and end of the string. + /// + /// If you want to match against this expression, use `PATH_ITEM_REGEX`. + pub const PATH_ITEM_REGEX_STR : &str = r"[a-zA-Z0-9_ -]+"; + + /// Regular expression for `Path` items. You can match whole `&str` with this type. + /// + /// To match whole `Path` in strings, use `PATH_REGEX`. + pub static PATH_ITEM_REGEX : LazyLock< Regex > = LazyLock::new( || + { + let regex = format! + ( + r"^{}$", + PATH_ITEM_REGEX_STR + ); + + Regex::new( ®ex ).unwrap() + }); + + /// Regular expression for `Path`. You can match whole `&str` with this type. + pub static PATH_REGEX : LazyLock< Regex > = LazyLock::new( || + { + let regex = format! + ( + r"^({sep})?({item}({sep}{item})*({sep})?)?$", + sep = PATH_SEPARATOR, + item = PATH_ITEM_REGEX_STR, + ); + + Regex::new( ®ex ).unwrap() + }); + + /// New type for paths in agents graph. Use `TryFrom` implementation + /// to create `Path`s. + /// + /// Paths resemble filesystem path, path separator is `::`. + /// Absolute path starts with `::`. + #[ derive( Debug, Clone, Eq, PartialEq, Hash ) ] + pub struct Path( String ); + + impl Path + { + /// Returns the parent directory, if it exists. + /// + /// Returns `None` if the `Path` terminates in a root or if it's the empty string. + #[ inline ] + pub fn parent( &self ) -> Option< Path > + { + find_parent( self.0.as_str() ) + .map( | s | Self( s.to_string() ) ) + } + + /// Returns whether the `Path` is relative (does not start with `::`). + pub fn is_relative( &self ) -> bool + { + !self.is_absolute() + } + + /// Returns whether the `Path` is absolute (starts with `::`). + pub fn is_absolute( &self ) -> bool + { + self.0.starts_with( PATH_SEPARATOR ) + } + + /// Turn an absolute `Path` into a relative one by removing leading `::`. + /// + /// If the `Path` is not absolute, a clone will be returned without any + /// changes. + pub fn remove_absolute( &self ) -> Path + { + if self.is_absolute() + { + Self( self.0.strip_prefix( PATH_SEPARATOR ).unwrap_or( "" ).to_string() ) + } + else + { + Self( self.0.clone() ) + } + } + + /// Creates an owned `Path` by joining a given path to `self`. + /// + /// Returns `Err(io::Error)` is the `path` is an absolute path. + #[ inline ] + pub fn join( &self, path : &Path ) -> Result< Self, io::Error > + { + if path.is_absolute() + { + Err( io::Error::from( io::ErrorKind::InvalidData ) ) + } + else + { + if self.0.ends_with( PATH_SEPARATOR ) + { + Ok( Self( format!( "{}{}", self.0, path.0 ) ) ) + } + else + { + Ok( Self( format!( "{}::{}", self.0, path.0 ) ) ) + } + } + } + + /// Checks if the `Path` starts with a given base path. + #[ inline ] + pub fn starts_with( &self, base : &Path ) -> bool + { + self.0.starts_with( base.0.as_str() ) + } + + /// Returns the inner `String`. + #[ inline( always ) ] + pub fn inner( self ) -> String + { + self.0 + } + + /// Creates a relative `Path` from an iterator over items that implement `AsRef`. + /// To create an absolute `Path`, use `from_iter_abs` method. + /// + /// Returns `Err(io::Error)` if the items are not valid `Path` items. + pub fn from_iter_rel< 'a >( iter : impl Iterator< Item = &'a str > ) -> Result< Self, io::Error > + { + iter.map( | path_element_str | + { + if PATH_ITEM_REGEX.is_match( path_element_str ) + { + Ok ( path_element_str ) + } + else + { + Err ( io::Error::from( io::ErrorKind::InvalidData ) ) + } + }) + .process_results( | mut item_iter | + { + Self( item_iter.join( PATH_SEPARATOR ) ) + }) + } + + /// Creates an absolute `Path` from an iterator over strings. + /// To create a relative `Path`, use `from_iter_rel` method. + /// + /// Returns `Err(io::Error)` if the items are not valid `Path` items. + pub fn from_iter_abs< 'a >( iter : impl Iterator< Item = &'a str > ) -> Result< Self, io::Error > + { + iter.map( | path_element_str | + { + if PATH_ITEM_REGEX.is_match( path_element_str ) + { + Ok ( path_element_str ) + } + else + { + Err ( io::Error::from( io::ErrorKind::InvalidData ) ) + } + }) + .process_results( | mut item_iter | + { + let mut res = item_iter.join( PATH_SEPARATOR ); + res.insert_str( 0, PATH_SEPARATOR ); + Self( res ) + }) + } + + /// Iterate over components of a `Path`. If the `Path` is absolute, then the first + /// element will be `::`. + pub fn components( &self ) -> impl Iterator< Item = &str > + { + self.0.split( PATH_SEPARATOR ).map( | c | + { + if c.is_empty() + { + PATH_SEPARATOR + } + else + { + c + } + }) + } + } + + /// Find parent of a `Path`. + /// + /// This method uses `&str` as an argument instead of `Path` + /// in order to be more general and handle trailing `::` case. + fn find_parent( s : &str ) -> Option< &str > + { + s.rfind( PATH_SEPARATOR ) + .map( | sep_pos | + { + if sep_pos == 0 + { + // We found root. We should not return string before `::`, + // as it will be empty. + Some( PATH_SEPARATOR ) + } + else if sep_pos == s.len() - PATH_SEPARATOR.len() + { + // We found trailing `::`. We should continue looking for last separator. + find_parent( &s[ .. sep_pos ] ) + } + else + { + Some( &s[ .. sep_pos ] ) + } + }) + .flatten() + } + + impl fmt::Display for Path + { + #[ inline ] + fn fmt( &self, f : &mut fmt::Formatter<'_> ) -> fmt::Result + { + write!( f, "{}", self.0 ) + } + } + + impl TryFrom< String > for Path + { + type Error = io::Error; + + fn try_from( src : String ) -> Result< Self, Self::Error > + { + if PATH_REGEX.is_match( src.as_str() ) + { + Ok( Self ( src ) ) + } + else + { + Err( io::Error::from( io::ErrorKind::InvalidData ) ) + } + } + } + + impl TryFrom< &str > for Path + { + type Error = io::Error; + + fn try_from( src : &str ) -> Result< Self, Self::Error > + { + if PATH_REGEX.is_match( src ) + { + Ok( Self ( src.to_string() ) ) + } + else + { + Err( io::Error::from( io::ErrorKind::InvalidData ) ) + } + } + } + + impl AsRef< str > for Path + { + #[ inline ] + fn as_ref( &self ) -> &str + { + self.0.as_ref() + } + } + + impl Into< String > for Path + { + #[ inline ] + fn into( self ) -> String + { + self.0 + } + } + + impl Deref for Path + { + type Target = str; + + #[ inline ] + fn deref( &self ) -> &Self::Target + { + &self.0 + } + } +} + +crate::mod_interface! +{ + own use Path; +} \ No newline at end of file diff --git a/module/move/assistant/src/agents/scenario_raw.rs b/module/move/assistant/src/agents/scenario_raw.rs new file mode 100644 index 0000000000..8aef1e2250 --- /dev/null +++ b/module/move/assistant/src/agents/scenario_raw.rs @@ -0,0 +1,71 @@ +//! +//! Raw scenario representation. Captures only the basic syntax of scenario file. +//! +//! For more detailed representation, use `ScenarioProcessed`. +//! + +mod private +{ + use std:: + { + io, + collections::HashMap, + }; + + use former::Former; + use serde:: + { + Serialize, + Deserialize, + }; + + /// Struct that represents user written scenarios. + /// + /// This is a raw form of a scenario, only the general structure is captured there. + /// For more detailed representation of scenarios, use `ScenarioProcessed` type. + #[ derive( Debug, Serialize, Deserialize, Former, PartialEq ) ] + pub struct ScenarioRaw + { + /// Nodes in the scenario. + pub nodes: Vec< NodeRaw >, + } + + impl ScenarioRaw + { + /// Read scenario file in YAML format. + pub fn read( reader : impl io::Read ) -> Result< Self, serde_yaml::Error > + { + serde_yaml::from_reader( reader ) + } + } + + /// Node representation in a scenario file. + /// + /// This is a raw form of a node, only the general structure is captured there. + /// For more detailed representation of scenarios, use `Node` type. + #[ derive( Debug, Serialize, Deserialize, Former, PartialEq ) ] + pub struct NodeRaw + { + /// ID of the node. Must be unique, will also identify node output. + pub id : String, + + /// Type of the node. Represented as a path. + pub r#type : String, + + /// Rest of the key-value pairs in the node that are specific to node types. + #[ serde( flatten ) ] + pub params : HashMap< String, String >, + + /// ID of the next node to execute. Represented as a path. + pub next : String, + } +} + +crate::mod_interface! +{ + own use + { + ScenarioRaw, + NodeRaw, + }; +} \ No newline at end of file diff --git a/module/move/assistant/src/agents/scenario_raw_processors.rs b/module/move/assistant/src/agents/scenario_raw_processors.rs new file mode 100644 index 0000000000..4e9ebb7798 --- /dev/null +++ b/module/move/assistant/src/agents/scenario_raw_processors.rs @@ -0,0 +1,13 @@ +//! +//! `ScenarioRaw` processors: functions that work with `ScenarioRaw`. +//! +//! Currently only formatters are implemented. +//! + +mod private {} + +crate::mod_interface! +{ + layer yaml_formatter; + layer plantuml_formatter; +} \ No newline at end of file diff --git a/module/move/assistant/src/agents/scenario_raw_processors/plantuml_formatter.rs b/module/move/assistant/src/agents/scenario_raw_processors/plantuml_formatter.rs new file mode 100644 index 0000000000..8f1114fe2d --- /dev/null +++ b/module/move/assistant/src/agents/scenario_raw_processors/plantuml_formatter.rs @@ -0,0 +1,76 @@ +//! +//! Format scenario in PlantUML diagram. +//! + +mod private +{ + use std::io; + + use crate::*; + use agents::scenario_raw::ScenarioRaw; + + /// Format scenario in PlantUML diagram. + pub fn plantuml_formatter + ( + scenario : &ScenarioRaw, + writer : &mut impl io::Write, + ) -> Result< (), io::Error > + { + writer.write( b"@startuml\n" )?; + + for node in &scenario.nodes + { + writer.write( b"json " )?; + writer.write( node.id.as_bytes() )?; + writer.write( b" {\n" )?; + + writer.write( b" \"type\": \"" )?; + writer.write( node.r#type.as_bytes() )?; + writer.write( b"\"" )?; + + if node.params.len() > 0 + { + writer.write( b"," )?; + } + + writer.write( b"\n" )?; + + for ( i, ( key, value ) ) in node.params.iter().enumerate() + { + writer.write( b" \"" )?; + writer.write( key.as_bytes() )?; + writer.write( b"\": \"" )?; + writer.write( value.as_bytes() )?; + writer.write( b"\"" )?; + + if i != node.params.len() - 1 + { + writer.write( b"," )?; + } + + writer.write( b"\n" )?; + } + + writer.write( b"}\n" )?; + } + + writer.write( b"json ::scenario::termination {\n" )?; + writer.write( b"}\n" )?; + + for node in &scenario.nodes + { + writer.write( node.id.as_bytes() )?; + writer.write( b" --> " )?; + writer.write( node.next.as_bytes() )?; + writer.write( b" : next\n" )?; + } + + writer.write( b"@enduml" )?; + Ok( () ) + } +} + +crate::mod_interface! +{ + own use plantuml_formatter; +} \ No newline at end of file diff --git a/module/move/assistant/src/agents/scenario_raw_processors/yaml_formatter.rs b/module/move/assistant/src/agents/scenario_raw_processors/yaml_formatter.rs new file mode 100644 index 0000000000..05d1bb5668 --- /dev/null +++ b/module/move/assistant/src/agents/scenario_raw_processors/yaml_formatter.rs @@ -0,0 +1,26 @@ +//! +//! Format scenario in YAML format (pretty-printing). +//! + +mod private +{ + use std::io; + + use crate::*; + use agents::scenario_raw::ScenarioRaw; + + /// Pretty-print `ScenarioRaw` in YAML format. + pub fn yaml_formatter + ( + scenario : &ScenarioRaw, + writer : &mut impl io::Write, + ) -> Result< (), serde_yaml::Error > + { + serde_yaml::to_writer( writer, scenario ) + } +} + +crate::mod_interface! +{ + own use yaml_formatter; +} \ No newline at end of file diff --git a/module/move/assistant/src/lib.rs b/module/move/assistant/src/lib.rs index 5a33e41692..4d21799cc5 100644 --- a/module/move/assistant/src/lib.rs +++ b/module/move/assistant/src/lib.rs @@ -33,6 +33,7 @@ crate::mod_interface! layer actions; layer secret; layer util; + layer agents; exposed use ::reflect_tools:: { diff --git a/module/move/assistant/tests/inc/agents_tests/context_test.rs b/module/move/assistant/tests/inc/agents_tests/context_test.rs new file mode 100644 index 0000000000..08b0461696 --- /dev/null +++ b/module/move/assistant/tests/inc/agents_tests/context_test.rs @@ -0,0 +1,139 @@ +use super::*; + +use the_module::agents:: +{ + path::Path, + context:: + { + ContextDir, + ContextEntry, + Context, + }, +}; + +#[ test ] +fn context_dir_add_terminal() +{ + let mut ctx : ContextDir< () > = ContextDir::new(); + let entry = ContextEntry::Terminal( () ); + let name = "test"; + + let res = ctx.add( name, entry.clone() ); + + assert!( res ); + assert_eq!( ctx.get( name ), Some( &entry ) ); +} + +#[ test ] +fn context_dir_add_dir() +{ + let mut ctx : ContextDir< () > = ContextDir::new(); + let entry : ContextEntry< () > = ContextDir::new().into(); + let name = "test"; + + let res = ctx.add( name, entry.clone() ); + + assert!( res ); + assert_eq!( ctx.get( name ), Some( &entry ) ); +} + +#[ test ] +fn context_dir_add_duplicate() +{ + let name = "test"; + let orig_entry = ContextEntry::Terminal( 1 ); + + let mut ctx : ContextDir< usize > = ContextDir::new(); + ctx.add( name, orig_entry.clone() ); + + let res = ctx.add( name, ContextEntry::Terminal( 2 ) ); + + assert!( !res ); + assert_eq!( ctx.get( name ), Some( &orig_entry ) ); +} + +#[ test ] +fn context_dir_get() +{ + let mut ctx : ContextDir< usize > = ContextDir::new(); + ctx.add( "test_1", ContextEntry::Terminal( 1 ) ); + ctx.add( "test_2", ContextEntry::Terminal( 2 ) ); + ctx.add( "test_3", ContextEntry::Terminal( 3 ) ); + + assert_eq!( ctx.get( "test_1" ), Some( &ContextEntry::Terminal( 1 ) ) ); + assert_eq!( ctx.get( "test_2" ), Some( &ContextEntry::Terminal( 2 ) ) ); + assert_eq!( ctx.get( "test_3" ), Some( &ContextEntry::Terminal( 3 ) ) ); +} + +#[ test ] +fn context_dir_get_non_existing() +{ + let ctx : ContextDir< () > = ContextDir::new(); + + let res = ctx.get( "test" ); + + assert!( res.is_none() ); +} + +#[ test ] +fn context_dir_get_by_path_relative() +{ + let value_1 = ContextEntry::Terminal( 1 ); + let value_2 = ContextEntry::Terminal( 2 ); + let value_3 = ContextEntry::Terminal( 3 ); + + let mut dir_1 : ContextDir< usize > = ContextDir::new(); + dir_1.add( "value_1", value_1.clone() ); + dir_1.add( "value_2", value_2.clone() ); + + let mut dir_3 : ContextDir< usize > = ContextDir::new(); + dir_3.add( "value_3", value_3.clone() ); + + let mut dir_2 : ContextDir< usize > = ContextDir::new(); + dir_2.add( "dir_3", dir_3.into() ); + + let mut ctx : ContextDir< usize > = ContextDir::new(); + ctx.add( "dir_1", dir_1.into() ); + ctx.add( "dir_2", dir_2.into() ); + + let got_value_1 = ctx.get_by_path( &Path::try_from( "dir_1::value_1" ).unwrap() ); + let got_value_2 = ctx.get_by_path( &Path::try_from( "dir_1::value_2" ).unwrap() ); + let got_value_3 = ctx.get_by_path( &Path::try_from( "dir_2::dir_3::value_3" ).unwrap() ); + + assert_eq!( got_value_1, Some( &value_1 ) ); + assert_eq!( got_value_2, Some( &value_2 ) ); + assert_eq!( got_value_3, Some( &value_3 ) ); +} + +#[ test ] +fn context_dir_get_by_path_absolute() +{ + let mut ctx : ContextDir< () > = ContextDir::new(); + ctx.add( "test", ContextEntry::Terminal( () ) ); + + let res = ctx.get_by_path( &&Path::try_from( "::test" ).unwrap() ); + + assert!( res.is_none() ); +} + +#[ test ] +fn context_dir_get_by_path_non_existing() +{ + let ctx : ContextDir< () > = ContextDir::new(); + + let res = ctx.get_by_path( &Path::try_from( "test" ).unwrap() ); + + assert!( res.is_none() ); +} + +#[ test ] +fn context_get_by_path_absolute() +{ + let mut ctx : Context< () > = Context::new(); + let entry = ContextEntry::Terminal( () ); + ctx.add( "test", entry.clone() ); + + let res = ctx.get_by_path( &Path::try_from( "::test" ).unwrap() ); + + assert_eq!( res, Some( &entry ) ); +} \ No newline at end of file diff --git a/module/move/assistant/tests/inc/agents_tests/mod.rs b/module/move/assistant/tests/inc/agents_tests/mod.rs new file mode 100644 index 0000000000..6c94bd4a2e --- /dev/null +++ b/module/move/assistant/tests/inc/agents_tests/mod.rs @@ -0,0 +1,8 @@ +use super::*; + +mod test_scenarios; + +mod path_test; +mod context_test; +mod scenario_raw_test; +mod scenario_raw_processors; \ No newline at end of file diff --git a/module/move/assistant/tests/inc/agents_tests/path_test.rs b/module/move/assistant/tests/inc/agents_tests/path_test.rs new file mode 100644 index 0000000000..78e4132502 --- /dev/null +++ b/module/move/assistant/tests/inc/agents_tests/path_test.rs @@ -0,0 +1,330 @@ +use super::*; + +use the_module::agents::path::Path; + +#[ test ] +fn path_create_right() +{ + let path_str = "agent::completion"; + + let path = Path::try_from( path_str ); + + assert!( path.is_ok() ); + assert_eq! ( path.unwrap().inner(), path_str ); +} + +#[ test ] +fn path_create_wrong() +{ + let path = Path::try_from( "agent:completion" ); + assert!( path.is_err() ); +} + +#[ test ] +fn path_create_absolute() +{ + let path_str = "::agent::completion"; + + let path = Path::try_from( path_str ); + + assert!( path.is_ok() ); + assert_eq! ( path.unwrap().inner(), path_str ); +} + +#[ test ] +fn path_create_trailing() +{ + let path_str = "agent::completion::"; + + let path = Path::try_from( path_str ); + + assert!( path.is_ok() ); + assert_eq! ( path.unwrap().inner(), path_str ); +} + +#[ test ] +fn path_some_parent_relative() +{ + let path_str = "agent::completion"; + let path = Path::try_from( path_str ).unwrap(); + + let path_parent = path.parent(); + + assert!( path_parent.is_some() ); + assert_eq!( path_parent.unwrap().inner(), "agent" ); +} + +#[ test ] +fn path_some_parent_relative_trailing() +{ + let path_str = "agent::completion::"; + let path = Path::try_from( path_str ).unwrap(); + + let path_parent = path.parent(); + + assert!( path_parent.is_some() ); + assert_eq!( path_parent.unwrap().inner(), "agent" ); +} + +#[ test ] +fn path_some_parent_absolute() +{ + let path_str = "::agent"; + let path = Path::try_from( path_str ).unwrap(); + + let path_parent = path.parent(); + + assert!( path_parent.is_some() ); + assert_eq!( path_parent.unwrap().inner(), "::" ); +} + +#[ test ] +fn path_some_parent_absolute_trailing() +{ + let path_str = "::agent::"; + let path = Path::try_from( path_str ).unwrap(); + + let path_parent = path.parent(); + + assert!( path_parent.is_some() ); + assert_eq!( path_parent.unwrap().inner(), "::" ); +} + +#[ test ] +fn path_none_parent() +{ + let path_str = "agent"; + let path = Path::try_from( path_str ).unwrap(); + + let path_parent = path.parent(); + + assert!( path_parent.is_none() ); +} + +#[ test ] +fn path_is_relative() +{ + let path_str = "agent"; + let path = Path::try_from( path_str ).unwrap(); + + let is_relative = path.is_relative(); + let is_absolute = path.is_absolute(); + + assert!( is_relative ); + assert!( !is_absolute ); +} + +#[ test ] +fn path_is_absolute() +{ + let path_str = "::agent"; + let path = Path::try_from( path_str ).unwrap(); + + let is_relative = path.is_relative(); + let is_absolute = path.is_absolute(); + + assert!( !is_relative ); + assert!( is_absolute ); +} + +#[ test ] +fn path_join_relative() +{ + let orig_path = Path::try_from( "agent" ).unwrap(); + let append = Path::try_from( "completion" ).unwrap(); + + let combined = orig_path.join( &append ); + + assert!( combined.is_ok() ); + assert_eq!( combined.unwrap().inner(), "agent::completion" ); +} + +#[ test ] +fn path_join_absolute() +{ + let orig_path = Path::try_from( "agent" ).unwrap(); + let append = Path::try_from( "::completion" ).unwrap(); + + let combined = orig_path.join( &append ); + + assert!( combined.is_err() ); +} + +#[ test ] +fn path_join_root() +{ + let orig_path = Path::try_from( "::" ).unwrap(); + let append = Path::try_from( "agent" ).unwrap(); + + let combined = orig_path.join( &append ); + + assert!( combined.is_ok() ); + assert_eq!( combined.unwrap().inner(), "::agent" ); +} + +#[ test ] +fn path_join_trailing() +{ + let orig_path = Path::try_from( "agents::" ).unwrap(); + let append = Path::try_from( "completion" ).unwrap(); + + let combined = orig_path.join( &append ); + + assert!( combined.is_ok() ); + assert_eq!( combined.unwrap().inner(), "agents::completion" ); +} + +#[ test ] +fn path_starts_with_abs_abs() +{ + let a = Path::try_from( "::agent::completion" ).unwrap(); + let b = Path::try_from( "::agent" ).unwrap(); + + let starts_with = a.starts_with( &b ); + + assert!( starts_with ); +} + +#[ test ] +fn path_starts_with_abs_rel() +{ + let a = Path::try_from( "::agent::completion" ).unwrap(); + let b = Path::try_from( "agent" ).unwrap(); + + let starts_with = a.starts_with( &b ); + + assert!( !starts_with ); +} + +#[ test ] +fn path_starts_with_rel_abs() +{ + let a = Path::try_from( "agent" ).unwrap(); + let b = Path::try_from( "::agent::completion" ).unwrap(); + + let starts_with = a.starts_with( &b ); + + assert!( !starts_with ); +} + +#[ test ] +fn path_starts_with_rel_rel() +{ + let a = Path::try_from( "agent::completion" ).unwrap(); + let b = Path::try_from( "agent" ).unwrap(); + + let starts_with = a.starts_with( &b ); + + assert!( starts_with ); +} + +#[ test ] +fn path_not_starts_with_abs_abs() +{ + let a = Path::try_from( "::agent::completion" ).unwrap(); + let b = Path::try_from( "::output" ).unwrap(); + + let starts_with = a.starts_with( &b ); + + assert!( !starts_with ); +} + +#[ test ] +fn path_not_starts_with_rel_rel() +{ + let a = Path::try_from( "agent::completion" ).unwrap(); + let b = Path::try_from( "output" ).unwrap(); + + let starts_with = a.starts_with( &b ); + + assert!( !starts_with ); +} + +#[ test ] +fn path_inner() +{ + let path_str = "::agent::completion"; + let path = Path::try_from( path_str ).unwrap(); + + let inner = path.inner(); + + assert_eq!( inner, path_str ); +} + +#[ test ] +fn path_from_iter_right() +{ + let expected = "agents::completion"; + let elements = vec![ "agents", "completion" ]; + + let path = Path::from_iter_rel( elements.into_iter() ); + + assert!( path.is_ok() ); + let path = path.unwrap(); + assert!( path.is_relative() ); + assert_eq!( path.inner(), expected ); +} + +#[ test ] +fn path_from_iter_wrong_item() +{ + let elements = vec![ "agents:", "completion" ]; + + let path = Path::from_iter_rel( elements.into_iter() ); + + assert!( path.is_err() ); +} + +#[ test ] +fn path_from_iter_wrong_separator() +{ + let elements = vec![ "agents", "::", "completion" ]; + + let path = Path::from_iter_rel( elements.into_iter() ); + + assert!( path.is_err() ); +} + +#[ test ] +fn path_from_iter_abs() +{ + let expected = "::agents::completion"; + let elements = vec![ "agents", "completion" ]; + + let path = Path::from_iter_abs( elements.into_iter() ); + + assert!( path.is_ok() ); + let path = path.unwrap(); + assert!( path.is_absolute() ); + assert_eq!( path.inner(), expected ); +} + +#[ test ] +fn path_remove_absolute() +{ + let path = Path::try_from( "::agents::completion" ).unwrap(); + + let got_path = path.remove_absolute(); + + assert_eq!( got_path.inner(), "agents::completion" ); +} + +#[ test ] +fn path_remove_absolute_from_rel() +{ + let path = Path::try_from( "agents::completion" ).unwrap(); + + let got_path = path.remove_absolute(); + + assert_eq!( got_path.inner(), "agents::completion" ); +} + +#[ test ] +fn path_components() +{ + let path = Path::try_from( "::agents::completion" ).unwrap(); + + let components : Vec< &str > = path.components().collect(); + + assert_eq!( components, vec![ "::", "agents", "completion" ] ); +} \ No newline at end of file diff --git a/module/move/assistant/tests/inc/agents_tests/scenario_raw_processors/mod.rs b/module/move/assistant/tests/inc/agents_tests/scenario_raw_processors/mod.rs new file mode 100644 index 0000000000..bbaccfe254 --- /dev/null +++ b/module/move/assistant/tests/inc/agents_tests/scenario_raw_processors/mod.rs @@ -0,0 +1,4 @@ +use super::*; + +mod plantuml_formatter_test; +mod yaml_formatter_test; \ No newline at end of file diff --git a/module/move/assistant/tests/inc/agents_tests/scenario_raw_processors/plantuml_formatter_test.rs b/module/move/assistant/tests/inc/agents_tests/scenario_raw_processors/plantuml_formatter_test.rs new file mode 100644 index 0000000000..44d5cf86b7 --- /dev/null +++ b/module/move/assistant/tests/inc/agents_tests/scenario_raw_processors/plantuml_formatter_test.rs @@ -0,0 +1,33 @@ +use super::*; + +use the_module::agents::scenario_raw_processors::plantuml_formatter::plantuml_formatter; + +use test_scenarios::gen_test_scenario_raw; + + +#[ test ] +fn plantuml_formatter_test() +{ + let expected_plantuml = r#"@startuml +json node_1 { + "type": "agents::completion", + "model": "gpt-4o-mini" +} +json node_2 { + "type": "agents::classify", + "model": "gpt-4o" +} +json ::scenario::termination { +} +node_1 --> node_2 : next +node_2 --> ::scenario::termination : next +@enduml"#; + + let scenario_raw = gen_test_scenario_raw(); + + let mut buffer = Vec::new(); + let result = plantuml_formatter( &scenario_raw, &mut buffer ); + + assert!( result.is_ok() ); + assert_eq!( String::from_utf8( buffer ).unwrap(), expected_plantuml ); +} diff --git a/module/move/assistant/tests/inc/agents_tests/scenario_raw_processors/yaml_formatter_test.rs b/module/move/assistant/tests/inc/agents_tests/scenario_raw_processors/yaml_formatter_test.rs new file mode 100644 index 0000000000..fd64cbacec --- /dev/null +++ b/module/move/assistant/tests/inc/agents_tests/scenario_raw_processors/yaml_formatter_test.rs @@ -0,0 +1,33 @@ +use super::*; + +use the_module::agents::scenario_raw_processors::yaml_formatter::yaml_formatter; + +use test_scenarios::gen_test_scenario_raw; + +#[ test ] +fn yaml_formatter_test() +{ + let expected_yaml = r#"nodes: +- id: node_1 + type: agents::completion + model: gpt-4o-mini + next: node_2 +- id: node_2 + type: agents::classify + model: gpt-4o + next: ::scenario::termination"#; + + let scenario_raw = gen_test_scenario_raw(); + + let mut buffer = Vec::new(); + let result = yaml_formatter( &scenario_raw, &mut buffer ); + assert!( result.is_ok() ); + + let result = String::from_utf8( buffer ); + assert!( result.is_ok() ); + + let result = result.unwrap(); + println!( "{}", result ); + + assert_eq!( result.trim(), expected_yaml.trim() ); +} diff --git a/module/move/assistant/tests/inc/agents_tests/scenario_raw_test.rs b/module/move/assistant/tests/inc/agents_tests/scenario_raw_test.rs new file mode 100644 index 0000000000..2f8acc60fe --- /dev/null +++ b/module/move/assistant/tests/inc/agents_tests/scenario_raw_test.rs @@ -0,0 +1,49 @@ +use super::*; + +use the_module::agents::scenario_raw::ScenarioRaw; + +use test_scenarios::gen_test_scenario_raw; + +#[ test ] +fn scenario_read() +{ + let scenario_text = r#" + nodes: + - id: node_1 + type: agents::completion + model: gpt-4o-mini + next: node_2 + + - id: node_2 + type: agents::classify + model: gpt-4o + next: ::scenario::termination + "#; + + let expected_scenario_raw = gen_test_scenario_raw(); + + let scenario_raw = ScenarioRaw::read( scenario_text.as_bytes() ); + + assert!( scenario_raw.is_ok() ); + + let scenario_raw = scenario_raw.unwrap(); + assert_eq!( scenario_raw, expected_scenario_raw ); +} + +#[ test ] +fn scenario_wrong() +{ + let scenario_text = r#" + nodes: + - completion: + model: + company: openai + name: gpt-4o + depends_on: + node_2 + "#; + + let scenario_raw = ScenarioRaw::read( scenario_text.as_bytes() ); + + assert!( scenario_raw.is_err() ); +} \ No newline at end of file diff --git a/module/move/assistant/tests/inc/agents_tests/test_scenarios.rs b/module/move/assistant/tests/inc/agents_tests/test_scenarios.rs new file mode 100644 index 0000000000..84e217b230 --- /dev/null +++ b/module/move/assistant/tests/inc/agents_tests/test_scenarios.rs @@ -0,0 +1,41 @@ +use super::*; + +use the_module::agents::scenario_raw:: +{ + ScenarioRaw, + NodeRaw, +}; + +pub fn gen_test_scenario_raw() -> ScenarioRaw +{ + ScenarioRaw::former() + .nodes( vec! + [ + NodeRaw::former() + .id( "node_1".to_string() ) + .r#type( "agents::completion".to_string() ) + .params( + { + let mut map : HashMap< String, String > = HashMap::new(); + map.insert( "model".into(), "gpt-4o-mini".into() ); + map + } + ) + .next( "node_2".to_string() ) + .form(), + + NodeRaw::former() + .id( "node_2".to_string() ) + .r#type( "agents::classify".to_string() ) + .params( + { + let mut map : HashMap< String, String > = HashMap::new(); + map.insert( "model".into(), "gpt-4o".into() ); + map + } + ) + .next( "::scenario::termination".to_string() ) + .form(), + ] ) + .form() +} \ No newline at end of file diff --git a/module/move/assistant/tests/inc/mod.rs b/module/move/assistant/tests/inc/mod.rs index 0706620c6e..abf35e2f97 100644 --- a/module/move/assistant/tests/inc/mod.rs +++ b/module/move/assistant/tests/inc/mod.rs @@ -1,6 +1,7 @@ #[ allow( unused_imports ) ] use super::*; -mod basic_test; +mod agents_tests; +mod basic_test; mod experiment; From 2e7e3de9d5cde69aa1d4610f1035be97c9ff439a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A1=D0=B5=D0=B2=D0=B0=20=D0=91=D0=B0=D0=BA=D1=83=D1=82?= =?UTF-8?q?=D0=BE=D0=B2?= <122987843+sevabakutov@users.noreply.github.com> Date: Tue, 3 Dec 2024 17:52:29 +0100 Subject: [PATCH 55/67] READY : Test task Google Sheets CLI (#1495) * Test task Google Sheets CLI * format_tools, mod_interface and usage information added * formating fixed * formating fixed * formating fixed * tests added, debug dir removed, output fixed * info fixed * cells command were added * format fixed * some code was commented --------- Co-authored-by: vsevolod Co-authored-by: Vsevolod --- module/core/process_tools/src/process.rs | 34 ++-- module/move/gspread/.key/readme.md | 44 +++++ module/move/gspread/Cargo.toml | 47 ++++++ module/move/gspread/readme.md | 1 + module/move/gspread/src/actions.rs | 16 ++ module/move/gspread/src/actions/gspread.rs | 57 +++++++ .../gspread/src/actions/gspread_cell_get.rs | 42 +++++ .../gspread/src/actions/gspread_cell_set.rs | 50 ++++++ .../gspread/src/actions/gspread_cells_set.rs | 132 +++++++++++++++ .../gspread/src/actions/gspread_get_header.rs | 58 +++++++ .../gspread/src/actions/gspread_get_rows.rs | 38 +++++ module/move/gspread/src/bin/main.rs | 33 ++++ module/move/gspread/src/client.rs | 79 +++++++++ module/move/gspread/src/commands.rs | 52 ++++++ module/move/gspread/src/commands/gspread.rs | 104 ++++++++++++ .../move/gspread/src/commands/gspread_cell.rs | 106 ++++++++++++ .../gspread/src/commands/gspread_cells.rs | 72 ++++++++ .../gspread/src/commands/gspread_header.rs | 77 +++++++++ .../move/gspread/src/commands/gspread_rows.rs | 77 +++++++++ module/move/gspread/src/debug.rs | 20 +++ module/move/gspread/src/debug/row_wrapper.rs | 59 +++++++ module/move/gspread/src/lib.rs | 41 +++++ module/move/gspread/src/secret.rs | 159 ++++++++++++++++++ module/move/gspread/src/util.rs | 6 + module/move/gspread/src/util/display_table.rs | 55 ++++++ module/move/gspread/tests/inc/cell_tests.rs | 99 +++++++++++ module/move/gspread/tests/inc/cells_tests.rs | 79 +++++++++ module/move/gspread/tests/inc/header_tests.rs | 90 ++++++++++ module/move/gspread/tests/inc/mod.rs | 14 ++ module/move/gspread/tests/inc/rows_tests.rs | 93 ++++++++++ module/move/gspread/tests/smoke_test.rs | 12 ++ module/move/gspread/tests/tests.rs | 9 + module/move/willbe/src/entity/files/either.rs | 2 +- 33 files changed, 1839 insertions(+), 18 deletions(-) create mode 100644 module/move/gspread/.key/readme.md create mode 100644 module/move/gspread/Cargo.toml create mode 100644 module/move/gspread/readme.md create mode 100644 module/move/gspread/src/actions.rs create mode 100644 module/move/gspread/src/actions/gspread.rs create mode 100644 module/move/gspread/src/actions/gspread_cell_get.rs create mode 100644 module/move/gspread/src/actions/gspread_cell_set.rs create mode 100644 module/move/gspread/src/actions/gspread_cells_set.rs create mode 100644 module/move/gspread/src/actions/gspread_get_header.rs create mode 100644 module/move/gspread/src/actions/gspread_get_rows.rs create mode 100644 module/move/gspread/src/bin/main.rs create mode 100644 module/move/gspread/src/client.rs create mode 100644 module/move/gspread/src/commands.rs create mode 100644 module/move/gspread/src/commands/gspread.rs create mode 100644 module/move/gspread/src/commands/gspread_cell.rs create mode 100644 module/move/gspread/src/commands/gspread_cells.rs create mode 100644 module/move/gspread/src/commands/gspread_header.rs create mode 100644 module/move/gspread/src/commands/gspread_rows.rs create mode 100644 module/move/gspread/src/debug.rs create mode 100644 module/move/gspread/src/debug/row_wrapper.rs create mode 100644 module/move/gspread/src/lib.rs create mode 100644 module/move/gspread/src/secret.rs create mode 100644 module/move/gspread/src/util.rs create mode 100644 module/move/gspread/src/util/display_table.rs create mode 100644 module/move/gspread/tests/inc/cell_tests.rs create mode 100644 module/move/gspread/tests/inc/cells_tests.rs create mode 100644 module/move/gspread/tests/inc/header_tests.rs create mode 100644 module/move/gspread/tests/inc/mod.rs create mode 100644 module/move/gspread/tests/inc/rows_tests.rs create mode 100644 module/move/gspread/tests/smoke_test.rs create mode 100644 module/move/gspread/tests/tests.rs diff --git a/module/core/process_tools/src/process.rs b/module/core/process_tools/src/process.rs index d58e95455a..4277115676 100644 --- a/module/core/process_tools/src/process.rs +++ b/module/core/process_tools/src/process.rs @@ -54,7 +54,7 @@ mod private // let ( program, args ) = // if cfg!( target_os = "windows" ) // { - // ( "cmd", [ "/C", exec_path ] ) + // ( "gspread", [ "/C", exec_path ] ) // } // else // { @@ -234,22 +234,22 @@ mod private /// # Returns: /// A `Result` containing a `Report` on success, which includes the command's output, /// or an error if the command fails to execute or complete. - pub fn run_with_shell( self, exec_path : &str, ) -> Result< Report, Report > - { - let ( program, args ) = - if cfg!( target_os = "windows" ) - { - ( "cmd", [ "/C", exec_path ] ) - } - else - { - ( "sh", [ "-c", exec_path ] ) - }; - self - .args( args.into_iter().map( OsString::from ).collect::< Vec< _ > >() ) - .bin_path( program ) - .run() - } + // pub fn run_with_shell( self, exec_path : &str, ) -> Result< Report, Report > + // { + // let ( program, args ) = + // if cfg!( target_os = "windows" ) + // { + // ( "gspread", [ "/C", exec_path ] ) + // } + // else + // { + // ( "sh", [ "-c", exec_path ] ) + // }; + // self + // .args( args.into_iter().map( OsString::from ).collect::< Vec< _ > >() ) + // .bin_path( program ) + // .run() + // } } /// Process command output. diff --git a/module/move/gspread/.key/readme.md b/module/move/gspread/.key/readme.md new file mode 100644 index 0000000000..cfd1cc23d2 --- /dev/null +++ b/module/move/gspread/.key/readme.md @@ -0,0 +1,44 @@ +# Getting API Keys for OAuth Authentication + +Follow these steps to create and configure your OAuth credentials for using Google APIs. + +## 1. Create API Credentials + +1. Go to the [Google API Console](https://console.developers.google.com/). +2. From the projects list, select an existing project or create a new one. +3. In the left side menu, select **APIs & Services**. +4. On the left menu, click **Credentials**. +5. Click **Create Credentials** and select **OAuth client ID**. +6. In the **Application type** section, select **Desktop app**. +7. Provide an appropriate name for your client ID (e.g., "MyApp OAuth Client"). +8. Click **Create**. + +Once the credential is created, you will receive a **Client ID** and **Client Secret**. These are required for accessing the API. + +## 2. Store Your Credentials + +Save the **Client ID** and **Client Secret** in a `.sh` file (e.g., `-env.sh`) within a `key` directory. The file should look like this: + +```bash +CLIENT_ID=YOUR_CLIENT_ID +CLIENT_SECRET=YOUR_SECRET_KEY +``` + +Set also these keys with following values: +```bash +AUTH_URI=https://accounts.google.com/o/oauth2/auth +TOKEN_URI=https://oauth2.googleapis.com/token +``` +If you get problems, most likely you will need to change **AUTH_URI** or **TOKEN_URI** to the appropriate one. Try to download your API KEY that you created in JSON format. Then open it and you will see right links. Just copy them and past to file. +Otherwise, follow [Google OAuth Documentation](https://developers.google.com/identity/protocols/oauth2/) to solve them. +Most likely you will need to change **AUTH_URI** or **TOKEN_URI** to the appropriate one. + +## How to Use in Shell + +To apply these variables to your current shell session, use: + +```bash +. ./key/-env.sh +``` + +This command sources the script, making the variables available in your current session. Ensure `-env.sh` is in the `key` directory relative to your current location. \ No newline at end of file diff --git a/module/move/gspread/Cargo.toml b/module/move/gspread/Cargo.toml new file mode 100644 index 0000000000..7d75b04122 --- /dev/null +++ b/module/move/gspread/Cargo.toml @@ -0,0 +1,47 @@ +[package] +name = "gspread" +version = "0.1.0" +edition = "2021" +authors = [ + "Vsevolod Bakutov " +] +license = "MIT" +description = """ + Google Sheets Cli API +""" +categories = [ "algorithms", "development-tools" ] +keywords = [ "fundamental", "general-purpose" ] +default-run = "main" + +[[bin]] +name = "main" +path = "src/bin/main.rs" + +[features] +default = [ "enabled" ] +full = [ "enabled" ] +enabled = [ + "former/enabled", + "format_tools/enabled", + "reflect_tools/enabled", +] + +[dependencies] +mod_interface = { workspace = true, features = ["full"] } +former = { workspace = true, features = ["full"] } +format_tools = { workspace = true, features = ["full"] } +reflect_tools = { workspace = true, features = [ "full" ] } +clap = { version = "4.5.20", features = ["derive"] } +tokio = { version = "1", features = ["full"] } +google-sheets4 = "*" +pth = "0.21.0" +dotenv = "0.15" +serde = { version = "1.0.213", features = ["derive"] } +serde_with = "3.11.0" +error_tools = "0.19.0" +derive_tools = { version = "0.32.0", features = ["full"] } +serde_json = "1.0.132" +regex = "1.11.1" + +[dev-dependencies] +test_tools = { workspace = true } diff --git a/module/move/gspread/readme.md b/module/move/gspread/readme.md new file mode 100644 index 0000000000..2fdeb0f40a --- /dev/null +++ b/module/move/gspread/readme.md @@ -0,0 +1 @@ +## Google Sheets CLI \ No newline at end of file diff --git a/module/move/gspread/src/actions.rs b/module/move/gspread/src/actions.rs new file mode 100644 index 0000000000..1c96538040 --- /dev/null +++ b/module/move/gspread/src/actions.rs @@ -0,0 +1,16 @@ +//! +//! CLI actions of the tool. +//! + +mod private {} + +crate::mod_interface! +{ + layer gspread; + layer gspread_get_header; + layer gspread_get_rows; + layer gspread_cell_get; + layer gspread_cell_set; + layer gspread_cells_set; +} + diff --git a/module/move/gspread/src/actions/gspread.rs b/module/move/gspread/src/actions/gspread.rs new file mode 100644 index 0000000000..3bee6a3e27 --- /dev/null +++ b/module/move/gspread/src/actions/gspread.rs @@ -0,0 +1,57 @@ +//! +//! Google Sheets API actions. +//! +//! This module also contains the definition of Google Sheets Error. +//! + +mod private +{ + use regex::Regex; + use error_tools::typed::Error; + use derive_tools::AsRefStr; + use crate::*; + use ser::DisplayFromStr; + + #[ ser::serde_as ] + #[ derive( Debug, Error, AsRefStr, ser::Serialize ) ] + #[ serde( tag = "type", content = "data" ) ] + pub enum Error + { + #[ error( "Google Sheets returned error:\n{0}" ) ] + ApiError + ( + #[ from ] + #[ serde_as( as = "DisplayFromStr" ) ] + google_sheets4::Error + ) + } + + pub fn get_spreadsheet_id_from_url + ( + url : &str + ) -> Option< &str > + { + + let re = Regex::new( r"d/([^/]+)/edit" ).unwrap(); + if let Some( captures ) = re.captures( url ) + { + if let Some( id ) = captures.get( 1 ) + { + return Some( id.as_str() ); + } + } + + None + } + + pub type Result< T > = core::result::Result< T, Error >; +} + +crate::mod_interface! +{ + own use + { + Result, + get_spreadsheet_id_from_url, + }; +} \ No newline at end of file diff --git a/module/move/gspread/src/actions/gspread_cell_get.rs b/module/move/gspread/src/actions/gspread_cell_get.rs new file mode 100644 index 0000000000..3a4d6b1be3 --- /dev/null +++ b/module/move/gspread/src/actions/gspread_cell_get.rs @@ -0,0 +1,42 @@ +//! +//! Action for command "cell get" +//! +//! It returns a selected cell +//! + +mod private +{ + use crate::*; + use actions::gspread::Result; + use client::SheetsType; + use ser::JsonValue; + + pub async fn action + ( + hub : &SheetsType, + spreadsheet_id : &str, + table_name : &str, + cell_id : &str, + ) -> Result< JsonValue > + { + let result = hub + .spreadsheets() + .values_get( spreadsheet_id, format!( "{}!{}", table_name, cell_id ).as_str() ) + .doit() + .await? + .1 + .values; + + match result + { + Some( values ) => Ok( values.get( 0 ).unwrap().get( 0 ).unwrap().clone() ), + None => Ok( JsonValue::Null.clone() ) + } + + } +} + +crate::mod_interface! +{ + own use action; +} \ No newline at end of file diff --git a/module/move/gspread/src/actions/gspread_cell_set.rs b/module/move/gspread/src/actions/gspread_cell_set.rs new file mode 100644 index 0000000000..818a667f1c --- /dev/null +++ b/module/move/gspread/src/actions/gspread_cell_set.rs @@ -0,0 +1,50 @@ +//! +//! Action for command "cell set" +//! +//! It updates a selected cell +//! + + +mod private +{ + use google_sheets4::api::ValueRange; + use crate::*; + use actions::gspread::Result; + use client::SheetsType; + use ser::JsonValue; + + pub async fn action + ( + hub : &SheetsType, + spreadsheet_id : &str, + table_name : &str, + cell_id : &str, + value : &str + ) -> Result< i32 > + { + + let value = JsonValue::String( value.to_string() ); + let value_range = ValueRange + { + values : Some( vec![ vec![ value ] ] ), + ..ValueRange::default() + }; + + let result = hub + .spreadsheets() + .values_update( value_range, spreadsheet_id, format!( "{}!{}", table_name, cell_id ).as_str() ) + .value_input_option( "USER_ENTERED" ) + .doit() + .await? + .1 + .updated_cells + .unwrap(); + + Ok( result ) + } +} + +crate::mod_interface! +{ + own use action; +} \ No newline at end of file diff --git a/module/move/gspread/src/actions/gspread_cells_set.rs b/module/move/gspread/src/actions/gspread_cells_set.rs new file mode 100644 index 0000000000..a6528b6c4b --- /dev/null +++ b/module/move/gspread/src/actions/gspread_cells_set.rs @@ -0,0 +1,132 @@ +//! +//! Set command -> set specified values in specified columns in specified row +//! + +mod private +{ + use crate::*; + use google_sheets4::api:: + { + BatchUpdateValuesRequest, + ValueRange + }; + use ser:: + { + Deserialize, + JsonValue + }; + use std::collections::HashMap; + + /// Structure for --json value + #[ derive( Deserialize, Debug ) ] + struct ParsedJson + { + #[ serde( flatten ) ] + columns : HashMap< String, String > + } + + /// Parse --json value + fn parse_json + ( + json_str : &str + ) -> Result< ParsedJson, String > + { + serde_json::from_str::< ParsedJson >( json_str ).map_err + ( + | err | format!( "Failed to parse JSON: {}", err ) + ) + } + + /// Check availables keys. + /// Available keys: "id" -> row's id + fn check_select_row_by_key + ( + key : &str + ) -> Result< (), String > + { + let keys = vec![ "id" ]; + if keys.contains( &key ) + { + Ok( () ) + } + else + { + Err( format!( "Invalid select_row_by_key: '{}'. Allowed keys: {:?}", key, keys ) ) + } + } + + fn is_all_uppercase_letters + ( + s : &str + ) -> Result< (), String > + { + if s.chars().all( | c | c.is_ascii_uppercase() ) + { + Ok( () ) + } + else + { + Err( format!( "The string '{}' contains invalid characters. Only uppercase letters (A-Z) are allowed.", s ) ) + } + } + + pub async fn action + ( + hub : &SheetsType, + select_row_by_key : &str, + json_str : &str, + spreadsheet_id : &str, + table_name : &str + ) -> Result< String, String > + { + check_select_row_by_key( select_row_by_key )?; + + let mut pairs = parse_json( json_str )?; + + let row_id = pairs + .columns + .remove( select_row_by_key ) + .ok_or_else( || format!( "Key '{}' not found in JSON", select_row_by_key ) )?; + + let mut value_ranges= Vec::new(); + + for ( key, value ) in pairs.columns.into_iter() + { + is_all_uppercase_letters( key.as_str() )?; + value_ranges.push + ( + ValueRange + { + range: Some( format!( "{}!{}{}", table_name, key, row_id ) ), + values: Some( vec![ vec![ JsonValue::String( value.to_string() ) ] ] ), + ..Default::default() + } + ); + }; + + let req = BatchUpdateValuesRequest + { + value_input_option: Some( "USER_ENTERED".to_string() ), + data: Some( value_ranges ), + include_values_in_response: Some( true ), + ..Default::default() + }; + + let result = hub + .spreadsheets() + .values_batch_update( req, spreadsheet_id ) + .doit() + .await; + + match result + { + Ok( _ ) => Ok( format!( "Cells were sucsessfully updated!" ) ), + Err( error ) => Err( format!( "{}", error ) ) + } + } +} + +crate::mod_interface! +{ + own use action; +} \ No newline at end of file diff --git a/module/move/gspread/src/actions/gspread_get_header.rs b/module/move/gspread/src/actions/gspread_get_header.rs new file mode 100644 index 0000000000..8f7b83c477 --- /dev/null +++ b/module/move/gspread/src/actions/gspread_get_header.rs @@ -0,0 +1,58 @@ +//! +//! Action for command "header" +//! +//! It returns header (first row) +//! + + +mod private +{ + use std::fmt; + use crate::*; + use client::SheetsType; + use actions::gspread::Result; + use format_tools::AsTable; + use util::display_table::display_header; + use ser::JsonValue; + + #[ derive( Debug ) ] + pub struct Report + { + pub rows : Vec< RowWrapper > + } + + impl fmt::Display for Report + { + fn fmt + ( + &self, + f : &mut fmt::Formatter + ) -> fmt::Result + { + display_header( &AsTable::new( &self.rows ), f ) + } + } + + pub async fn action + ( + hub : &SheetsType, + spreadsheet_id : &str, + table_name: &str) -> Result< Vec< Vec< JsonValue > > > + { + let result = hub + .spreadsheets() + .values_get( spreadsheet_id, format!( "{}!A1:Z1", table_name ).as_str() ) + .doit() + .await? + .1 + .values + .unwrap_or_else( | | Vec::new() ); + + Ok( result ) + } +} + +crate::mod_interface! +{ + own use action; +} \ No newline at end of file diff --git a/module/move/gspread/src/actions/gspread_get_rows.rs b/module/move/gspread/src/actions/gspread_get_rows.rs new file mode 100644 index 0000000000..3a083217ed --- /dev/null +++ b/module/move/gspread/src/actions/gspread_get_rows.rs @@ -0,0 +1,38 @@ +//! +//! Action for command "rows" +//! +//! It returns all rows but not header +//! + + +mod private +{ + use crate::*; + use client::SheetsType; + use actions::gspread::Result; + use ser::JsonValue; + + pub async fn action + ( + hub : &SheetsType, + spreadsheet_id : &str, + table_name : &str + ) -> Result< Vec< Vec < JsonValue > > > + { + let result = hub + .spreadsheets() + .values_get( spreadsheet_id, format!( "{}!A2:Z", table_name ).as_str() ) + .doit() + .await? + .1 + .values + .unwrap_or_else( | | Vec::new() ); + + Ok( result ) + } +} + +crate::mod_interface! +{ + own use action; +} diff --git a/module/move/gspread/src/bin/main.rs b/module/move/gspread/src/bin/main.rs new file mode 100644 index 0000000000..71fd85c041 --- /dev/null +++ b/module/move/gspread/src/bin/main.rs @@ -0,0 +1,33 @@ +use std::error::Error; +use std::env; +use clap::Parser; +use dotenv::dotenv; + +use gspread:: +{ + client::hub, + commands::{ Cli, CliCommand, self }, + secret::Secret, +}; + +#[ tokio::main ] +async fn main() -> Result< (), Box< dyn Error > > +{ + dotenv().ok(); + + let secret = Secret::load()?; + + let hub = hub( &secret ).await?; + + let cli = Cli::parse(); + + match cli.command + { + CliCommand::GSpread( cmd ) => + { + commands::gspread::command( &hub, cmd ).await; + } + } + + Ok( () ) +} diff --git a/module/move/gspread/src/client.rs b/module/move/gspread/src/client.rs new file mode 100644 index 0000000000..c836ccf4d3 --- /dev/null +++ b/module/move/gspread/src/client.rs @@ -0,0 +1,79 @@ +//! +//! Client of API. +//! + +mod private +{ + + use google_sheets4 as sheets4; + use sheets4::Sheets; + use sheets4::hyper_rustls; + use sheets4::hyper_util; + use sheets4::yup_oauth2:: + { + self, + ApplicationSecret + }; + use hyper_util::client::legacy::connect::HttpConnector; + + pub use hyper_util::client::legacy::Client; + + use std:: + { + error::Error, + }; + + use crate::*; + use secret::Secret; + + pub type SheetsType = Sheets< hyper_rustls::HttpsConnector< HttpConnector > >; + + pub async fn hub( secrets: &Secret ) -> Result< SheetsType, Box< dyn Error > > + { + let secret: ApplicationSecret = ApplicationSecret + { + client_id : secrets.CLIENT_ID.clone(), + auth_uri : secrets.AUTH_URI.clone(), + token_uri : secrets.TOKEN_URI.clone(), + client_secret : secrets.CLIENT_SECRET.clone(), + .. Default::default() + }; + + let auth = yup_oauth2::InstalledFlowAuthenticator::builder + ( + secret, + yup_oauth2::InstalledFlowReturnMethod::HTTPRedirect, + ) + .build() + .await + .unwrap(); + + let client = Client::builder + ( + hyper_util::rt::TokioExecutor::new() + ) + .build + ( + hyper_rustls::HttpsConnectorBuilder::new() + .with_native_roots() + .unwrap() + .https_or_http() + .enable_http1() + .build() + ); + + Ok( Sheets::new( client, auth ) ) + } + + +} + +crate::mod_interface! +{ + exposed use + { + hub, + Client, + SheetsType + }; +} \ No newline at end of file diff --git a/module/move/gspread/src/commands.rs b/module/move/gspread/src/commands.rs new file mode 100644 index 0000000000..5ce88f9eb2 --- /dev/null +++ b/module/move/gspread/src/commands.rs @@ -0,0 +1,52 @@ +//! +//! Commands +//! + + +mod private +{ + + use clap:: + { + Parser, + Subcommand + }; + + use crate::*; + use commands::gspread; + + /// CLI commands of the tool. + #[ derive ( Debug, Parser ) ] + pub struct Cli + { + /// Root of the CLI commands. + #[ command ( subcommand ) ] + pub command : CliCommand, + } + + /// Root of the CLI commands. + #[ derive ( Debug, Subcommand ) ] + pub enum CliCommand + { + /// Google Sheets commands. + #[ command ( subcommand, name = "gspread" ) ] + GSpread( gspread::Command ), + } + +} + +crate::mod_interface! +{ + layer gspread; + layer gspread_header; + layer gspread_rows; + layer gspread_cell; + layer gspread_cells; + + own use + { + Cli, + CliCommand, + }; +} + diff --git a/module/move/gspread/src/commands/gspread.rs b/module/move/gspread/src/commands/gspread.rs new file mode 100644 index 0000000000..8398aa3ec6 --- /dev/null +++ b/module/move/gspread/src/commands/gspread.rs @@ -0,0 +1,104 @@ +//! +//! Collection of Google Sheets API commands. +//! + + +mod private +{ + + use clap::{ Subcommand, Parser }; + + use crate::*; + use client::SheetsType; + + use commands:: + { + gspread_header, + gspread_rows, + gspread_cell, + gspread_cells + }; + + #[ derive( Debug, Parser ) ] + pub struct CommonArgs + { + #[ arg( long ) ] + pub url : String, + + #[ arg( long ) ] + pub tab : String + } + + #[ derive( Debug, Subcommand ) ] + pub enum Command + { + + #[ command ( name = "header" ) ] + Header + ( + CommonArgs + ), + + #[ command( name = "rows" ) ] + Rows + ( + CommonArgs + ), + + #[ command ( subcommand, name = "cell" ) ] + Cell + ( + gspread_cell::Commands + ), + + #[ command ( subcommand, name = "cells" ) ] + Cells + ( + gspread_cells::Commands + ) + + } + + pub async fn command + ( + hub : &SheetsType, + command : Command, + ) + { + match command + { + + Command::Header( header_command ) => + { + gspread_header::command( hub, header_command ).await; + }, + + Command::Rows( rows_command ) => + { + gspread_rows::command( hub, rows_command ).await; + }, + + Command::Cell( cell_command ) => + { + gspread_cell::command( hub, cell_command ).await; + }, + + Command::Cells( cells_command) => + { + gspread_cells::command( hub, cells_command ).await; + }, + + } + } + +} + +crate::mod_interface! +{ + own use + { + CommonArgs, + Command, + command, + }; +} \ No newline at end of file diff --git a/module/move/gspread/src/commands/gspread_cell.rs b/module/move/gspread/src/commands/gspread_cell.rs new file mode 100644 index 0000000000..e81f9d8595 --- /dev/null +++ b/module/move/gspread/src/commands/gspread_cell.rs @@ -0,0 +1,106 @@ +//! +//! Collection of subcommands fo command "cell" +//! + +mod private +{ + + use clap::Subcommand; + + use crate::*; + use actions; + use actions::gspread::get_spreadsheet_id_from_url; + use client::SheetsType; + + #[ derive( Debug, Subcommand ) ] + pub enum Commands + { + #[ command( name = "get" ) ] + Get + { + #[ arg( long ) ] + url : String, + + #[ arg( long ) ] + tab : String, + + #[ arg( long ) ] + cel : String, + }, + + #[ command( name = "set" ) ] + Set + { + #[ arg( long ) ] + url : String, + + #[ arg( long ) ] + tab : String, + + #[ arg( long ) ] + cel : String, + + #[ arg( long ) ] + val : String + } + } + + pub async fn command + ( + hub : &SheetsType, + commands : Commands + ) + { + match commands + { + Commands::Get { url, tab, cel } => + { + let spreadsheet_id = get_spreadsheet_id_from_url( url.as_str() ).unwrap(); + + let result = actions::gspread_cell_get::action + ( + hub, + spreadsheet_id, + tab.as_str(), + cel.as_str() + ).await; + + match result + { + Ok( value ) => println!( "Value: {}", value ), + Err( error ) => println!( "Error: {}", error ), + } + }, + + Commands::Set { url, tab, cel, val } => + { + let spreadsheet_id = get_spreadsheet_id_from_url( url.as_str() ).unwrap(); + + let result = actions::gspread_cell_set::action + ( + hub, + spreadsheet_id, + tab.as_str(), + cel.as_str(), + val.as_str() + ).await; + + match result + { + Ok( value ) => println!( "Success: {:?}", value ), + Err( error ) => println!( "Error: {}", error ), + } + } + + } + } +} + +crate::mod_interface! +{ + own use + { + command, + Commands, + }; +} \ No newline at end of file diff --git a/module/move/gspread/src/commands/gspread_cells.rs b/module/move/gspread/src/commands/gspread_cells.rs new file mode 100644 index 0000000000..cd7a4cc555 --- /dev/null +++ b/module/move/gspread/src/commands/gspread_cells.rs @@ -0,0 +1,72 @@ +//! +//! Cells commands. +//! set command -> set specified values in specified columns in specified row. +//! + +mod private +{ + use clap::Subcommand; + + use crate::*; + use actions::gspread::get_spreadsheet_id_from_url; + + #[ derive( Debug, Subcommand ) ] + pub enum Commands + { + #[ command( name = "set" ) ] + Set + { + #[ arg( long ) ] + select_row_by_key : String, + + #[ arg( long ) ] + json : String, + + #[ arg( long ) ] + url : String, + + #[ arg( long ) ] + tab : String + } + + } + + pub async fn command + ( + hub : &SheetsType, + commands : Commands + ) + { + match commands + { + Commands::Set { select_row_by_key, json, url, tab } => + { + let spreadsheet_id = get_spreadsheet_id_from_url( url.as_str() ).unwrap(); + + let result = actions::gspread_cells_set::action + ( + &hub, + select_row_by_key.as_str(), + json.as_str(), + spreadsheet_id, + tab.as_str() + ).await; + + match result + { + Ok( msg ) => println!( "{}", msg ), + Err( error ) => println!( "{}", error ) + } + } + } + } +} + +crate::mod_interface! +{ + own use + { + command, + Commands + }; +} \ No newline at end of file diff --git a/module/move/gspread/src/commands/gspread_header.rs b/module/move/gspread/src/commands/gspread_header.rs new file mode 100644 index 0000000000..49be5e6e86 --- /dev/null +++ b/module/move/gspread/src/commands/gspread_header.rs @@ -0,0 +1,77 @@ +//! +//! Command "header" +//! + +mod private +{ + use std::fmt; + use crate::*; + use commands::gspread::CommonArgs; + use client::SheetsType; + use actions; + use actions::gspread::get_spreadsheet_id_from_url; + use format_tools::AsTable; + use util::display_table::display_header; + + #[ derive( Debug ) ] + pub struct Report + { + pub rows : Vec< RowWrapper > + } + + impl fmt::Display for Report + { + fn fmt + ( + &self, + f : &mut fmt::Formatter + ) -> fmt::Result + { + display_header( &AsTable::new( &self.rows ), f ) + } + } + + pub async fn command + ( + hub : &SheetsType, + args : CommonArgs, + ) + { + match args + { + CommonArgs { url, tab } => + { + let spreadsheet_id = get_spreadsheet_id_from_url( url.as_str() ).unwrap(); + let result = actions::gspread_get_header::action + ( + hub, + spreadsheet_id, + tab.as_str() + ).await; + + match result + { + Ok( header ) => + { + let header_wrapped = header + .into_iter() + .map( | row | RowWrapper{ max_len: row.len(), row } ) + .collect(); + + println!( "Header: \n {}", Report{ rows: header_wrapped } ); + } + Err( error ) => println!( "Error: {}", error ), + } + } + } + } +} + +crate::mod_interface! +{ + own use + { + command + }; +} + diff --git a/module/move/gspread/src/commands/gspread_rows.rs b/module/move/gspread/src/commands/gspread_rows.rs new file mode 100644 index 0000000000..86f0c41f59 --- /dev/null +++ b/module/move/gspread/src/commands/gspread_rows.rs @@ -0,0 +1,77 @@ +//! +//! Command "rows" +//! + +mod private +{ + use std::fmt; + use crate::*; + use commands::gspread::CommonArgs; + use client::SheetsType; + use actions; + use actions::gspread::get_spreadsheet_id_from_url; + use format_tools::AsTable; + use util::display_table::display_rows; + + pub struct Report + { + pub rows : Vec< RowWrapper > + } + + impl fmt::Display for Report + { + fn fmt + ( + &self, + f : &mut fmt::Formatter + ) -> fmt::Result + { + display_rows( &AsTable::new( &self.rows ), f ) + } + } + + pub async fn command + ( + hub : &SheetsType, + args : CommonArgs + ) + { + match args + { + CommonArgs { url, tab } => + { + let spreadsheet_id = get_spreadsheet_id_from_url( url.as_str() ).unwrap(); + + let result = actions::gspread_get_rows::action + ( + hub, + spreadsheet_id, + tab.as_str() + ).await; + + match result + { + Ok( rows ) => + { + let max_len = rows.iter().map(|row| row.len()).max().unwrap_or(0); + let rows_wrapped: Vec = rows + .into_iter() + .map(|row| RowWrapper { row, max_len }) + .collect(); + + println!( "Rows: \n {}", Report{ rows: rows_wrapped } ); + } + Err( error ) => println!( "Error: {}", error ), + } + } + } + } +} + +crate::mod_interface! +{ + own use + { + command + }; +} diff --git a/module/move/gspread/src/debug.rs b/module/move/gspread/src/debug.rs new file mode 100644 index 0000000000..11f63d821e --- /dev/null +++ b/module/move/gspread/src/debug.rs @@ -0,0 +1,20 @@ +mod private +{ +} + +use format_tools:: +{ + Fields, + TableWithFields, +}; +use std::borrow::Cow; + +pub mod row_wrapper; + +crate::mod_interface! +{ + exposed use + { + row_wrapper::RowWrapper, + }; +} diff --git a/module/move/gspread/src/debug/row_wrapper.rs b/module/move/gspread/src/debug/row_wrapper.rs new file mode 100644 index 0000000000..b8e1635ac7 --- /dev/null +++ b/module/move/gspread/src/debug/row_wrapper.rs @@ -0,0 +1,59 @@ +//! +//! Gspread wrapper for outputting data to console +//! +//! It is used for "header" and "rows" commands +//! + +use super::*; +use crate::*; +use ser::JsonValue; + + +#[ derive( Debug ) ] +pub struct RowWrapper +{ + pub row: Vec< JsonValue >, + pub max_len: usize +} + +impl Clone for RowWrapper +{ + fn clone( &self ) -> Self + { + Self + { + row: self.row.clone(), + max_len: self.max_len.clone() + } + } +} + +impl TableWithFields for RowWrapper {} +impl Fields< &'_ str, Option< Cow< '_, str > > > +for RowWrapper +{ + type Key< 'k > = &'k str; + type Val< 'v > = Option< Cow< 'v, str > >; + + fn fields( &self ) -> impl IteratorTrait< Item= ( &'_ str, Option > ) > + { + let mut dst = Vec::new(); + + for ( index, value ) in self.row.iter().enumerate() + { + let column_name = format!( "Column{}", index ); + let title = Box::leak( column_name.into_boxed_str() ) as &str; + dst.push( ( title, Some( Cow::Owned( value.to_string() ) ) ) ) + } + + //adding empty values for missing cells + for index in self.row.len()..self.max_len + { + let column_name = format!( "Column{}", index ); + let title = Box::leak( column_name.into_boxed_str() ) as &str; + dst.push( ( title, Some( Cow::Owned( "".to_string() ) ) ) ); + } + + dst.into_iter() + } +} \ No newline at end of file diff --git a/module/move/gspread/src/lib.rs b/module/move/gspread/src/lib.rs new file mode 100644 index 0000000000..c0d2432985 --- /dev/null +++ b/module/move/gspread/src/lib.rs @@ -0,0 +1,41 @@ +use mod_interface::mod_interface; +use error_tools::thiserror; + +mod private +{ +} + +pub mod ser +{ + pub use serde:: + { + Serialize, + Deserialize, + }; + pub use serde_json:: + { + value::{ Value as JsonValue, Number as JsonNumber }, + error::Error, + self + }; + pub use serde_with::*; +} + +crate::mod_interface! +{ + + layer client; + layer debug; + layer commands; + layer actions; + layer secret; + layer util; + + exposed use ::reflect_tools:: + { + Fields, + _IteratorTrait, + IteratorTrait, + }; + +} \ No newline at end of file diff --git a/module/move/gspread/src/secret.rs b/module/move/gspread/src/secret.rs new file mode 100644 index 0000000000..f70792b958 --- /dev/null +++ b/module/move/gspread/src/secret.rs @@ -0,0 +1,159 @@ +//! +//! Tool's secret +//! + +mod private +{ + use crate::*; + use std:: + { + env, + sync::OnceLock, + }; + + use error_tools::typed::Error; + use ser::DisplayFromStr; + + #[ ser::serde_as ] + #[ derive( Debug, Error, ser::Serialize ) ] + #[ serde( tag = "type", content = "data" ) ] + pub enum Error + { + #[ error( "Secret file is illformed\n{0}" ) ] + SecretFileIllformed + ( + #[ from ] + #[ serde_as( as = "DisplayFromStr" ) ] + dotenv::Error + ), + + #[ error( "Secret missing the variable {0}" ) ] + VariableMissing( &'static str ), + + #[ error( "Secret error processing in the variable {0}\n{1}" ) ] + VariableIllformed( &'static str, String ), + + } + + pub type Result< R > = std::result::Result< R, Error >; + + #[ derive( Debug ) ] + #[ allow( non_snake_case ) ] + pub struct Secret + { + pub CLIENT_SECRET : String, + pub CLIENT_ID: String, + pub AUTH_URI : String, + pub TOKEN_URI : String, + } + + impl Secret + { + #[ allow( non_snake_case ) ] + pub fn load() -> Result< Self > + { + let path = "./.key/-env.sh"; + + let r = dotenv::from_path( path ); + if let Err( ref err ) = r + { + if !matches!( err, dotenv::Error::Io(_) ) + { + return Err( r.expect_err( &format!( "Failed to load {path}" ) ).into() ); + } + } + + let config = Self + { + CLIENT_SECRET : var( "CLIENT_SECRET", None )?, + CLIENT_ID : var( "CLIENT_ID", None )?, + AUTH_URI : var ( "AUTH_URI", None )?, + TOKEN_URI : var ( "TOKEN_URI", None )? + }; + Ok( config ) + } + + pub fn read() -> Secret + { + Self::load().unwrap_or_else( | err | + { + let example = include_str!("../.key/readme.md"); + let explanation = format! + ( + r#" = Lack of secrets + +Failed to load secret or some its parameters. +{err} + + = Fix + +Either define missing environment variable or make sure `./.key/-env.toml` file has it defined. + + = More information + +{example} +"# + ); + panic!( "{}", explanation ); + }) + } + + pub fn get() -> &'static Secret + { + static INSTANCE : OnceLock< Secret > = OnceLock::new(); + INSTANCE.get_or_init( || Self::read() ) + } + + } + + fn var + ( + name : &'static str, + default : Option<&'static str>, + ) -> Result < String > + { + match env::var(name) + { + Ok( val ) => Ok ( val ), + Err( _ ) => + { + if let Some( default_value ) = default + { + Ok( default_value.to_string() ) + } + else + { + Err ( Error::VariableMissing( name ) ) + } + } + } + } + + fn _var_path + ( + name : &'static str, + default : Option<&'static str>, + ) -> Result < pth::AbsolutePath > + { + let p = var( name, default )?; + pth::AbsolutePath::from_paths( ( pth::CurrentPath, p ) ) + .map_err( |e| Error::VariableIllformed( name, e.to_string() ) ) + } + +} + +crate::mod_interface! +{ + + own use + { + Error, + Result, + }; + + orphan use + { + Secret, + }; + +} \ No newline at end of file diff --git a/module/move/gspread/src/util.rs b/module/move/gspread/src/util.rs new file mode 100644 index 0000000000..ac76ad86b2 --- /dev/null +++ b/module/move/gspread/src/util.rs @@ -0,0 +1,6 @@ +mod private {} + +crate::mod_interface! +{ + layer display_table; +} \ No newline at end of file diff --git a/module/move/gspread/src/util/display_table.rs b/module/move/gspread/src/util/display_table.rs new file mode 100644 index 0000000000..3d96964d9b --- /dev/null +++ b/module/move/gspread/src/util/display_table.rs @@ -0,0 +1,55 @@ + + +mod private +{ + + use std::fmt; + + use format_tools:: + { + TableFormatter, + print, + output_format, + TableOutputFormat + }; + + pub fn display_rows< 'a > + ( + data : &'a impl TableFormatter< 'a >, + f : &mut fmt::Formatter< '_ > + ) -> fmt::Result + { + display_data( data, f, output_format::Table::default() ) + } + + pub fn display_header < 'a > + ( + data : &'a impl TableFormatter< 'a >, + f : &mut fmt::Formatter< '_ > + ) -> fmt::Result + { + display_data( data, f, output_format::Table::default() ) + } + + pub fn display_data < 'a > + ( + data : &'a impl TableFormatter< 'a >, + f : &mut fmt::Formatter< '_ >, + format : impl TableOutputFormat, + ) -> fmt::Result + { + let printer = print::Printer::with_format( &format ); + let mut context = print::Context::new( f, printer ); + TableFormatter::fmt( data, &mut context ) + } + +} + +crate::mod_interface! +{ + own use + { + display_rows, + display_header + }; +} \ No newline at end of file diff --git a/module/move/gspread/tests/inc/cell_tests.rs b/module/move/gspread/tests/inc/cell_tests.rs new file mode 100644 index 0000000000..f93ec7b1a4 --- /dev/null +++ b/module/move/gspread/tests/inc/cell_tests.rs @@ -0,0 +1,99 @@ +#[ allow( unused_imports ) ] +use super::*; + +use the_module:: +{ + hub, + Secret, + actions, + SheetsType, + ser::JsonValue +}; + +async fn setup() -> ( SheetsType, &'static str, &'static str ) +{ + let secret = Secret::load().expect( "Failed to load secret" ); + let hub = hub( &secret ).await.expect( "Failed to create a hub" ); + let spreadsheet_id = "1EAEdegMpitv-sTuxt8mV8xQxzJE7h_J0MxQoyLH7xxU"; + let table_name = "tab5"; + + ( hub, spreadsheet_id, table_name ) +} + +#[ tokio::test ] +async fn test_get_cell() +{ + let ( hub, spreadsheet_id, table_name ) = setup().await; + let cell_id = "R2C1"; + + let result = actions::gspread_cell_get::action + ( + &hub, + spreadsheet_id, + table_name, + cell_id + ) + .await + .expect( "Error getting cell" ); + + assert_eq!( result, "Vsevolod" ) +} + +#[ tokio::test ] +async fn test_get_cell_empty() +{ + let ( hub, spreadsheet_id, table_name ) = setup().await; + let cell_id = "R4C1"; + + let result = actions::gspread_cell_get::action + ( + &hub, + spreadsheet_id, + table_name, + cell_id + ) + .await + .expect( "Error getting cell" ); + + assert_eq!( result, JsonValue::Null ) +} + +#[ tokio::test ] +async fn test_set_cell() +{ + let ( hub, spreadsheet_id, table_name ) = setup().await; + let cell_id = "R2C1"; + let value = "Seva"; + + let result = actions::gspread_cell_set::action + ( + &hub, + spreadsheet_id, + table_name, + cell_id, + value + ) + .await; + + assert!( result.is_ok() ); +} + +#[ tokio::test ] +async fn test_set_empty_cell() +{ + let ( hub, spreadsheet_id, table_name ) = setup().await; + let cell_id = "R4C1"; + let value = "Stanislav"; + + let result = actions::gspread_cell_set::action + ( + &hub, + spreadsheet_id, + table_name, + cell_id, + value + ) + .await; + + assert!( result.is_ok() ); +} \ No newline at end of file diff --git a/module/move/gspread/tests/inc/cells_tests.rs b/module/move/gspread/tests/inc/cells_tests.rs new file mode 100644 index 0000000000..4c91a9a19c --- /dev/null +++ b/module/move/gspread/tests/inc/cells_tests.rs @@ -0,0 +1,79 @@ +#[ allow( unused_imports ) ] +use super::*; + +use the_module:: +{ + hub, + Secret, + actions, + SheetsType, +}; + +async fn setup() -> ( SheetsType, &'static str, &'static str, &'static str ) +{ + let secret = Secret::load().expect( "Failed to load secret" ); + let hub = hub( &secret ).await.expect( "Failed to create a hub" ); + let select_row_by_key = "id"; + let spreadsheet_id = "1EAEdegMpitv-sTuxt8mV8xQxzJE7h_J0MxQoyLH7xxU"; + let table_name = "tab7"; + + ( hub, select_row_by_key, spreadsheet_id, table_name ) +} + +#[ tokio::test ] +async fn test_set_cells() +{ + let + ( + hub, + select_row_by_key, + spreadsheet_id, + table_name + ) = setup().await; + + let json = r#"{ "id": "2", "A": "new_val1", "B": "new_val2"}"#; + + let result = actions::gspread_cells_set::action + ( + &hub, + select_row_by_key, + json, + spreadsheet_id, + table_name, + ) + .await + .expect( "Error while updating" ); + + assert_eq!( result, "Cells were sucsessfully updated!" ) +} + +#[ tokio::test ] +async fn test_set_cells_wrong_row() +{ + let + ( + hub, + select_row_by_key, + spreadsheet_id, + table_name + ) = setup().await; + + let json = r#"{ "id": "a", "A": "new_val1", "B": "new_val2"}"#; + + let result = actions::gspread_cells_set::action + ( + &hub, + select_row_by_key, + json, + spreadsheet_id, + table_name, + ) + .await + .expect( "Error while updating" ); + + assert_eq! + ( + result, + r#"Bad Request: {"error":{"code":400,"message":"Invalid data[0]: Unable to parse range: tab7!Aa","status":"INVALID_ARGUMENT"}}"# + ) +} \ No newline at end of file diff --git a/module/move/gspread/tests/inc/header_tests.rs b/module/move/gspread/tests/inc/header_tests.rs new file mode 100644 index 0000000000..3009a63bb1 --- /dev/null +++ b/module/move/gspread/tests/inc/header_tests.rs @@ -0,0 +1,90 @@ +#[ allow( unused_imports ) ] +use super::*; + +use the_module:: +{ + hub, + Secret, + actions, + SheetsType +}; + +async fn setup() -> ( SheetsType, &'static str ) +{ + let secret = Secret::load().expect( "Failed to load secret" ); + let hub = hub( &secret ).await.expect( "Failed to create a hub" ); + let spreadsheet_id = "1EAEdegMpitv-sTuxt8mV8xQxzJE7h_J0MxQoyLH7xxU"; + + ( hub, spreadsheet_id ) +} +#[ tokio::test ] +async fn test_get_header() +{ + let ( hub, spreadsheet_id ) = setup().await; + let table_name = "tab1"; + + let result = actions::gspread_get_header::action + ( + &hub, + spreadsheet_id, + table_name + ) + .await + .expect( "Error getting header" ); + + assert_eq!( result, vec![ vec![ "Name", "Surname", "Age" ] ] ); +} + +#[ tokio::test ] +async fn test_get_header_with_spaces() +{ + let ( hub, spreadsheet_id ) = setup().await; + let table_name = "tab2"; + + let result = actions::gspread_get_header::action + ( + &hub, + spreadsheet_id, + table_name + ) + .await + .expect( "Error getting header" ); + + assert_eq!( result, vec![ vec![ "Name", "", "Age" ] ] ); +} + +#[ tokio::test ] +async fn test_get_header_empty() +{ + let ( hub, spreadsheet_id ) = setup().await; + let table_name = "tab3"; + + let result = actions::gspread_get_header::action + ( + &hub, + spreadsheet_id, + table_name + ) + .await + .expect( "Error getting header" ); + + assert_eq!( result, Vec::< Vec< String > >::new() ); +} + +#[ tokio::test ] +async fn test_get_header_with_empty_end() +{ + let ( hub, spreadsheet_id ) = setup().await; + let table_name = "tab4"; + + let result = actions::gspread_get_header::action + ( + &hub, + spreadsheet_id, + table_name + ) + .await + .expect( "Error getting header" ); + + assert_eq!( result, vec![ vec![ "Name", "Surname" ] ] ); +} \ No newline at end of file diff --git a/module/move/gspread/tests/inc/mod.rs b/module/move/gspread/tests/inc/mod.rs new file mode 100644 index 0000000000..a357c3fe4a --- /dev/null +++ b/module/move/gspread/tests/inc/mod.rs @@ -0,0 +1,14 @@ +//! +//! Here is used the +//! https://docs.google.com/spreadsheets/d/1EAEdegMpitv-sTuxt8mV8xQxzJE7h_J0MxQoyLH7xxU/edit?gid=0#gid=0 +//! test spreadsheet +//! + + +#[ allow( unused_imports ) ] +use super::*; + +mod header_tests; +mod rows_tests; +mod cell_tests; +mod cells_tests; \ No newline at end of file diff --git a/module/move/gspread/tests/inc/rows_tests.rs b/module/move/gspread/tests/inc/rows_tests.rs new file mode 100644 index 0000000000..d9032f8544 --- /dev/null +++ b/module/move/gspread/tests/inc/rows_tests.rs @@ -0,0 +1,93 @@ +#[ allow( unused_imports ) ] +use super::*; + +use the_module:: +{ + hub, + Secret, + actions, + SheetsType +}; + +async fn setup() -> ( SheetsType, &'static str ) +{ + let secret = Secret::load().expect( "Failed to load secret" ); + let hub = hub( &secret ).await.expect( "Failed to create a hub" ); + let spreadsheet_id = "1EAEdegMpitv-sTuxt8mV8xQxzJE7h_J0MxQoyLH7xxU"; + + ( hub, spreadsheet_id ) +} + +#[ tokio::test ] +async fn test_get_rows() +{ + let ( hub, spreadsheet_id ) = setup().await; + let table_name = "tab1"; + + let result = actions::gspread_get_rows::action + ( + &hub, + spreadsheet_id, + table_name + ) + .await + .expect( "Error getting rows" ); + + assert_eq! + ( + result, + vec![ + vec![ "Vsevolod", "Bakutov", "20" ], + vec![ "Victor", "Ovsyanik", "85" ], + vec![ "Olexandr", "Optimus", "28" ], + vec![ "Ivan", "Optimus", "34" ], + vec![ "Bogdan", "Optimus", "28" ], + ] + ) +} + +#[ tokio::test ] +async fn test_get_rows_with_spaces() +{ + let ( hub, spreadsheet_id ) = setup().await; + let table_name = "tab2"; + + let result = actions::gspread_get_rows::action + ( + &hub, + spreadsheet_id, + table_name + ) + .await + .expect( "Error getting rows" ); + + assert_eq! + ( + result, + vec![ + vec![ "Vsevolod", "Bakutov" ], + vec![ "Victor", "", "85" ], + vec![ "", "Optimus", "28" ], + vec![ ], + vec![ "Bogdan", "Optimus", "28" ], + ] + ) +} + +#[ tokio::test ] +async fn test_get_rows_empty() +{ + let ( hub, spreadsheet_id ) = setup().await; + let table_name = "tab3"; + + let result = actions::gspread_get_rows::action + ( + &hub, + spreadsheet_id, + table_name + ) + .await + .expect( "Error getting rows" ); + + assert_eq!( result, Vec::< Vec< String > >::new() ) +} \ No newline at end of file diff --git a/module/move/gspread/tests/smoke_test.rs b/module/move/gspread/tests/smoke_test.rs new file mode 100644 index 0000000000..c3163b32ed --- /dev/null +++ b/module/move/gspread/tests/smoke_test.rs @@ -0,0 +1,12 @@ + +#[ test ] +fn local_smoke_test() +{ + test_tools::smoke_test_for_local_run(); +} + +#[ test ] +fn published_smoke_test() +{ + test_tools::smoke_test_for_published_run(); +} \ No newline at end of file diff --git a/module/move/gspread/tests/tests.rs b/module/move/gspread/tests/tests.rs new file mode 100644 index 0000000000..201ae26926 --- /dev/null +++ b/module/move/gspread/tests/tests.rs @@ -0,0 +1,9 @@ + + +#[ allow( unused_imports ) ] +use gspread as the_module; +#[ allow( unused_imports ) ] +use test_tools::exposed::*; + +#[ cfg( feature = "enabled" ) ] +mod inc; \ No newline at end of file diff --git a/module/move/willbe/src/entity/files/either.rs b/module/move/willbe/src/entity/files/either.rs index aa7fdb5863..ef151d616c 100644 --- a/module/move/willbe/src/entity/files/either.rs +++ b/module/move/willbe/src/entity/files/either.rs @@ -16,7 +16,7 @@ use std:: // Result, // }; -/// Wrapper over `data_type::Either< CrateDir, ManifestFile >` with utils methods. +/// Wrapper over `data_type::Either< CrateDir, ManifestFile >` with util methods. #[ derive( Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug ) ] pub struct EitherDirOrFile( data_type::Either< CrateDir, ManifestFile > ); From f8ab967785d388c55b6eefd2fa68938b5cedd996 Mon Sep 17 00:00:00 2001 From: Ruslan Date: Tue, 3 Dec 2024 19:45:09 +0200 Subject: [PATCH 56/67] READY : Agents (#1500) * `parent` * Simplify context * Remove redunant `from_iter` * Fix joining of paths * Start working on `ScenarioProcessed` * Implement `ScenarioProcessed` --- module/move/assistant/Cargo.toml | 1 - module/move/assistant/src/agents.rs | 1 + module/move/assistant/src/agents/context.rs | 72 ++------ module/move/assistant/src/agents/path.rs | 160 +++++------------- .../src/agents/scenario_processed.rs | 99 +++++++++++ .../tests/inc/agents_tests/context_test.rs | 19 +-- .../assistant/tests/inc/agents_tests/mod.rs | 3 +- .../tests/inc/agents_tests/path_test.rs | 79 +-------- .../agents_tests/scenario_processed_test.rs | 25 +++ .../tests/inc/agents_tests/test_scenarios.rs | 23 +++ 10 files changed, 218 insertions(+), 264 deletions(-) create mode 100644 module/move/assistant/src/agents/scenario_processed.rs create mode 100644 module/move/assistant/tests/inc/agents_tests/scenario_processed_test.rs diff --git a/module/move/assistant/Cargo.toml b/module/move/assistant/Cargo.toml index 1031eed817..50700f8c3c 100644 --- a/module/move/assistant/Cargo.toml +++ b/module/move/assistant/Cargo.toml @@ -57,7 +57,6 @@ serde_with = "3.11.0" error_tools = "0.17.0" derive_tools = { version = "0.32.0", features = ["full"] } regex = { version = "1.10.3" } -itertools = "0.13.0" serde_yaml = "0.9" [dev-dependencies] diff --git a/module/move/assistant/src/agents.rs b/module/move/assistant/src/agents.rs index c902864e0a..0f04d8a61c 100644 --- a/module/move/assistant/src/agents.rs +++ b/module/move/assistant/src/agents.rs @@ -11,5 +11,6 @@ crate::mod_interface! layer context; layer scenario_raw; layer scenario_raw_processors; + layer scenario_processed; } \ No newline at end of file diff --git a/module/move/assistant/src/agents/context.rs b/module/move/assistant/src/agents/context.rs index 27969f0ef4..a18a535780 100644 --- a/module/move/assistant/src/agents/context.rs +++ b/module/move/assistant/src/agents/context.rs @@ -9,62 +9,14 @@ mod private use std::collections::HashMap; use crate::*; - use agents::path::Path; - - /// Simplistic in-memory "filesystem". Represents the root of the filesystem. - /// - /// `T` is the type of terminal object. - #[ derive( Debug, Default ) ] - pub struct Context< T > - { - root : ContextDir< T >, - } - - impl< T > Context< T > + use agents::path:: { - /// Create an empty `Context`. - pub fn new() -> Self - { - Self - { - root : ContextDir::new() - } - } - - /// Add new entry to the directory. - /// - /// Returns `true` if entry was successfully added. - /// Returns `false` if there is already and entry with such name. - /// Old entry will not be overriden. - pub fn add( &mut self, name : impl Into< String >, entry : ContextEntry< T > ) -> bool - { - self.root.add( name, entry ) - } - - /// Get an entry by its name. Returns `None` is there is no such entry. - /// - /// `name` must be a valid path item. Refer to `path::PATH_ITEM_REGEX_STR` for syntax. - /// - /// This method is useful for quickly getting an entry only by its name. - /// For complex paths, where your object is located in several consecutives directories, - /// you can use `Path` type and use method `Context::get_by_path`. - pub fn get( &self, name : impl AsRef< str > ) -> Option< &ContextEntry< T > > - { - self.root.get( name ) - } - - /// Get an entry by its path. Returns `None` is there is no such entry. - /// - /// This function can accept absolute `Path`s as `Context` represents the root of the - /// filesystem. - pub fn get_by_path( &self, path : &Path ) -> Option< &ContextEntry< T > > - { - self.root.get_by_path( &path.remove_absolute() ) - } - } + Path, + PATH_SEPARATOR, + }; - /// Represents a directory in `Context` with other directories and - /// terminal objects. + /// Represents a directory in a simplistic in-memory "filesystem" + /// with other directories and terminal objects. /// /// `T` is the type of terminal object. #[ derive( Debug, PartialEq, Clone, Default ) ] @@ -119,14 +71,19 @@ mod private /// Get an entry by its path. Returns `None` is there is no such entry. /// - /// This function does not accept absolute `Path`, as `ContextDir` does not know - /// whether it is root or not. For absolute `Path`s use `Context::get_by_path`. + /// This function accepts both relative and absolute paths and it will + /// treat itself as the root. pub fn get_by_path( &self, path : &Path ) -> Option< &ContextEntry< T > > { let mut cur : Option< &ContextEntry< T > > = None; for component in path.components() { + if component == PATH_SEPARATOR + { + continue; + } + match cur { None => @@ -161,7 +118,7 @@ mod private } } - /// Entry in `Context`: either a directory or a terminal object `T`. + /// Entry in a simplistic in-memory "filesystem": either a directory or a terminal object `T`. /// /// Notice, this struct does not store the name of the entry. #[ derive( Debug, PartialEq, Clone ) ] @@ -187,7 +144,6 @@ crate::mod_interface! { own use { - Context, ContextDir, ContextEntry, }; diff --git a/module/move/assistant/src/agents/path.rs b/module/move/assistant/src/agents/path.rs index 2959e94ea0..d0c29a15dd 100644 --- a/module/move/assistant/src/agents/path.rs +++ b/module/move/assistant/src/agents/path.rs @@ -12,7 +12,12 @@ mod private sync::LazyLock, }; - use itertools::Itertools; + use serde:: + { + Serialize, + Deserialize, + }; + use regex::Regex; /// Path separator string. @@ -24,20 +29,6 @@ mod private /// If you want to match against this expression, use `PATH_ITEM_REGEX`. pub const PATH_ITEM_REGEX_STR : &str = r"[a-zA-Z0-9_ -]+"; - /// Regular expression for `Path` items. You can match whole `&str` with this type. - /// - /// To match whole `Path` in strings, use `PATH_REGEX`. - pub static PATH_ITEM_REGEX : LazyLock< Regex > = LazyLock::new( || - { - let regex = format! - ( - r"^{}$", - PATH_ITEM_REGEX_STR - ); - - Regex::new( ®ex ).unwrap() - }); - /// Regular expression for `Path`. You can match whole `&str` with this type. pub static PATH_REGEX : LazyLock< Regex > = LazyLock::new( || { @@ -56,7 +47,7 @@ mod private /// /// Paths resemble filesystem path, path separator is `::`. /// Absolute path starts with `::`. - #[ derive( Debug, Clone, Eq, PartialEq, Hash ) ] + #[ derive( Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize ) ] pub struct Path( String ); impl Path @@ -67,6 +58,34 @@ mod private #[ inline ] pub fn parent( &self ) -> Option< Path > { + /// Find parent of a `Path`. + /// + /// This method uses `&str` as an argument instead of `Path` + /// in order to be more general and handle trailing `::` case. + fn find_parent( s : &str ) -> Option< &str > + { + s.rfind( PATH_SEPARATOR ) + .map( | sep_pos | + { + if sep_pos == 0 + { + // We found root. We should not return string before `::`, + // as it will be empty. + Some( PATH_SEPARATOR ) + } + else if sep_pos == s.len() - PATH_SEPARATOR.len() + { + // We found trailing `::`. We should continue looking for last separator. + find_parent( &s[ .. sep_pos ] ) + } + else + { + Some( &s[ .. sep_pos ] ) + } + }) + .flatten() + } + find_parent( self.0.as_str() ) .map( | s | Self( s.to_string() ) ) } @@ -83,41 +102,26 @@ mod private self.0.starts_with( PATH_SEPARATOR ) } - /// Turn an absolute `Path` into a relative one by removing leading `::`. - /// - /// If the `Path` is not absolute, a clone will be returned without any - /// changes. - pub fn remove_absolute( &self ) -> Path - { - if self.is_absolute() - { - Self( self.0.strip_prefix( PATH_SEPARATOR ).unwrap_or( "" ).to_string() ) - } - else - { - Self( self.0.clone() ) - } - } - /// Creates an owned `Path` by joining a given path to `self`. /// - /// Returns `Err(io::Error)` is the `path` is an absolute path. + /// If path is joined with an absolute path, then this absolute + /// path will be returned. #[ inline ] - pub fn join( &self, path : &Path ) -> Result< Self, io::Error > + pub fn join( &self, path : &Path ) -> Self { if path.is_absolute() { - Err( io::Error::from( io::ErrorKind::InvalidData ) ) + path.clone() } else { if self.0.ends_with( PATH_SEPARATOR ) { - Ok( Self( format!( "{}{}", self.0, path.0 ) ) ) + Self( format!( "{}{}", self.0, path.0 ) ) } else { - Ok( Self( format!( "{}::{}", self.0, path.0 ) ) ) + Self( format!( "{}::{}", self.0, path.0 ) ) } } } @@ -136,54 +140,6 @@ mod private self.0 } - /// Creates a relative `Path` from an iterator over items that implement `AsRef`. - /// To create an absolute `Path`, use `from_iter_abs` method. - /// - /// Returns `Err(io::Error)` if the items are not valid `Path` items. - pub fn from_iter_rel< 'a >( iter : impl Iterator< Item = &'a str > ) -> Result< Self, io::Error > - { - iter.map( | path_element_str | - { - if PATH_ITEM_REGEX.is_match( path_element_str ) - { - Ok ( path_element_str ) - } - else - { - Err ( io::Error::from( io::ErrorKind::InvalidData ) ) - } - }) - .process_results( | mut item_iter | - { - Self( item_iter.join( PATH_SEPARATOR ) ) - }) - } - - /// Creates an absolute `Path` from an iterator over strings. - /// To create a relative `Path`, use `from_iter_rel` method. - /// - /// Returns `Err(io::Error)` if the items are not valid `Path` items. - pub fn from_iter_abs< 'a >( iter : impl Iterator< Item = &'a str > ) -> Result< Self, io::Error > - { - iter.map( | path_element_str | - { - if PATH_ITEM_REGEX.is_match( path_element_str ) - { - Ok ( path_element_str ) - } - else - { - Err ( io::Error::from( io::ErrorKind::InvalidData ) ) - } - }) - .process_results( | mut item_iter | - { - let mut res = item_iter.join( PATH_SEPARATOR ); - res.insert_str( 0, PATH_SEPARATOR ); - Self( res ) - }) - } - /// Iterate over components of a `Path`. If the `Path` is absolute, then the first /// element will be `::`. pub fn components( &self ) -> impl Iterator< Item = &str > @@ -202,34 +158,6 @@ mod private } } - /// Find parent of a `Path`. - /// - /// This method uses `&str` as an argument instead of `Path` - /// in order to be more general and handle trailing `::` case. - fn find_parent( s : &str ) -> Option< &str > - { - s.rfind( PATH_SEPARATOR ) - .map( | sep_pos | - { - if sep_pos == 0 - { - // We found root. We should not return string before `::`, - // as it will be empty. - Some( PATH_SEPARATOR ) - } - else if sep_pos == s.len() - PATH_SEPARATOR.len() - { - // We found trailing `::`. We should continue looking for last separator. - find_parent( &s[ .. sep_pos ] ) - } - else - { - Some( &s[ .. sep_pos ] ) - } - }) - .flatten() - } - impl fmt::Display for Path { #[ inline ] @@ -305,5 +233,9 @@ mod private crate::mod_interface! { - own use Path; + own use + { + Path, + PATH_SEPARATOR, + }; } \ No newline at end of file diff --git a/module/move/assistant/src/agents/scenario_processed.rs b/module/move/assistant/src/agents/scenario_processed.rs new file mode 100644 index 0000000000..b43e9c0075 --- /dev/null +++ b/module/move/assistant/src/agents/scenario_processed.rs @@ -0,0 +1,99 @@ +//! +//! Scenario representation. Stores parsed representation of templates and paths. +//! This is the type used for running scenarios. +//! +//! For a more simplistic representation use `ScenarioRaw`. +//! + +mod private +{ + use std::collections::HashMap; + + use crate::*; + use agents:: + { + path::Path, + scenario_raw:: + { + ScenarioRaw, + NodeRaw, + }, + }; + + /// New type for templates in scenarios. + #[ derive( Debug, PartialEq ) ] + pub struct TemplateBody( pub String ); + + /// Struct that represents user written scenarios. + /// + /// This is a processed form of a scenario, paths are distinguished here with types. + /// For more simplistic representation of scenarios, use `ScenarioRaw` type. + #[ derive( Debug, PartialEq ) ] + pub struct ScenarioProcessed + { + /// Nodes in the scenario. + pub nodes: Vec< Node >, + } + + impl TryFrom< ScenarioRaw > for ScenarioProcessed + { + type Error = std::io::Error; + + fn try_from( scenario_raw : ScenarioRaw ) -> Result< Self, Self::Error > + { + let nodes : Result< Vec< Node >, Self::Error > = + scenario_raw.nodes.into_iter().map( | rn | Node::try_from( rn ) ).collect(); + + Ok( Self { nodes : nodes? } ) + } + } + + /// Node representation in a scenario file. + /// + /// This is a processed form of a node, paths are distinguished here with types. + /// For more simplistic representation of scenarios, use `NodeRaw` type. + #[ derive( Debug, PartialEq ) ] + pub struct Node + { + /// ID of the node. Must be unique, will also identify node output. + pub id : String, + + /// Type of the node. + pub r#type : Path, + + /// Parameters of the node. + pub params : HashMap< String, String >, + + /// ID of the next node to execute. + pub next : Path, + } + + impl TryFrom< NodeRaw > for Node + { + type Error = std::io::Error; + + fn try_from( node_raw : NodeRaw ) -> Result< Self, Self::Error > + { + Ok + ( + Self + { + id : node_raw.id, + r#type : Path::try_from( node_raw.r#type )?, + params : node_raw.params, + next : Path::try_from( node_raw.next )?, + } + ) + } + } +} + +crate::mod_interface! +{ + own use + { + TemplateBody, + ScenarioProcessed, + Node, + }; +} \ No newline at end of file diff --git a/module/move/assistant/tests/inc/agents_tests/context_test.rs b/module/move/assistant/tests/inc/agents_tests/context_test.rs index 08b0461696..e28fc8c264 100644 --- a/module/move/assistant/tests/inc/agents_tests/context_test.rs +++ b/module/move/assistant/tests/inc/agents_tests/context_test.rs @@ -7,7 +7,6 @@ use the_module::agents:: { ContextDir, ContextEntry, - Context, }, }; @@ -108,12 +107,14 @@ fn context_dir_get_by_path_relative() #[ test ] fn context_dir_get_by_path_absolute() { + let entry = ContextEntry::Terminal( () ); let mut ctx : ContextDir< () > = ContextDir::new(); - ctx.add( "test", ContextEntry::Terminal( () ) ); + ctx.add( "test", entry.clone() ); let res = ctx.get_by_path( &&Path::try_from( "::test" ).unwrap() ); - assert!( res.is_none() ); + assert!( res.is_some() ); + assert_eq!( res.unwrap(), &entry ); } #[ test ] @@ -124,16 +125,4 @@ fn context_dir_get_by_path_non_existing() let res = ctx.get_by_path( &Path::try_from( "test" ).unwrap() ); assert!( res.is_none() ); -} - -#[ test ] -fn context_get_by_path_absolute() -{ - let mut ctx : Context< () > = Context::new(); - let entry = ContextEntry::Terminal( () ); - ctx.add( "test", entry.clone() ); - - let res = ctx.get_by_path( &Path::try_from( "::test" ).unwrap() ); - - assert_eq!( res, Some( &entry ) ); } \ No newline at end of file diff --git a/module/move/assistant/tests/inc/agents_tests/mod.rs b/module/move/assistant/tests/inc/agents_tests/mod.rs index 6c94bd4a2e..f4260d9ed5 100644 --- a/module/move/assistant/tests/inc/agents_tests/mod.rs +++ b/module/move/assistant/tests/inc/agents_tests/mod.rs @@ -5,4 +5,5 @@ mod test_scenarios; mod path_test; mod context_test; mod scenario_raw_test; -mod scenario_raw_processors; \ No newline at end of file +mod scenario_raw_processors; +mod scenario_processed_test; \ No newline at end of file diff --git a/module/move/assistant/tests/inc/agents_tests/path_test.rs b/module/move/assistant/tests/inc/agents_tests/path_test.rs index 78e4132502..277f4965ff 100644 --- a/module/move/assistant/tests/inc/agents_tests/path_test.rs +++ b/module/move/assistant/tests/inc/agents_tests/path_test.rs @@ -135,8 +135,7 @@ fn path_join_relative() let combined = orig_path.join( &append ); - assert!( combined.is_ok() ); - assert_eq!( combined.unwrap().inner(), "agent::completion" ); + assert_eq!( combined.inner(), "agent::completion" ); } #[ test ] @@ -147,7 +146,7 @@ fn path_join_absolute() let combined = orig_path.join( &append ); - assert!( combined.is_err() ); + assert_eq!( combined.inner(), "::completion" ); } #[ test ] @@ -158,8 +157,7 @@ fn path_join_root() let combined = orig_path.join( &append ); - assert!( combined.is_ok() ); - assert_eq!( combined.unwrap().inner(), "::agent" ); + assert_eq!( combined.inner(), "::agent" ); } #[ test ] @@ -170,8 +168,7 @@ fn path_join_trailing() let combined = orig_path.join( &append ); - assert!( combined.is_ok() ); - assert_eq!( combined.unwrap().inner(), "agents::completion" ); + assert_eq!( combined.inner(), "agents::completion" ); } #[ test ] @@ -251,74 +248,6 @@ fn path_inner() assert_eq!( inner, path_str ); } -#[ test ] -fn path_from_iter_right() -{ - let expected = "agents::completion"; - let elements = vec![ "agents", "completion" ]; - - let path = Path::from_iter_rel( elements.into_iter() ); - - assert!( path.is_ok() ); - let path = path.unwrap(); - assert!( path.is_relative() ); - assert_eq!( path.inner(), expected ); -} - -#[ test ] -fn path_from_iter_wrong_item() -{ - let elements = vec![ "agents:", "completion" ]; - - let path = Path::from_iter_rel( elements.into_iter() ); - - assert!( path.is_err() ); -} - -#[ test ] -fn path_from_iter_wrong_separator() -{ - let elements = vec![ "agents", "::", "completion" ]; - - let path = Path::from_iter_rel( elements.into_iter() ); - - assert!( path.is_err() ); -} - -#[ test ] -fn path_from_iter_abs() -{ - let expected = "::agents::completion"; - let elements = vec![ "agents", "completion" ]; - - let path = Path::from_iter_abs( elements.into_iter() ); - - assert!( path.is_ok() ); - let path = path.unwrap(); - assert!( path.is_absolute() ); - assert_eq!( path.inner(), expected ); -} - -#[ test ] -fn path_remove_absolute() -{ - let path = Path::try_from( "::agents::completion" ).unwrap(); - - let got_path = path.remove_absolute(); - - assert_eq!( got_path.inner(), "agents::completion" ); -} - -#[ test ] -fn path_remove_absolute_from_rel() -{ - let path = Path::try_from( "agents::completion" ).unwrap(); - - let got_path = path.remove_absolute(); - - assert_eq!( got_path.inner(), "agents::completion" ); -} - #[ test ] fn path_components() { diff --git a/module/move/assistant/tests/inc/agents_tests/scenario_processed_test.rs b/module/move/assistant/tests/inc/agents_tests/scenario_processed_test.rs new file mode 100644 index 0000000000..5fc734ae41 --- /dev/null +++ b/module/move/assistant/tests/inc/agents_tests/scenario_processed_test.rs @@ -0,0 +1,25 @@ +use super::*; + +use the_module::agents::scenario_processed::ScenarioProcessed; + +use test_scenarios:: +{ + gen_test_scenario_raw, + gen_test_scenario_raw_wrong, +}; + +#[ test ] +fn scenario_processed_right() +{ + let scenario_processed = ScenarioProcessed::try_from( gen_test_scenario_raw() ); + + assert!( scenario_processed.is_ok() ); +} + +#[ test ] +fn scenario_processed_wrong() +{ + let scenario_processed = ScenarioProcessed::try_from( gen_test_scenario_raw_wrong() ); + + assert!( scenario_processed.is_err() ); +} \ No newline at end of file diff --git a/module/move/assistant/tests/inc/agents_tests/test_scenarios.rs b/module/move/assistant/tests/inc/agents_tests/test_scenarios.rs index 84e217b230..02433a68ea 100644 --- a/module/move/assistant/tests/inc/agents_tests/test_scenarios.rs +++ b/module/move/assistant/tests/inc/agents_tests/test_scenarios.rs @@ -6,6 +6,7 @@ use the_module::agents::scenario_raw:: NodeRaw, }; +/// Generates an example `ScenarioRaw`. pub fn gen_test_scenario_raw() -> ScenarioRaw { ScenarioRaw::former() @@ -38,4 +39,26 @@ pub fn gen_test_scenario_raw() -> ScenarioRaw .form(), ] ) .form() +} + +/// Generates a `ScenarioRaw` with wrong syntax for `Path`. +pub fn gen_test_scenario_raw_wrong() -> ScenarioRaw +{ + ScenarioRaw::former() + .nodes( vec! + [ + NodeRaw::former() + .id( "node_1".to_string() ) + .r#type( ":agents:".to_string() ) // This part is incorrect. Path written in wrong syntax. + .params( + { + let mut map : HashMap< String, String > = HashMap::new(); + map.insert( "model".into(), "gpt-4o-mini".into() ); + map + } + ) + .next( "node_2".to_string() ) + .form(), + ] ) + .form() } \ No newline at end of file From 958c5146b76ba6ef7052f4c8f9135c93589c202b Mon Sep 17 00:00:00 2001 From: Ruslan Date: Wed, 4 Dec 2024 10:40:02 +0200 Subject: [PATCH 57/67] Remove doc comment (#1502) --- module/core/process_tools/src/process.rs | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/module/core/process_tools/src/process.rs b/module/core/process_tools/src/process.rs index 4277115676..d11e5c3d16 100644 --- a/module/core/process_tools/src/process.rs +++ b/module/core/process_tools/src/process.rs @@ -223,17 +223,17 @@ mod private run( self.form() ) } - /// Executes an external process using the system shell. - /// - /// This function abstracts over the differences between shells on Windows and Unix-based - /// systems, allowing for a unified interface to execute shell commands. - /// - /// # Parameters: - /// - `exec_path`: The command line string to execute in the shell. - /// - /// # Returns: - /// A `Result` containing a `Report` on success, which includes the command's output, - /// or an error if the command fails to execute or complete. + // Executes an external process using the system shell. + // + // This function abstracts over the differences between shells on Windows and Unix-based + // systems, allowing for a unified interface to execute shell commands. + // + // # Parameters: + // - `exec_path`: The command line string to execute in the shell. + // + // # Returns: + // A `Result` containing a `Report` on success, which includes the command's output, + // or an error if the command fails to execute or complete. // pub fn run_with_shell( self, exec_path : &str, ) -> Result< Report, Report > // { // let ( program, args ) = From 2d5382eba9118c3c2344e9361a5ddd427164d935 Mon Sep 17 00:00:00 2001 From: Ruslan Date: Wed, 4 Dec 2024 10:40:18 +0200 Subject: [PATCH 58/67] Add documentation to `format_tools` (#1501) --- module/core/format_tools/src/format/print.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/module/core/format_tools/src/format/print.rs b/module/core/format_tools/src/format/print.rs index e102eb5372..f1aa104c24 100644 --- a/module/core/format_tools/src/format/print.rs +++ b/module/core/format_tools/src/format/print.rs @@ -229,9 +229,13 @@ mod private #[ derive( Debug, Default ) ] pub struct RowDescriptor { + /// Index of the row. pub irow : usize, + /// Height of the row. pub height : usize, + /// Type of the line: header or regular. pub typ : LineType, + /// Visibility of the row. pub vis : bool, } @@ -240,8 +244,11 @@ mod private #[ derive( Debug, Default ) ] pub struct ColDescriptor< 'label > { + /// Index of the column. pub icol : usize, + /// Column width. pub width : usize, + /// Label of the column. pub label : &'label str, } From 7e9afaeae857a5241f237f0326ba2605f248d650 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A1=D0=B5=D0=B2=D0=B0=20=D0=91=D0=B0=D0=BA=D1=83=D1=82?= =?UTF-8?q?=D0=BE=D0=B2?= <122987843+sevabakutov@users.noreply.github.com> Date: Wed, 4 Dec 2024 18:15:06 +0100 Subject: [PATCH 59/67] READY: Fix module/core/process_tools/src/process.rs (#1508) * fix file * fix process file --------- Co-authored-by: Vsevolod --- module/core/process_tools/src/process.rs | 56 ++++++++++++------------ 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/module/core/process_tools/src/process.rs b/module/core/process_tools/src/process.rs index d11e5c3d16..d58e95455a 100644 --- a/module/core/process_tools/src/process.rs +++ b/module/core/process_tools/src/process.rs @@ -54,7 +54,7 @@ mod private // let ( program, args ) = // if cfg!( target_os = "windows" ) // { - // ( "gspread", [ "/C", exec_path ] ) + // ( "cmd", [ "/C", exec_path ] ) // } // else // { @@ -223,33 +223,33 @@ mod private run( self.form() ) } - // Executes an external process using the system shell. - // - // This function abstracts over the differences between shells on Windows and Unix-based - // systems, allowing for a unified interface to execute shell commands. - // - // # Parameters: - // - `exec_path`: The command line string to execute in the shell. - // - // # Returns: - // A `Result` containing a `Report` on success, which includes the command's output, - // or an error if the command fails to execute or complete. - // pub fn run_with_shell( self, exec_path : &str, ) -> Result< Report, Report > - // { - // let ( program, args ) = - // if cfg!( target_os = "windows" ) - // { - // ( "gspread", [ "/C", exec_path ] ) - // } - // else - // { - // ( "sh", [ "-c", exec_path ] ) - // }; - // self - // .args( args.into_iter().map( OsString::from ).collect::< Vec< _ > >() ) - // .bin_path( program ) - // .run() - // } + /// Executes an external process using the system shell. + /// + /// This function abstracts over the differences between shells on Windows and Unix-based + /// systems, allowing for a unified interface to execute shell commands. + /// + /// # Parameters: + /// - `exec_path`: The command line string to execute in the shell. + /// + /// # Returns: + /// A `Result` containing a `Report` on success, which includes the command's output, + /// or an error if the command fails to execute or complete. + pub fn run_with_shell( self, exec_path : &str, ) -> Result< Report, Report > + { + let ( program, args ) = + if cfg!( target_os = "windows" ) + { + ( "cmd", [ "/C", exec_path ] ) + } + else + { + ( "sh", [ "-c", exec_path ] ) + }; + self + .args( args.into_iter().map( OsString::from ).collect::< Vec< _ > >() ) + .bin_path( program ) + .run() + } } /// Process command output. From 8fdf3627a1996fc2021120006801551d23dd2b86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A1=D0=B5=D0=B2=D0=B0=20=D0=91=D0=B0=D0=BA=D1=83=D1=82?= =?UTF-8?q?=D0=BE=D0=B2?= <122987843+sevabakutov@users.noreply.github.com> Date: Wed, 4 Dec 2024 18:15:22 +0100 Subject: [PATCH 60/67] dependecies fix (#1509) Co-authored-by: Vsevolod --- module/move/gspread/Cargo.toml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/module/move/gspread/Cargo.toml b/module/move/gspread/Cargo.toml index 7d75b04122..8d1d86b4a3 100644 --- a/module/move/gspread/Cargo.toml +++ b/module/move/gspread/Cargo.toml @@ -33,7 +33,9 @@ format_tools = { workspace = true, features = ["full"] } reflect_tools = { workspace = true, features = [ "full" ] } clap = { version = "4.5.20", features = ["derive"] } tokio = { version = "1", features = ["full"] } -google-sheets4 = "*" +google-sheets4 = "6.0.0" +hyper-util = "0.1.10" +yup-oauth2 = "11.0.0" pth = "0.21.0" dotenv = "0.15" serde = { version = "1.0.213", features = ["derive"] } From 39907f72cc84627b40f276583d603bd371e4f2f0 Mon Sep 17 00:00:00 2001 From: ".Barsik" Date: Wed, 4 Dec 2024 23:19:54 +0200 Subject: [PATCH 61/67] READY: (wca): qqq (#1492) * wip * wip * wip * done --- module/move/wca/Cargo.toml | 12 +- module/move/wca/Readme.md | 4 +- module/move/wca/examples/wca_custom_error.rs | 43 +++ module/move/wca/examples/wca_fluent.rs | 9 +- module/move/wca/examples/wca_suggest.rs | 13 +- module/move/wca/examples/wca_trivial.rs | 10 +- module/move/wca/src/ca/aggregator.rs | 35 +- module/move/wca/src/ca/executor/context.rs | 6 +- module/move/wca/src/ca/executor/executor.rs | 51 ++- module/move/wca/src/ca/executor/routine.rs | 65 ++-- module/move/wca/src/ca/facade.rs | 345 ------------------ module/move/wca/src/ca/formatter.rs | 1 + module/move/wca/src/ca/grammar/command.rs | 17 +- module/move/wca/src/ca/grammar/dictionary.rs | 7 +- module/move/wca/src/ca/grammar/types.rs | 15 +- module/move/wca/src/ca/help.rs | 14 +- module/move/wca/src/ca/input.rs | 7 +- module/move/wca/src/ca/parser/command.rs | 6 +- module/move/wca/src/ca/parser/parser.rs | 44 ++- module/move/wca/src/ca/tool/table.rs | 11 +- module/move/wca/src/ca/verifier/command.rs | 6 +- module/move/wca/src/ca/verifier/verifier.rs | 111 ++++-- module/move/wca/tests/inc/adapter.rs | 44 --- .../tests/inc/commands_aggregator/basic.rs | 35 +- .../tests/inc/commands_aggregator/callback.rs | 1 + .../wca/tests/inc/commands_aggregator/help.rs | 18 +- .../wca/tests/inc/commands_aggregator/mod.rs | 11 - module/move/wca/tests/inc/executor/command.rs | 24 +- module/move/wca/tests/inc/executor/mod.rs | 13 - module/move/wca/tests/inc/executor/program.rs | 20 +- .../wca/tests/inc/grammar/from_command.rs | 31 +- .../wca/tests/inc/grammar/from_program.rs | 13 +- module/move/wca/tests/inc/grammar/mod.rs | 8 - module/move/wca/tests/inc/grammar/types.rs | 4 +- module/move/wca/tests/inc/mod.rs | 9 +- module/move/wca/tests/inc/parser/command.rs | 1 + module/move/wca/tests/inc/parser/mod.rs | 6 - module/move/wca/tests/inc/parser/program.rs | 1 + 38 files changed, 423 insertions(+), 648 deletions(-) create mode 100644 module/move/wca/examples/wca_custom_error.rs delete mode 100644 module/move/wca/src/ca/facade.rs delete mode 100644 module/move/wca/tests/inc/adapter.rs diff --git a/module/move/wca/Cargo.toml b/module/move/wca/Cargo.toml index da8d1227b6..06791cf645 100644 --- a/module/move/wca/Cargo.toml +++ b/module/move/wca/Cargo.toml @@ -40,12 +40,12 @@ harness = false [dependencies] ## internal -error_tools = { workspace = true, features = [ "default" ] } -strs_tools = { workspace = true, features = [ "default" ] } -mod_interface = { workspace = true, features = [ "default" ] } -iter_tools = { workspace = true, features = [ "default" ] } -former = { workspace = true, features = [ "default" ] } -# xxx : qqq : optimize set of features +error_tools = { workspace = true, features = [ "enabled", "error_typed", "error_untyped" ] } +mod_interface = { workspace = true, features = [ "enabled" ] } +iter_tools = { workspace = true, features = [ "enabled" ] } +former = { workspace = true, features = [ "enabled", "derive_former" ] } +# xxx : aaa : optimize set of features +# aaa : done. ## external log = "0.4" diff --git a/module/move/wca/Readme.md b/module/move/wca/Readme.md index b808fce2bc..ecda885b57 100644 --- a/module/move/wca/Readme.md +++ b/module/move/wca/Readme.md @@ -14,7 +14,7 @@ The tool to make CLI ( commands user interface ). It is able to aggregate extern ```rust #[ cfg( not( feature = "no_std" ) ) ] { - use wca::{ VerifiedCommand, Context, Type }; + use wca::{ VerifiedCommand, Type }; fn main() { @@ -37,7 +37,7 @@ The tool to make CLI ( commands user interface ). It is able to aggregate extern .end() .perform(); - let args = std::env::args().skip( 1 ).collect::< Vec< String > >(); + let args: Vec< String > = std::env::args().skip( 1 ).collect(); ca.perform( args ).unwrap(); } diff --git a/module/move/wca/examples/wca_custom_error.rs b/module/move/wca/examples/wca_custom_error.rs new file mode 100644 index 0000000000..47fef16985 --- /dev/null +++ b/module/move/wca/examples/wca_custom_error.rs @@ -0,0 +1,43 @@ +//! +//! # Handling Errors with CommandsAggregator +//! +//! This module provides an example of how to use `wca::CommandsAggregator` to manage error handling in a command-line interface. The `CommandsAggregator` offers a fluent interface for defining commands and associating them with various error types, making it straightforward to handle and present errors in a structured way. +//! +//! ## Purpose +//! +//! The primary goal of this example is to showcase how `CommandsAggregator` facilitates error handling, whether errors are simple strings, custom typed errors, untyped errors, or errors with additional context. This approach ensures that error management is both consistent and extensible. +//! + +#[ derive( Debug, error_tools::typed::Error )] +enum CustomError +{ + #[ error( "this is typed error" ) ] + TheError, +} + +fn main() -> error_tools::error::untyped::Result< () > +{ + let ca = wca::CommandsAggregator::former() + .command( "error.string" ) + .hint( "Returns error as a string" ) + .routine( || { Err( "this is string error" ) } ) + .end() + .command( "error.typed" ) + .hint( "Returns error as a custom error" ) + .routine( || { Err( CustomError::TheError ) } ) + .end() + .command( "error.untyped" ) + .hint( "Returns error as untyped error" ) + .routine( || { Err( error_tools::error::untyped::format_err!( "this is untyped error" ) ) } ) + .end() + .command( "error.with_context" ) + .hint( "Returns error as untyped error with context" ) + .routine( || { Err( error_tools::error::untyped::format_err!( "this is untyped error" ).context( "with context" ) ) } ) + .end() + .perform(); + + let args: Vec< String > = std::env::args().skip( 1 ).collect(); + () = ca.perform( args )?; + + Ok( () ) +} \ No newline at end of file diff --git a/module/move/wca/examples/wca_fluent.rs b/module/move/wca/examples/wca_fluent.rs index 487d6ee97d..3d5475a481 100644 --- a/module/move/wca/examples/wca_fluent.rs +++ b/module/move/wca/examples/wca_fluent.rs @@ -7,10 +7,10 @@ //! -use wca::{ Context, Handler, Type, VerifiedCommand }; +use wca::{ executor::{ Context, Handler }, Type, VerifiedCommand }; use std::sync::{ Arc, Mutex }; -fn main() +fn main() -> error_tools::error::untyped::Result< () > { let ca = wca::CommandsAggregator::former() @@ -45,7 +45,8 @@ fn main() .end() .perform(); - let args = std::env::args().skip( 1 ).collect::< Vec< String > >(); - ca.perform( args ).unwrap(); + let args: Vec< String > = std::env::args().skip( 1 ).collect(); + ca.perform( args )?; + Ok( () ) } diff --git a/module/move/wca/examples/wca_suggest.rs b/module/move/wca/examples/wca_suggest.rs index 2bb73fa111..b9b54989a8 100644 --- a/module/move/wca/examples/wca_suggest.rs +++ b/module/move/wca/examples/wca_suggest.rs @@ -22,7 +22,7 @@ use wca::{ CommandsAggregator, Type, VerifiedCommand }; -fn main() +fn main() -> error_tools::error::untyped::Result< () > { let ca = CommandsAggregator::former() @@ -34,14 +34,11 @@ fn main() { println!( "= Args\n{:?}\n\n= Properties\n{:?}\n", o.args, o.props ); }) - .end() + .end() .perform(); - let args = std::env::args().skip( 1 ).collect::< Vec< String > >(); - match ca.perform( args.join( " " ) ) - { - Ok( _ ) => {} - Err( err ) => println!( "{err}" ), - }; + let args: Vec< String > = std::env::args().skip( 1 ).collect(); + ca.perform( args.join( " " ) )?; + Ok( () ) } diff --git a/module/move/wca/examples/wca_trivial.rs b/module/move/wca/examples/wca_trivial.rs index c228e6e20a..1df6dec815 100644 --- a/module/move/wca/examples/wca_trivial.rs +++ b/module/move/wca/examples/wca_trivial.rs @@ -16,7 +16,7 @@ fn exit() std::process::exit( 0 ) } -fn main() +fn main() -> error_tools::error::untyped::Result< () > { let ca = CommandsAggregator::former() .command( "exit" ) @@ -33,7 +33,7 @@ fn main() .perform() ; - // aaa : qqq2 : for Bohdan : that should work + // aaa : aaa2 : for Bohdan : that should work // let ca = wca::CommandsAggregator::former() // .command( "echo" ) // .hint( "prints all subjects and properties" ) @@ -50,6 +50,8 @@ fn main() // ca.execute( input ).unwrap(); //aaa: works - let input = std::env::args().skip( 1 ).collect::< Vec< String > >(); - ca.perform( input ).unwrap(); + let input: Vec< String > = std::env::args().skip( 1 ).collect(); + ca.perform( input )?; + + Ok( () ) } diff --git a/module/move/wca/src/ca/aggregator.rs b/module/move/wca/src/ca/aggregator.rs index 60668ad4a0..c8cd532342 100644 --- a/module/move/wca/src/ca/aggregator.rs +++ b/module/move/wca/src/ca/aggregator.rs @@ -3,7 +3,6 @@ mod private use crate::*; use ca:: { - Verifier, Executor, grammar::command:: { @@ -14,15 +13,17 @@ mod private }, help::{ HelpGeneratorFn, HelpGeneratorOptions, HelpVariants }, }; + use verifier::{ Verifier, VerificationError, VerifiedCommand }; + use parser::{ Program, Parser, ParserError }; + use grammar::Dictionary; + use executor::Context; - // qqq : group uses - use std::collections::HashSet; - use std::fmt; + use std:: + { + fmt, + collections::HashSet + }; use former::StoragePreform; - // use wtools:: - // { - // }; - // use wtools::thiserror; use error:: { // Result, @@ -54,11 +55,11 @@ mod private /// source of the program input : String, /// original error - error : wError, + error : ParserError, }, /// This variant represents errors that occur during grammar conversion. #[ error( "Can not identify a command.\nDetails: {0}" ) ] - Verifier( wError ), + Verifier( VerificationError ), /// This variant is used to represent errors that occur during executor conversion. #[ error( "Can not find a routine for a command.\nDetails: {0}" ) ] ExecutorConverter( wError ), @@ -70,14 +71,14 @@ mod private { /// This variant is used to represent validation errors. /// It carries a `ValidationError` payload that provides additional information about the error. - #[ error( "Validation error. {0}" ) ] + #[ error( "Validation error\n{0}" ) ] Validation( ValidationError ), /// This variant represents execution errors. - #[ error( "Execution failed. {0:?}" ) ] + #[ error( "Execution failed\n{0:?}" ) ] Execution( wError ), } - // xxx : qqq : qqq2 : for Bohdan : one level is obviously redundant + // xxx : aaa : aaa2 : for Bohdan : one level is obviously redundant // Program< Namespace< ExecutableCommand_ > > -> Program< ExecutableCommand_ > // aaa : done. The concept of `Namespace` has been removed struct CommandsAggregatorCallback( Box< dyn Fn( &str, &Program< VerifiedCommand > ) > ); @@ -283,7 +284,7 @@ mod private callback.0( &program.join( " " ), &grammar_program ) } - self.executor.program( &self.dictionary, grammar_program ).map_err( | e | Error::Execution( e ) ) + self.executor.program( &self.dictionary, grammar_program ).map_err( | e | Error::Execution( e.into() ) ) } } } @@ -293,8 +294,8 @@ mod private crate::mod_interface! { exposed use CommandsAggregator; - exposed use CommandsAggregatorFormer; - exposed use Error; - exposed use ValidationError; + orphan use CommandsAggregatorFormer; + orphan use Error; + orphan use ValidationError; exposed use Order; } diff --git a/module/move/wca/src/ca/executor/context.rs b/module/move/wca/src/ca/executor/context.rs index df60994a23..716bbafda6 100644 --- a/module/move/wca/src/ca/executor/context.rs +++ b/module/move/wca/src/ca/executor/context.rs @@ -7,7 +7,7 @@ mod private /// # Examples: /// /// ``` - /// # use wca::{ Routine, Handler, Context, Value, Args, Props, VerifiedCommand }; + /// # use wca::{ executor::{ Routine, Handler, Args, Props, Context }, Value, VerifiedCommand }; /// # use std::sync::{ Arc, Mutex }; /// let routine = Routine::from( Handler::from /// ( @@ -33,7 +33,7 @@ mod private /// } /// assert_eq!( 1, *ctx.get::< Mutex< i32 > >().unwrap().lock().unwrap() ); /// ``` - // qqq : ? + // xxx clarification is needed qqq : поточнити #[ derive( Debug, Clone ) ] pub struct Context { @@ -91,5 +91,5 @@ mod private crate::mod_interface! { - exposed use Context; + orphan use Context; } diff --git a/module/move/wca/src/ca/executor/executor.rs b/module/move/wca/src/ca/executor/executor.rs index 22e44fc19b..9d662801a8 100644 --- a/module/move/wca/src/ca/executor/executor.rs +++ b/module/move/wca/src/ca/executor/executor.rs @@ -2,13 +2,23 @@ mod private { use crate::*; - // use wtools::error::Result; - // use error::return_err; use ca::help::{ HelpGeneratorOptions, generate_help_content, LevelOfDetail }; + use verifier::VerifiedCommand; + use parser::Program; + use grammar::Dictionary; + use executor::{ Routine, Context }; // aaa : for Bohdan : how is it useful? where is it used? // aaa : `ExecutorType` has been removed + #[ derive( Debug, error::typed::Error ) ] + pub enum CommandError + { + #[ error( "Internal command: `.{}` failed with: {}", command.phrase, error ) ] + Internal { command: VerifiedCommand, error: InternalCommandError }, + #[ error( "Command: `.{}` failed with: {}", command.phrase, error ) ] + User { command: VerifiedCommand, error: error::untyped::Error }, + } /// Executor that is responsible for executing the program's commands. /// It uses the given `Context` to store and retrieve values during runtime. @@ -36,9 +46,10 @@ mod private /// /// A `Result` with `Ok( () )` if the execution was successful, or an `Err` containing an error message if an error occurred. /// - // qqq : use typed error + // aaa : use typed error + // aaa : done pub fn program( &self, dictionary : &Dictionary, program : Program< VerifiedCommand > ) - -> error::untyped::Result< () > + -> Result< (), CommandError > { for command in program.commands { @@ -60,18 +71,21 @@ mod private /// # Returns /// /// Returns a Result indicating success or failure. If successful, returns `Ok(())`, otherwise returns an error. - // qqq : use typed error + // aaa : use typed error + // aaa : done pub fn command( &self, dictionary : &Dictionary, command : VerifiedCommand ) - -> error::untyped::Result< () > + -> Result< (), CommandError > { if command.internal_command { - _exec_internal_command( dictionary, command ) + _exec_internal_command( dictionary, command.clone() ) + .map_err( | error | CommandError::Internal { command, error } ) } else { let routine = dictionary.command( &command.phrase ).unwrap().routine.clone(); - _exec_command( command, routine, self.context.clone() ) + _exec_command( command.clone(), routine, self.context.clone() ) + .map_err( | error | CommandError::User { command, error } ) } } @@ -80,6 +94,7 @@ mod private } // qqq : use typed error + // aaa : should it be typed? it is user command with unknown error type fn _exec_command( command : VerifiedCommand, routine : Routine, ctx : Context ) -> error::untyped::Result< () > { @@ -90,9 +105,19 @@ mod private } } - // qqq : use typed error + #[ derive( Debug, error::typed::Error ) ] + pub enum InternalCommandError + { + #[ error( "Encountered an unrecognized internal command: `.{user_input}`." ) ] + UnknownInternalCommand { user_input: String }, + #[ error( "Not found command that starts with `.{user_input}`." ) ] + CommandNotFound { user_input: String }, + } + + // aaa : use typed error + // aaa : done fn _exec_internal_command( dictionary : &Dictionary, command : VerifiedCommand ) - -> error::untyped::Result< () > + -> Result< (), InternalCommandError > { match command.phrase.as_str() { @@ -122,7 +147,7 @@ mod private let commands = dictionary.search( name.strip_prefix( '.' ).unwrap_or( name ) ); if commands.is_empty() { - error::untyped::return_err!( "Not found command that starts with `.{}`.", name ); + return Err( InternalCommandError::CommandNotFound { user_input : name.into() } ); } let generator_args = HelpGeneratorOptions::former() .command_prefix( "." ) @@ -151,10 +176,10 @@ mod private } else { - error::untyped::return_err!( "Not found command that starts with `.{}`.", name ); + return Err( InternalCommandError::CommandNotFound { user_input : name.into() } ); } } - unexpected => error::untyped::return_err!( "Encountered an unrecognized internal command: `.{}`.", unexpected ), + unexpected => return Err( InternalCommandError::UnknownInternalCommand { user_input: unexpected.into() }), } Ok( () ) diff --git a/module/move/wca/src/ca/executor/routine.rs b/module/move/wca/src/ca/executor/routine.rs index 45fc96bed1..ad9a455052 100644 --- a/module/move/wca/src/ca/executor/routine.rs +++ b/module/move/wca/src/ca/executor/routine.rs @@ -2,13 +2,17 @@ mod private { use crate::*; - // qqq : group + // aaa : group + // aaa : done - use std::collections::HashMap; - // use wtools::error::Result; - - use std::{ fmt::Formatter, rc::Rc }; - // use wtools::anyhow::anyhow; + use std:: + { + collections::HashMap, + fmt::Formatter, + rc::Rc, + }; + use verifier::VerifiedCommand; + use executor::Context; /// Command Args /// @@ -17,7 +21,7 @@ mod private /// # Example: /// /// ``` - /// use wca::{ Args, Value }; + /// use wca::{ executor::Args, Value }; /// /// let args = Args( vec![ Value::String( "Hello, World!".to_string() ) ] ); /// @@ -30,7 +34,7 @@ mod private /// /// ## Use case /// ``` - /// # use wca::{ Routine, Handler, VerifiedCommand }; + /// # use wca::{ executor::{ Routine, Handler }, VerifiedCommand }; /// let routine = Routine::from( Handler::from /// ( /// | o : VerifiedCommand | @@ -47,7 +51,7 @@ mod private /// Returns owned casted value by its index /// /// ``` - /// # use wca::{ Args, Value }; + /// # use wca::{ executor::Args, Value }; /// /// let args = Args( vec![ Value::String( "Hello, World!".to_string() ) ] ); /// @@ -79,7 +83,7 @@ mod private /// # Example: /// /// ``` - /// use wca::{ Props, Value }; + /// use wca::{ executor::Props, Value }; /// /// let props = Props( [ ( "hello".to_string(), Value::String( "World!".to_string() ) ) ].into() ); /// let hello_prop : &str = props.get_owned( "hello" ).unwrap(); @@ -89,7 +93,7 @@ mod private /// /// ## Use case /// ``` - /// # use wca::{ Routine, Handler, Props, VerifiedCommand }; + /// # use wca::{ executor::{ Routine, Handler, Props }, VerifiedCommand }; /// let routine = Routine::from( Handler::from /// ( /// | o : VerifiedCommand | @@ -106,7 +110,7 @@ mod private /// Returns owned casted value by its key /// /// ``` - /// # use wca::{ Props, Value }; + /// # use wca::{ executor::Props, Value }; /// /// let props = Props( [ ( "hello".to_string(), Value::String( "World!".to_string() ) ) ].into() ); /// let hello_prop : &str = props.get_owned( "hello" ).unwrap(); @@ -132,7 +136,10 @@ mod private // aaa : done. now it works with the following variants: // fn(), fn(args), fn(props), fn(args, props), fn(context), fn(context, args), fn(context, props), fn(context, args, props) - // qqq : why not public? + // aaa : why not public? // aaa : described + + // These type aliases are kept private to hide implementation details and prevent misuse. + // Exposing them would risk complicating the API and limit future refactoring flexibility. type RoutineWithoutContextFn = dyn Fn( VerifiedCommand ) -> error::untyped::Result< () >; type RoutineWithContextFn = dyn Fn( Context, VerifiedCommand ) -> error::untyped::Result< () >; @@ -140,7 +147,7 @@ mod private /// Routine handle. /// /// ``` - /// # use wca::{ Handler, Routine }; + /// # use wca::executor::{ Handler, Routine }; /// let routine = Routine::from( Handler::from /// ( /// || @@ -151,7 +158,7 @@ mod private /// ``` /// /// ``` - /// # use wca::{ Handler, Routine, VerifiedCommand }; + /// # use wca::{ executor::{ Handler, Routine }, VerifiedCommand }; /// let routine = Routine::from( Handler::from /// ( /// | o : VerifiedCommand | @@ -162,7 +169,7 @@ mod private /// ``` /// /// ``` - /// # use wca::{ Handler, Routine }; + /// # use wca::executor::{ Handler, Routine }; /// let routine = Routine::from( Handler::from /// ( /// | ctx, o | @@ -243,7 +250,7 @@ mod private /// /// - `WithoutContext`: A routine that does not require any context. /// - `WithContext`: A routine that requires a context. -// qqq : for Bohdan : instead of array of Enums, lets better have 5 different arrays of different Routine and no enum +// xxx clarification is needed : for Bohdan : instead of array of Enums, lets better have 5 different arrays of different Routine and no enum // to use statical dispatch #[ derive( Clone ) ] pub enum Routine @@ -327,15 +334,25 @@ mod private } // xxx + // aaa : This is an untyped error because we want to provide a common interface for all commands, while also allowing users to propagate their own specific custom errors. impl IntoResult for std::convert::Infallible { fn into_result( self ) -> error::untyped::Result< () > { Ok( () ) } } impl IntoResult for () { fn into_result( self ) -> error::untyped::Result< () > { Ok( () ) } } - impl< E : std::fmt::Debug > IntoResult + impl< E : std::fmt::Debug + std::fmt::Display + 'static > IntoResult for error::untyped::Result< (), E > { fn into_result( self ) -> error::untyped::Result< () > { - self.map_err( | e | error::untyped::format_err!( "{e:?}" )) - // xxx : qqq : ? + use std::any::TypeId; + // if it's anyhow error we want to have full context(debug), and if it's not(this error) we want to display + if TypeId::of::< error::untyped::Error >() == TypeId::of::< E >() + { + self.map_err( | e | error::untyped::format_err!( "{e:?}" )) + } + else + { + self.map_err( | e | error::untyped::format_err!( "{e}" )) + } + // xxx : aaa : ? } } } @@ -344,8 +361,8 @@ mod private crate::mod_interface! { - exposed use Routine; - exposed use Handler; - exposed use Args; - exposed use Props; + orphan use Routine; + orphan use Handler; + orphan use Args; + orphan use Props; } diff --git a/module/move/wca/src/ca/facade.rs b/module/move/wca/src/ca/facade.rs deleted file mode 100644 index 80fca20afc..0000000000 --- a/module/move/wca/src/ca/facade.rs +++ /dev/null @@ -1,345 +0,0 @@ -// mod private -// { -// use crate::*; -// use core::fmt; -// use ca::grammar; -// -// /// Macro for parsing WCA arguments. -// /// -// /// # Examples -// /// ```rust -// /// use wca::Value; -// /// -// /// let mut args = vec![ Value::Number( 42. ), Value::String( "Rust".into() ) ].into_iter(); -// /// wca::parse_args!( args, n : f64, name : String ); -// /// -// /// assert_eq!( n, 42. ); -// /// assert_eq!( name, "Rust" ); -// /// ``` -// #[macro_export] -// macro_rules! parse_args -// { -// ( $args : ident, mut $b : ident : $ty : ident $( $rest : tt )* ) => -// { -// let mut $b : $ty = std::convert::TryFrom::try_from( $args.next().unwrap() ).unwrap(); -// $crate::parse_args!( $args $( $rest )* ) -// }; -// ( $args : ident, $b : ident : $ty : ident $( $rest : tt )* ) => -// { -// let $b : $ty = std::convert::TryFrom::try_from( $args.next().unwrap() ).unwrap(); -// $crate::parse_args!( $args $( $rest )* ) -// }; -// ( $args : ident, $b : ident $( $rest : tt )* ) => -// { -// let $b = $args.next().unwrap(); -// $crate::parse_args!( $args $( $rest )* ) -// }; -// ( $args : ident, mut $b : ident $( $rest : tt )* ) => -// { -// let mut $b = $args.next().unwrap(); -// $crate::parse_args!( $args $( $rest )* ) -// }; -// ( $args : ident ) => -// { -// assert!( $args.next().is_none() ); -// }; -// ( $args : ident, ) => -// { -// $crate::parse_args!( $args ) -// }; -// } -// -// /// Creates a command-line interface (CLI) builder with the given initial state. -// /// -// /// This function initializes a `CommandBuilder` with the provided `state` and -// /// returns it for further configuration of the CLI. -// pub fn cui< T >( state : T ) -> CommandBuilder< T > -// { -// CommandBuilder::with_state( state ) -// } -// -// /// A struct representing a property. -// #[ derive( Debug, Clone ) ] -// pub struct Property< 'a > -// { -// /// The name of the property. -// pub name : &'a str, -// /// The hint for the property. -// pub debug : &'a str, -// /// The tag representing the property's type. -// pub tag : Type, -// } -// -// impl< 'a > Property< 'a > -// { -// /// Constructor of a property. -// pub fn new( name : &'a str, hint : &'a str, tag : Type ) -> Self { Self { name, hint, tag } } -// } -// -// /// A builder struct for constructing commands. -// #[ derive( Debug ) ] -// pub struct CommandBuilder< T > -// { -// state : T, -// commands : Vec< Command >, -// handlers : std::collections::HashMap< String, Routine >, -// } -// -// impl< T > CommandBuilder< T > -// { -// /// Constructs a `CommandBuilder` with the given state. -// pub fn with_state( state : T ) -> Self -// { -// Self { state, handlers : < _ >::default(), commands : vec![] } -// } -// } -// -// #[ derive( Debug ) ] -// pub struct Builder< F > -// { -// handler : F, -// command : Command, -// } -// -// impl< F > Builder< F > -// { -// /// Creates a new instance of the command with the provided handler function. -// /// -// /// This method takes in a handler function `handler` and creates a new instance of the command. -// /// The `handler` function is used to handle the execution logic associated with the command. -// /// -// /// # Arguments -// /// -// /// * `handler` - The handler function that will be invoked when the command is executed. -// /// -// /// # Returns -// /// -// /// A new instance of the command with the specified `handler`. -// /// -// #[ inline ] -// pub fn new( handler: F ) -> Self -// { -// let name = -// { -// use iter_tools::Itertools as _; -// -// let name = std::any::type_name::< F >(); -// let name = name.split("::").last().unwrap(); -// name.split( '_' ).join( "." ) -// }; -// -// Self { handler, command : Command::former().phrase( name ).form() } -// } -// -// /// Adds an argument to the command. -// /// -// /// This method takes in the `hint` and `tag` parameters to create a `ValueDescription` object -// /// representing an argument. The `ValueDescription` object is then appended to the command's -// /// `subjects` collection. -// /// -// /// # Arguments -// /// -// /// * `hint` - The hint for the argument, represented as a string slice (`&str`). -// /// * `tag` - The type of the argument, represented by a `Type` object from the `Type` module. -// /// -// /// # Returns -// /// -// /// The modified command instance with the argument added. -// /// -// #[ inline ] -// pub fn arg( mut self, hint : &str, tag : Type ) -> Self -// { -// self.command.subjects.push( grammar::command::ValueDescription -// { -// hint : hint.into(), -// kind : tag, -// optional : false, -// }); -// -// self -// } -// -// /// Adds a property to the command. -// /// -// /// This method takes in the `name`, `hint`, and `kind` parameters to create a `ValueDescription` -// /// object representing a property. The `ValueDescription` object is then inserted into the -// /// command's properties collection using the `name` as the key. -// /// -// /// # Example -// /// ```no_rust -// /// let ca = cui(()) -// /// .command(user.property("name", "Name property", Type::String)) -// /// .build(); -// /// ``` -// /// -// /// # Arguments -// /// -// /// * `name` - The name of the property. It should implement the `ToString` trait. -// /// * `hint` - The hint for the property. It should implement the `ToString` trait. -// /// * `kind` - The type of the property, represented by a `Type` object from the `Type` module. -// /// -// /// # Returns -// /// -// /// The modified command instance with the property added. -// /// -// #[ inline ] -// pub fn property( mut self, name : impl ToString , hint : impl ToString, kind : Type ) -> Self -// { -// self.command.properties.insert -// ( -// name.to_string(), -// grammar::command::ValueDescription -// { -// hint : hint.to_string(), -// kind, -// optional : false, -// } -// ); -// -// self -// } -// -// /// Adds multiple properties to the command. -// /// -// /// This method takes in an array of `Property` objects and adds them to the command's properties. -// /// The properties are provided in the `properties` parameter as an array of length `N`. -// /// -// /// ```without_std -// /// let ca = cui(()) -// /// .properties([ -// /// Property::new("name", "Name property", Type::String), -// /// Property::new("age", "Age property", Type::Integer), -// /// ]).build(); -// /// ``` -// /// -// /// # Arguments -// /// -// /// * `properties` - An array of `Property` objects representing the properties to be added. -// /// -// /// # Returns -// /// -// /// The modified command instance with the properties added. -// /// -// #[ inline ] -// pub fn properties< const N: usize >( mut self, properties : [ Property< '_ >; N ] ) -> Self -// { -// self.command.properties.reserve( properties.len() ); -// -// for Property { name, hint, tag } in properties -// { -// self = self.property(name, hint, tag); -// } -// -// self -// } -// } -// -// impl< T: Clone + 'static > CommandBuilder< T > -// { -// /// Adds a command to the `CommandBuilder`. -// /// ```no_rust -// /// let ca = cui( () ) // Add commands using the builder pattern -// /// .command( command ) -// /// .command( command2 ) -// /// .command( echo.arg("string", Type::String ) ) // Customize your commands by chaining methods such as properties -// /// // property, and arg to add properties and arguments. -// /// .build(); -// /// -// /// ``` -// pub fn command< F, E > -// ( -// mut self, -// command : impl IntoBuilder< F, T >, -// ) -> Self -// where -// F : Fn( T, Args, Props ) -> Result< (), E > + 'static + Copy, -// E : fmt::Debug, -// { -// let Builder { handler, command } = command.into_builder(); -// let state = self.state.clone(); -// -// let closure = closure::closure!( | ( args, props ) | -// { -// handler( state.clone(), args, props ) -// .map_err( | report | BasicError::new( format!( "{report:?}" ) ).into() ) -// }); -// -// let handler = Routine::new( closure ); -// -// self.handlers.insert( command.phrase.clone(), handler ); -// self.commands.push( command ); -// -// self -// } -// -// /// Builds and returns a `wca::CommandsAggregator` instance. -// /// -// /// This method finalizes the construction of the `CommandBuilder` by -// /// creating a `wca::CommandsAggregator` instance with the accumulated -// /// commands and handlers. -// pub fn build( self ) -> CommandsAggregator -// { -// CommandsAggregator::former().grammar( self.commands ).executor( self.handlers ).perform() -// } -// } -// -// /// An extension trait for commands. -// /// -// /// This trait provides additional methods for enhancing commands, such as -// /// adding arguments and properties. -// pub trait CommandExt< T > : Sized -// { -// /// Adds an argument to the command. -// fn arg( self, hint : &str, tag : Type ) -> Builder< Self > -// { -// Builder::new( self ).arg( hint, tag ) -// } -// -// /// Adds property to the command. -// fn property< const N: usize >( self, name : impl ToString , hint : impl ToString, kind : Type ) -> Builder< Self > -// { -// Builder::new( self ).property( name, hint, kind ) -// } -// -// /// Adds properties to the command. -// fn properties< const N: usize >( self, properties: [ Property< '_ >; N ] ) -> Builder< Self > -// { -// Builder::new( self ).properties( properties ) -// } -// } -// -// impl< F: Fn( T, Args, Props ) -> Result< (), E>, T, E > CommandExt< T > for F {} -// -// /// A trait for converting a type into a `Builder`. -// pub trait IntoBuilder< F, T > : Sized -// { -// /// Converts the type into a `Builder` instance. -// fn into_builder( self ) -> Builder< F >; -// } -// -// impl< F, T > IntoBuilder< F, T > for Builder< F > -// { -// fn into_builder( self ) -> Self -// { -// self -// } -// } -// -// impl< F: Fn( T, Args, Props ) -> Result< (), E >, T, E > IntoBuilder< F, T > for F -// { -// fn into_builder( self ) -> Builder< F > -// { -// Builder::new( self ) -// } -// } -// -// } -// -// crate::mod_interface! -// { -// exposed use cui; -// exposed use CommandBuilder; -// exposed use Property; -// prelude use IntoBuilder; -// prelude use CommandExt; -// } diff --git a/module/move/wca/src/ca/formatter.rs b/module/move/wca/src/ca/formatter.rs index 905fed5b2d..30e6787d6d 100644 --- a/module/move/wca/src/ca/formatter.rs +++ b/module/move/wca/src/ca/formatter.rs @@ -4,6 +4,7 @@ mod private use crate::*; use iter_tools::Itertools; use ca::aggregator::Order; + use grammar::Dictionary; /// Enum representing the format options for generating help content. /// diff --git a/module/move/wca/src/ca/grammar/command.rs b/module/move/wca/src/ca/grammar/command.rs index ad34d5ef85..0c17e726b1 100644 --- a/module/move/wca/src/ca/grammar/command.rs +++ b/module/move/wca/src/ca/grammar/command.rs @@ -2,10 +2,11 @@ mod private { use crate::*; - use std::collections::{ HashMap }; + use std::collections::HashMap; use indexmap::IndexMap; use former::{ Former, StoragePreform }; use iter_tools::Itertools; + use executor::{ Routine, Handler }; /// A description of a Value in a command. Used to specify the expected type and provide a hint for the Value. /// @@ -35,7 +36,7 @@ mod private pub struct PropertyDescription { name : String, - // qqq : how to re-use ValueDescriptionFormer without additional end? + // xxx : how to re-use ValueDescriptionFormer without additional end? // #[subform_scalar] // value : ValueDescription, /// providing guidance to the user for entering a valid value @@ -74,7 +75,7 @@ mod private /// # Example: /// /// ``` - /// # use wca::{ Command, Type }; + /// # use wca::{ grammar::Command, Type }; /// let command = Command::former() /// .hint( "hint" ) /// .long_hint( "long_hint" ) @@ -103,7 +104,8 @@ mod private /// Map of aliases. // Aliased key -> Original key pub properties_aliases : HashMap< String, String >, - // qqq : make it usable and remove default(?) + // aaa : make it usable and remove default(?) + // aaa : it is usable /// The type `Routine` represents the specific implementation of the routine. #[ scalar( setter = false ) ] #[ former( default = Routine::from( Handler::< _, std::convert::Infallible >::from( || { panic!( "No routine available: A handler function for the command is missing" ) } ) ) ) ] @@ -246,8 +248,8 @@ mod private crate::mod_interface! { - exposed use Command; - exposed use CommandFormer; + orphan use Command; + orphan use CommandFormer; own use ValueDescription; own use CommandAsSubformer; @@ -256,4 +258,5 @@ crate::mod_interface! } -// qqq : use orphan instead of exposed for ALL files in the folder, dont use prelude for structs \ No newline at end of file +// aaa : use orphan instead of exposed for ALL files in the folder, dont use prelude for structs +// aaa : done. \ No newline at end of file diff --git a/module/move/wca/src/ca/grammar/dictionary.rs b/module/move/wca/src/ca/grammar/dictionary.rs index e6887aef26..8dc784f3db 100644 --- a/module/move/wca/src/ca/grammar/dictionary.rs +++ b/module/move/wca/src/ca/grammar/dictionary.rs @@ -4,8 +4,9 @@ mod private use former::Former; use indexmap::IndexMap; use iter_tools::Itertools; + use grammar::Command; - // qqq : `Former` does not handle this situation well + // xxx : `Former` does not handle this situation well // /// A collection of commands. // /// @@ -25,8 +26,6 @@ mod private pub( crate ) order : Order, } - // qqq : IDK how to integrate it into the `CommandsAggregatorFormer` - // impl DictionaryFormer { pub fn command( mut self, command : Command ) -> Self @@ -109,5 +108,5 @@ mod private crate::mod_interface! { - exposed use Dictionary; + orphan use Dictionary; } diff --git a/module/move/wca/src/ca/grammar/types.rs b/module/move/wca/src/ca/grammar/types.rs index 2141ca335b..99526b35dd 100644 --- a/module/move/wca/src/ca/grammar/types.rs +++ b/module/move/wca/src/ca/grammar/types.rs @@ -6,9 +6,6 @@ mod private Display, Formatter }; - // use wtools; - // use wtools::{ error::Result, err }; - // use error::err; use iter_tools::Itertools; /// Available types that can be converted to a `Value` @@ -59,7 +56,7 @@ mod private /// # Example: /// /// ``` - /// # use wca::{ VerifiedCommand, Value, Args, Props }; + /// # use wca::{ VerifiedCommand, Value, executor::{ Args, Props } }; /// # use std::collections::HashMap; /// let command = VerifiedCommand /// { @@ -119,7 +116,7 @@ mod private } Value::List( list ) => { - let list = list.iter().map( | element | element.to_string() ).join( "," ); // qqq : don't hardcode ", " find way to get original separator + let list = list.iter().map( | element | element.to_string() ).join( "," ); write!( f, "{list}" )?; } } @@ -191,11 +188,13 @@ mod private Self::Bool => Ok( Value::Bool( match value.as_str() { "1" | "true" => true, "0" | "false" => false, _ => return Err( error::untyped::format_err!( "Can not parse bool from `{}`", value ) ) } ) ), Self::List( kind, delimeter ) => { - let values = value + let values: error::untyped::Result< Vec< Value > > = value .split( *delimeter ) .map( | val | kind.try_cast( val.into() ) ) - .collect::< error::untyped::Result< Vec< Value > > >()?; - // qqq : avoid using fish notation whenever possible. review whole crate + .collect(); + let values = values?; + // aaa : avoid using fish notation whenever possible. review whole crate + // aaa : done Ok( Value::List( values ) ) }, } diff --git a/module/move/wca/src/ca/help.rs b/module/move/wca/src/ca/help.rs index a2a7129f49..6a40e28e1c 100644 --- a/module/move/wca/src/ca/help.rs +++ b/module/move/wca/src/ca/help.rs @@ -3,8 +3,6 @@ mod private use crate::*; use ca:: { - Command, - Routine, Type, formatter:: { @@ -13,13 +11,17 @@ mod private }, tool::table::format_table, }; + use verifier::VerifiedCommand; + use grammar::{ Command, Dictionary }; + use executor::Routine; use iter_tools::Itertools; use std::rc::Rc; use error::untyped::format_err; use former::Former; - // qqq : for Bohdan : it should transparent mechanist which patch list of commands, not a stand-alone mechanism + // aaa : for Bohdan : it should transparent mechanist which patch list of commands, not a stand-alone mechanism + // aaa : it is /// Enum `LevelOfDetail` specifies the granularity of detail for rendering or processing: #[ derive( Debug, Default, Copy, Clone, PartialEq, Eq ) ] @@ -67,7 +69,9 @@ mod private pub order : Order, } - // qqq : for Barsik : make possible to change properties order + // aaa : for Barsik : make possible to change properties order + // aaa : order option + /// Generates help content as a formatted string based on a given dictionary and options. /// /// This function takes a `Dictionary` of terms or commands and a `HelpGeneratorOptions` @@ -366,7 +370,7 @@ mod private /// /// ``` /// # use wca::ca::help::{ HelpGeneratorOptions, HelpGeneratorFn }; - /// use wca::{ Command, Dictionary }; + /// use wca::grammar::{ Command, Dictionary }; /// /// fn my_help_generator( dictionary : &Dictionary, args : HelpGeneratorOptions< '_ > ) -> String /// { diff --git a/module/move/wca/src/ca/input.rs b/module/move/wca/src/ca/input.rs index c2826f99ef..46f70221b8 100644 --- a/module/move/wca/src/ca/input.rs +++ b/module/move/wca/src/ca/input.rs @@ -1,7 +1,6 @@ mod private { - use std::io; - use std::io::Write; + use std::io::{ self, Write }; /// Ask use input from standard input. pub fn ask( request : &str ) -> String @@ -78,6 +77,6 @@ mod private crate::mod_interface! { exposed use ask; - exposed use Input; - exposed use IntoInput; + orphan use Input; + orphan use IntoInput; } diff --git a/module/move/wca/src/ca/parser/command.rs b/module/move/wca/src/ca/parser/command.rs index 332c9e71f6..84cfbefc2b 100644 --- a/module/move/wca/src/ca/parser/command.rs +++ b/module/move/wca/src/ca/parser/command.rs @@ -25,7 +25,7 @@ mod private /// # Example: /// /// ``` - /// # use wca::ParsedCommand; + /// # use wca::parser::ParsedCommand; /// # use std::collections::HashMap; /// ParsedCommand /// { @@ -57,6 +57,6 @@ mod private crate::mod_interface! { - exposed use Program; - exposed use ParsedCommand; + orphan use Program; + orphan use ParsedCommand; } diff --git a/module/move/wca/src/ca/parser/parser.rs b/module/move/wca/src/ca/parser/parser.rs index 4a23f24db3..0fec9fb6f4 100644 --- a/module/move/wca/src/ca/parser/parser.rs +++ b/module/move/wca/src/ca/parser/parser.rs @@ -3,9 +3,20 @@ mod private use crate::*; use std::collections::HashMap; + use parser::{ Program, ParsedCommand }; // use error::{ return_err }; + #[ allow( missing_docs ) ] + #[ derive( Debug, error::typed::Error ) ] + pub enum ParserError + { + #[ error( "Internal Error: {details}" ) ] + InternalError { details: String }, + #[ error( "Unexpected input. Expected: {expected}, found {input}" ) ] + UnexpectedInput { expected: String, input: String }, + } + /// `Parser` is a struct used for parsing data. #[ derive( Debug ) ] pub struct Parser; @@ -21,13 +32,14 @@ mod private /// # Returns /// /// Returns a `Result` with a `Program` containing the parsed commands if successful, or an error if parsing fails. - // qqq : use typed error - pub fn parse< As, A >( &self, args : As ) -> error::untyped::Result< Program< ParsedCommand > > + // aaa : use typed error + // aaa : done. + pub fn parse< As, A >( &self, args : As ) -> Result< Program< ParsedCommand >, ParserError > where As : IntoIterator< Item = A >, A : Into< String >, { - let args = args.into_iter().map( Into::into ).collect::< Vec< _ > >(); + let args: Vec< _ > = args.into_iter().map( Into::into ).collect(); let mut commands = vec![]; let mut i = 0; while i < args.len() @@ -54,18 +66,18 @@ mod private } // returns ParsedCommand and relative position of the last parsed item - // qqq : use typed error - fn parse_command( args : &[ String ] ) -> error::untyped::Result< ( ParsedCommand, usize ) > + // aaa : use typed error + fn parse_command( args : &[ String ] ) -> Result< ( ParsedCommand, usize ), ParserError > { if args.is_empty() { - error::untyped::return_err!( "Unexpected behaviour: Try to parse command without input" ); + return Err( ParserError::InternalError { details: "Try to parse command without input".into() } ); } let mut i = 0; if !Self::valid_command_name( &args[ i ] ) { - error::untyped::return_err!( "Unexpected input: Expected a command, found: `{}`", args[ i ] ); + return Err( ParserError::UnexpectedInput { expected: "command".into(), input: args[ i ].clone() } ); } let name = match args[ i ].strip_prefix( '.' ).unwrap() { @@ -91,8 +103,9 @@ mod private } // returns ( subjects, properties, relative_end_pos ) - // qqq : use typed error - fn parse_command_args( args : &[ String ] ) -> error::untyped::Result< ( Vec< String >, HashMap< String, String >, usize ) > + // aaa : use typed error + // aaa : done + fn parse_command_args( args : &[ String ] ) -> Result< ( Vec< String >, HashMap< String, String >, usize ), ParserError > { let mut i = 0; @@ -125,7 +138,7 @@ mod private // prop: else { - error::untyped::return_err!( "Unexpected input '{}': Detected a possible property key preceding the ':' character. However, no corresponding value was found.", item ); + return Err( ParserError::UnexpectedInput { expected: "property value".into(), input: "end of input".into() } ); } } // prop : value | prop :value @@ -146,11 +159,7 @@ mod private // : else { - error::untyped::return_err! - ( - "Unexpected input '{} :': Detected a possible property key preceding the ':' character. However, no corresponding value was found.", - item, - ); + return Err( ParserError::UnexpectedInput { expected: "property value".into(), input: "end of input".into() } ); } } @@ -160,7 +169,7 @@ mod private } else { - error::untyped::return_err!( "Unexpected input: Expected `command` or `property`, found: `{}`", item ); + return Err( ParserError::UnexpectedInput { expected: "`command` or `property`".into(), input: item.into() } ); } i += 1; } @@ -174,5 +183,6 @@ mod private crate::mod_interface! { - exposed use Parser; + orphan use Parser; + orphan use ParserError; } diff --git a/module/move/wca/src/ca/tool/table.rs b/module/move/wca/src/ca/tool/table.rs index 424356530b..4315ee5c8e 100644 --- a/module/move/wca/src/ca/tool/table.rs +++ b/module/move/wca/src/ca/tool/table.rs @@ -81,6 +81,10 @@ mod private .collect() } + #[ derive( Debug, error::typed::Error ) ] + #[ error( "Invalid table" ) ] + pub struct FormatTableError; + /// Formats a table into a readable string representation. /// /// # Arguments @@ -90,15 +94,16 @@ mod private /// # Returns /// /// * `error::untyped::Result` - A `error::untyped::Result` containing the formatted table as a `String`, or an `Error` if the table is invalid. - // qqq : use typed error - pub fn format_table< IntoTable >( table : IntoTable ) -> error::untyped::Result< String > + // aaa : use typed error + // aaa : done + pub fn format_table< IntoTable >( table : IntoTable ) -> Result< String, FormatTableError > where IntoTable : Into< Table >, { let table = table.into(); if !table.validate() { - return Err( error::untyped::format_err!( "Invalid table" ) ); + return Err( FormatTableError ); } let max_lengths = max_column_lengths( &table ); diff --git a/module/move/wca/src/ca/verifier/command.rs b/module/move/wca/src/ca/verifier/command.rs index ef8c2824b9..c945bbb997 100644 --- a/module/move/wca/src/ca/verifier/command.rs +++ b/module/move/wca/src/ca/verifier/command.rs @@ -1,13 +1,14 @@ mod private { use crate::*; + use executor::{ Args, Props }; /// Represents a grammatically correct command with a phrase descriptor, a list of command subjects, and a set of command options. /// /// # Example: /// /// ``` - /// # use wca::{ VerifiedCommand, Value, Args, Props }; + /// # use wca::{ VerifiedCommand, Value, executor::{ Args, Props } }; /// # use std::collections::HashMap; /// VerifiedCommand /// { @@ -46,4 +47,5 @@ crate::mod_interface! exposed use VerifiedCommand; } -// qqq : use orphan instead of exposed for ALL files in the folder, dont use prelude for structs \ No newline at end of file +// aaa : use orphan instead of exposed for ALL files in the folder, dont use prelude for structs +// aaa : done. \ No newline at end of file diff --git a/module/move/wca/src/ca/verifier/verifier.rs b/module/move/wca/src/ca/verifier/verifier.rs index 0202279546..c0636a594b 100644 --- a/module/move/wca/src/ca/verifier/verifier.rs +++ b/module/move/wca/src/ca/verifier/verifier.rs @@ -2,19 +2,55 @@ mod private { use crate::*; - use ca::grammar::command::ValueDescription; - // use former::Former; + use help::{ HelpGeneratorOptions, LevelOfDetail, generate_help_content }; + use grammar::{ Dictionary, Command, command::ValueDescription }; + use executor::{ Args, Props }; use std::collections::HashMap; use indexmap::IndexMap; - // use wtools::{ error, error::Result, err }; - // use error::err; - use ca::help::{ HelpGeneratorOptions, LevelOfDetail, generate_help_content }; + use verifier::VerifiedCommand; + use parser::{ Program, ParsedCommand }; + + #[ allow( missing_docs ) ] + #[ derive( Debug, error::typed::Error ) ] + pub enum VerificationError + { + #[ error + ( + "Command not found. {} {}", + if let Some( phrase ) = name_suggestion { format!( "Maybe you mean `.{phrase}`?" ) } else { "Please use `.` command to see the list of available commands.".into() }, + if let Some( info ) = command_info { format!( "Command info: `{info}`" ) } else { "".into() } + )] + CommandNotFound { name_suggestion: Option< String >, command_info: Option< String > }, + #[ error( "Fail in command `.{command_name}` while processing subjects. {error}" ) ] + Subject { command_name: String, error: SubjectError }, + #[ error( "Fail in command `.{command_name}` while processing properties. {error}" ) ] + Property { command_name: String, error: PropertyError }, + } + + #[ allow( missing_docs ) ] + #[ derive( Debug, error::typed::Error ) ] + pub enum SubjectError + { + #[ error( "Missing not optional subject" ) ] + MissingNotOptional, + #[ error( "Can not identify a subject: `{value}`" ) ] + CanNotIdentify { value: String }, + } + + #[ allow( missing_docs ) ] + #[ derive( Debug, error::typed::Error ) ] + pub enum PropertyError + { + #[ error( "Expected: {description:?}. Found: {input}" ) ] + Cast { description: ValueDescription, input: String }, + } + // xxx /// Converts a `ParsedCommand` to a `VerifiedCommand` by performing validation and type casting on values. /// /// ``` - /// # use wca::{ Command, Type, Verifier, Dictionary, ParsedCommand }; + /// # use wca::{ Type, verifier::Verifier, grammar::{ Dictionary, Command }, parser::ParsedCommand }; /// # use std::collections::HashMap; /// # fn main() -> Result< (), Box< dyn std::error::Error > > /// # { @@ -48,13 +84,15 @@ mod private dictionary : &Dictionary, raw_program : Program< ParsedCommand > ) - -> error::untyped::Result< Program< VerifiedCommand > > - // qqq : use typed error + -> Result< Program< VerifiedCommand >, VerificationError > + // aaa : use typed error + // aaa : done { - let commands = raw_program.commands + let commands: Result< Vec< VerifiedCommand >, VerificationError > = raw_program.commands .into_iter() .map( | n | self.to_command( dictionary, n ) ) - .collect::< error::untyped::Result< Vec< VerifiedCommand > > >()?; + .collect(); + let commands = commands?; Ok( Program { commands } ) } @@ -109,14 +147,15 @@ mod private if Self::is_valid_command_variant( expected_subjects_count, raw_subjects_count, possible_subjects_count ) { Some( variant ) } else { None } } - // qqq : use typed error + // aaa : use typed error + // aaa : done. fn extract_subjects( command : &Command, raw_command : &ParsedCommand, used_properties : &[ &String ] ) -> - error::untyped::Result< Vec< Value > > + Result< Vec< Value >, SubjectError > { let mut subjects = vec![]; - let all_subjects = raw_command + let all_subjects: Vec< _ > = raw_command .subjects.clone().into_iter() .chain ( @@ -124,7 +163,7 @@ mod private .filter( |( key, _ )| !used_properties.contains( key ) ) .map( |( key, value )| format!( "{key}:{value}" ) ) ) - .collect::< Vec< _ > >(); + .collect(); let mut rc_subjects_iter = all_subjects.iter(); let mut current = rc_subjects_iter.next(); @@ -134,20 +173,21 @@ mod private { Some( v ) => v, None if *optional => continue, - _ => return Err( error::untyped::format_err!( "Missing not optional subject" ) ), + _ => return Err( SubjectError::MissingNotOptional ), }; subjects.push( value ); current = rc_subjects_iter.next(); } - if let Some( value ) = current { return Err( error::untyped::format_err!( "Can not identify a subject: `{}`", value ) ) } + if let Some( value ) = current { return Err( SubjectError::CanNotIdentify { value: value.clone() } ) } Ok( subjects ) } - // qqq : use typed error + // aaa : use typed error + // aaa : done. fn extract_properties( command: &Command, raw_command : HashMap< String, String > ) -> - error::untyped::Result< HashMap< String, Value > > + Result< HashMap< String, Value >, PropertyError > { raw_command.into_iter() .filter_map @@ -163,9 +203,9 @@ mod private .map ( |( value_description, key, value )| - value_description.kind.try_cast( value ).map( | v | ( key.clone(), v ) ) + value_description.kind.try_cast( value.clone() ).map( | v | ( key.clone(), v ) ).map_err( | _ | PropertyError::Cast { description: value_description.clone(), input: format!( "{key}: {value}" ) } ) ) - .collect::< error::untyped::Result< HashMap< _, _ > > >() + .collect() } fn group_properties_and_their_aliases< 'a, Ks >( aliases : &'a HashMap< String, String >, used_keys : Ks ) -> Vec< &String > @@ -186,16 +226,17 @@ mod private { reverse_aliases.get( key ).into_iter().flatten().map( | k | *k ).chain( Some( key ) ) }) - .collect::< Vec< _ > >() + .collect() } /// Converts raw command to grammatically correct /// /// Make sure that this command is described in the grammar and matches it(command itself and all it options too). - // qqq : use typed error + // aaa : use typed error + // aaa : done. pub fn to_command( &self, dictionary : &Dictionary, raw_command : ParsedCommand ) -> - error::untyped::Result< VerifiedCommand > + Result< VerifiedCommand, VerificationError > { if raw_command.name.ends_with( '.' ) | raw_command.name.ends_with( ".?" ) { @@ -208,32 +249,31 @@ mod private }); } let command = dictionary.command( &raw_command.name ) - .ok_or_else::< error::untyped::Error, _ > + .ok_or_else::< VerificationError, _ > ( || { #[ cfg( feature = "on_unknown_suggest" ) ] if let Some( phrase ) = Self::suggest_command( dictionary, &raw_command.name ) { - return error::untyped::format_err!( "Command not found. Maybe you mean `.{}`?", phrase ) + return VerificationError::CommandNotFound { name_suggestion: Some( phrase ), command_info: None }; } - error::untyped::format_err!( "Command not found. Please use `.` command to see the list of available commands." ) + VerificationError::CommandNotFound { name_suggestion: None, command_info: None } } )?; let Some( cmd ) = Self::check_command( command, &raw_command ) else { - error::untyped::bail! - ( - "`{}` command with specified subjects not found. Command info: `{}`", - &raw_command.name, - generate_help_content( dictionary, HelpGeneratorOptions::former().for_commands([ dictionary.command( &raw_command.name ).unwrap() ]).command_prefix( "." ).subject_detailing( LevelOfDetail::Detailed ).form() ).strip_suffix( " " ).unwrap() - ); + return Err( VerificationError::CommandNotFound + { + name_suggestion: Some( command.phrase.clone() ), + command_info: Some( generate_help_content( dictionary, HelpGeneratorOptions::former().for_commands([ dictionary.command( &raw_command.name ).unwrap() ]).command_prefix( "." ).subject_detailing( LevelOfDetail::Detailed ).form() ).strip_suffix( " " ).unwrap().into() ), + } ); }; - let properties = Self::extract_properties( cmd, raw_command.properties.clone() )?; + let properties = Self::extract_properties( cmd, raw_command.properties.clone() ).map_err( | e | VerificationError::Property { command_name: cmd.phrase.clone(), error: e } )?; let used_properties_with_their_aliases = Self::group_properties_and_their_aliases( &cmd.properties_aliases, properties.keys() ); - let subjects = Self::extract_subjects( cmd, &raw_command, &used_properties_with_their_aliases )?; + let subjects = Self::extract_subjects( cmd, &raw_command, &used_properties_with_their_aliases ).map_err( | e | VerificationError::Subject { command_name: cmd.phrase.clone(), error: e } )?; Ok( VerifiedCommand { @@ -250,7 +290,8 @@ mod private crate::mod_interface! { - exposed use Verifier; + orphan use Verifier; + orphan use VerificationError; // own use LevelOfDetail; // own use generate_help_content; diff --git a/module/move/wca/tests/inc/adapter.rs b/module/move/wca/tests/inc/adapter.rs deleted file mode 100644 index 33d5cd7e61..0000000000 --- a/module/move/wca/tests/inc/adapter.rs +++ /dev/null @@ -1,44 +0,0 @@ -use super::*; -use the_module::exposed::*; - -tests_impls! -{ - fn simple() - { - fn command( () : (), args : Args, props : Props) -> Result< (), () > - { - Ok( () ) - } - - fn command2( () : (), args : Args, props : Props ) -> Result< (), () > - { - Ok( () ) - } - - fn echo( () : (), args : Args, props : Props ) -> Result< (), () > - { - Ok( () ) - } - - let ca = the_module::cui( () ).command( command ).command( command2 ).command( echo.arg( "string", Type::String ) ).build(); - - a_id!( (), ca.perform( ".command2 .help" ).unwrap() ); - - a_id!( (), ca.perform( ".help command" ).unwrap() ); - a_id!( (), ca.perform( ".help command2" ).unwrap() ); - a_id!( (), ca.perform( ".help help" ).unwrap() ); - - a_id!( (), ca.perform( ".help.command" ).unwrap() ); - a_id!( (), ca.perform( ".help.command2" ).unwrap() ); - a_id!( (), ca.perform( ".help.help" ).unwrap() ); - - a_true!( ca.perform( ".help.help.help" ).is_err() ); - a_true!( ca.perform( ".echo 34" ).is_ok() ); - a_true!( ca.perform( ".echo" ).is_err() ); - } -} - -tests_index! -{ - simple -} diff --git a/module/move/wca/tests/inc/commands_aggregator/basic.rs b/module/move/wca/tests/inc/commands_aggregator/basic.rs index f7019bebf6..6c9ba72c09 100644 --- a/module/move/wca/tests/inc/commands_aggregator/basic.rs +++ b/module/move/wca/tests/inc/commands_aggregator/basic.rs @@ -1,5 +1,14 @@ use super::*; -use the_module::VerifiedCommand; +use the_module:: +{ + parser::Parser, + VerifiedCommand, + CommandsAggregator, + HelpVariants, + Type, + Error, + ValidationError, +}; // @@ -52,8 +61,7 @@ tests_impls! .perform(); a_id!( (), ca.perform( "." ).unwrap() ); - // qqq : this use case is disabled - // a_id!( (), ca.perform( ".cmd." ).unwrap() ); + a_id!( (), ca.perform( ".cmd." ).unwrap() ); } fn error_types() @@ -136,10 +144,10 @@ tests_impls! fn string_subject_with_colon() { - let dictionary = &the_module::Dictionary::former() + let dictionary = &the_module::grammar::Dictionary::former() .command ( - wca::Command::former() + wca::grammar::Command::former() .hint( "hint" ) .long_hint( "long_hint" ) .phrase( "command" ) @@ -150,7 +158,7 @@ tests_impls! ) .perform(); let parser = Parser; - let grammar = the_module::Verifier; + let grammar = the_module::verifier::Verifier; let executor = the_module::Executor::former().form(); let raw_command = parser.parse( [ ".command", "qwe:rty", "nightly:true" ] ).unwrap().commands.remove( 0 ); @@ -163,10 +171,10 @@ tests_impls! fn no_prop_subject_with_colon() { - let dictionary = &the_module::Dictionary::former() + let dictionary = &the_module::grammar::Dictionary::former() .command ( - the_module::Command::former() + the_module::grammar::Command::former() .hint( "hint" ) .long_hint( "long_hint" ) .phrase( "command" ) @@ -177,7 +185,7 @@ tests_impls! .form(); let parser = Parser; - let grammar = the_module::Verifier; + let grammar = the_module::verifier::Verifier; let executor = the_module::Executor::former().form(); let raw_command = parser.parse( [ ".command", "qwe:rty" ] ).unwrap().commands.remove( 0 ); @@ -190,10 +198,10 @@ tests_impls! fn optional_prop_subject_with_colon() { - let dictionary = &the_module::Dictionary::former() + let dictionary = &the_module::grammar::Dictionary::former() .command ( - the_module::Command::former() + the_module::grammar::Command::former() .hint( "hint" ) .long_hint( "long_hint" ) .phrase( "command" ) @@ -205,7 +213,7 @@ tests_impls! .form(); let parser = Parser; - let grammar = the_module::Verifier; + let grammar = the_module::verifier::Verifier; let executor = the_module::Executor::former().form(); let raw_command = parser.parse( [ ".command", "qwe:rty" ] ).unwrap().commands.remove( 0 ); @@ -216,7 +224,8 @@ tests_impls! a_id!( (), executor.command( dictionary, grammar_command ).unwrap() ); } - // qqq : make the following test work + // aaa : make the following test work + // aaa : works fn subject_with_spaces() { let query = "SELECT title, links, MIN( published ) FROM Frames"; diff --git a/module/move/wca/tests/inc/commands_aggregator/callback.rs b/module/move/wca/tests/inc/commands_aggregator/callback.rs index 834426c32d..21910a6560 100644 --- a/module/move/wca/tests/inc/commands_aggregator/callback.rs +++ b/module/move/wca/tests/inc/commands_aggregator/callback.rs @@ -1,5 +1,6 @@ use super::*; use std::sync::{ Arc, Mutex }; +use the_module::CommandsAggregator; #[ test ] fn changes_state_of_local_variable_on_perform() diff --git a/module/move/wca/tests/inc/commands_aggregator/help.rs b/module/move/wca/tests/inc/commands_aggregator/help.rs index 1df2be062e..2ce5a0bca5 100644 --- a/module/move/wca/tests/inc/commands_aggregator/help.rs +++ b/module/move/wca/tests/inc/commands_aggregator/help.rs @@ -1,7 +1,10 @@ -use std::fs::{DirBuilder, File}; -use std::io::Write; -use std::path::Path; -use std::process::{Command, Stdio}; +use std:: +{ + io::Write, + path::Path, + fs::{ DirBuilder, File }, + process::{ Command, Stdio }, +}; pub fn start_sync< AP, Args, Arg, P > ( @@ -11,9 +14,14 @@ pub fn start_sync< AP, Args, Arg, P > ) -> String where AP : AsRef< Path >, Args : IntoIterator< Item = Arg >, Arg : AsRef< std::ffi::OsStr >, P : AsRef< Path >, { let ( application, path ) = ( application.as_ref(), path.as_ref() ); - let args = args.into_iter().map( | a | a.as_ref().into() ).collect::< Vec< std::ffi::OsString > >(); + let args: Vec< std::ffi::OsString > = args.into_iter().map( | a | a.as_ref().into() ).collect(); let child = Command::new( application ).args( &args ).stdout( Stdio::piped() ).stderr( Stdio::piped() ).current_dir( path ).spawn().unwrap(); let output = child.wait_with_output().unwrap(); + + if !output.status.success() + { + println!( "{}", String::from_utf8( output.stderr ).unwrap() ); + } String::from_utf8( output.stdout ).unwrap() } diff --git a/module/move/wca/tests/inc/commands_aggregator/mod.rs b/module/move/wca/tests/inc/commands_aggregator/mod.rs index ca0cdc4b5a..fedda3d681 100644 --- a/module/move/wca/tests/inc/commands_aggregator/mod.rs +++ b/module/move/wca/tests/inc/commands_aggregator/mod.rs @@ -1,16 +1,5 @@ use super::*; -use the_module:: -{ - Parser, - - CommandsAggregator, - HelpVariants, - Type, - Error, - ValidationError, -}; - mod basic; mod callback; mod help; diff --git a/module/move/wca/tests/inc/executor/command.rs b/module/move/wca/tests/inc/executor/command.rs index b1dcf7ac12..e489b90764 100644 --- a/module/move/wca/tests/inc/executor/command.rs +++ b/module/move/wca/tests/inc/executor/command.rs @@ -1,5 +1,15 @@ use super::*; -use the_module::VerifiedCommand; +use the_module:: +{ + parser::Parser, + VerifiedCommand, + executor::Context, Type, + grammar::Dictionary, + verifier::Verifier, + + Executor, + // wtools +}; // @@ -14,7 +24,7 @@ tests_impls! let dictionary = &Dictionary::former() .command ( - wca::Command::former() + wca::grammar::Command::former() .hint( "hint" ) .long_hint( "long_hint" ) .phrase( "command" ) @@ -42,7 +52,7 @@ tests_impls! let dictionary = &Dictionary::former() .command ( - wca::Command::former() + wca::grammar::Command::former() .hint( "hint" ) .long_hint( "long_hint" ) .phrase( "command" ) @@ -78,7 +88,7 @@ tests_impls! let dictionary = &Dictionary::former() .command ( - wca::Command::former() + wca::grammar::Command::former() .hint( "hint" ) .long_hint( "long_hint" ) .phrase( "command" ) @@ -121,7 +131,7 @@ tests_impls! let dictionary = &Dictionary::former() .command ( - wca::Command::former() + wca::grammar::Command::former() .hint( "hint" ) .long_hint( "long_hint" ) .phrase( "check" ) @@ -137,7 +147,7 @@ tests_impls! ) .form(); let verifier = Verifier; - let mut ctx = wca::Context::new( Mutex::new( 1 ) ); + let mut ctx = wca::executor::Context::new( Mutex::new( 1 ) ); // init executor let executor = Executor::former() .context( ctx ) @@ -160,7 +170,7 @@ tests_impls! let dictionary = &Dictionary::former() .command ( - wca::Command::former() + wca::grammar::Command::former() .hint( "hint" ) .long_hint( "long_hint" ) .phrase( "command" ) diff --git a/module/move/wca/tests/inc/executor/mod.rs b/module/move/wca/tests/inc/executor/mod.rs index 7c84cbf8a3..617cf69b75 100644 --- a/module/move/wca/tests/inc/executor/mod.rs +++ b/module/move/wca/tests/inc/executor/mod.rs @@ -1,17 +1,4 @@ use super::*; -// qqq : rid of global uses in tests -use the_module:: -{ - Parser, - - Context, Type, - Dictionary, - Verifier, - - Executor, - // wtools -}; - mod command; mod program; diff --git a/module/move/wca/tests/inc/executor/program.rs b/module/move/wca/tests/inc/executor/program.rs index de33330259..ef0f63940a 100644 --- a/module/move/wca/tests/inc/executor/program.rs +++ b/module/move/wca/tests/inc/executor/program.rs @@ -1,5 +1,15 @@ use super::*; -use the_module::VerifiedCommand; +use the_module:: +{ + parser::Parser, + VerifiedCommand, + executor::Context, Type, + grammar::Dictionary, + verifier::Verifier, + + Executor, + // wtools +}; // @@ -14,7 +24,7 @@ tests_impls! let dictionary = &Dictionary::former() .command ( - wca::Command::former() + wca::grammar::Command::former() .hint( "hint" ) .long_hint( "long_hint" ) .phrase( "command" ) @@ -47,7 +57,7 @@ tests_impls! let dictionary = &Dictionary::former() .command ( - wca::Command::former() + wca::grammar::Command::former() .hint( "hint" ) .long_hint( "long_hint" ) .phrase( "inc" ) @@ -63,7 +73,7 @@ tests_impls! ) .command ( - wca::Command::former() + wca::grammar::Command::former() .hint( "hint" ) .long_hint( "long_hint" ) .phrase( "eq" ) @@ -91,7 +101,7 @@ tests_impls! let verifier = Verifier; // starts with 0 - let ctx = wca::Context::new( Mutex::new( 0 ) ); + let ctx = wca::executor::Context::new( Mutex::new( 0 ) ); // init simple executor let executor = Executor::former() .context( ctx ) diff --git a/module/move/wca/tests/inc/grammar/from_command.rs b/module/move/wca/tests/inc/grammar/from_command.rs index 9823236c0c..343cde7ffb 100644 --- a/module/move/wca/tests/inc/grammar/from_command.rs +++ b/module/move/wca/tests/inc/grammar/from_command.rs @@ -1,5 +1,14 @@ use super::*; +use the_module:: +{ + parser::Parser, + + Type, Value, + grammar::Dictionary, + verifier::Verifier, +}; + // tests_impls! @@ -13,7 +22,7 @@ tests_impls! let dictionary = &Dictionary::former() .command ( - wca::Command::former() + wca::grammar::Command::former() .hint( "hint" ) .long_hint( "long_hint" ) .phrase( "command" ) @@ -45,7 +54,7 @@ tests_impls! let dictionary = &Dictionary::former() .command ( - wca::Command::former() + wca::grammar::Command::former() .hint( "hint" ) .long_hint( "long_hint" ) .phrase( "command" ) @@ -92,7 +101,7 @@ tests_impls! let dictionary = &Dictionary::former() .command ( - wca::Command::former() + wca::grammar::Command::former() .hint( "hint" ) .long_hint( "long_hint" ) .phrase( "command" ) @@ -121,7 +130,7 @@ tests_impls! let dictionary = &Dictionary::former() .command ( - wca::Command::former() + wca::grammar::Command::former() .hint( "hint" ) .long_hint( "long_hint" ) .phrase( "command" ) @@ -156,7 +165,7 @@ tests_impls! let dictionary = &Dictionary::former() .command ( - wca::Command::former() + wca::grammar::Command::former() .hint( "hint" ) .long_hint( "long_hint" ) .phrase( "command" ) @@ -184,7 +193,7 @@ tests_impls! let dictionary = &Dictionary::former() .command ( - wca::Command::former() + wca::grammar::Command::former() .hint( "hint" ) .long_hint( "long_hint" ) .phrase( "command" ) @@ -223,7 +232,7 @@ tests_impls! let dictionary = &Dictionary::former() .command ( - wca::Command::former() + wca::grammar::Command::former() .hint( "hint" ) .long_hint( "long_hint" ) .phrase( "command" ) @@ -268,7 +277,7 @@ tests_impls! let dictionary = &Dictionary::former() .command ( - wca::Command::former() + wca::grammar::Command::former() .hint( "hint" ) .long_hint( "long_hint" ) .phrase( "command" ) @@ -297,7 +306,7 @@ tests_impls! let dictionary = &Dictionary::former() .command ( - wca::Command::former() + wca::grammar::Command::former() .hint( "hint" ) .long_hint( "long_hint" ) .phrase( "command" ) @@ -328,7 +337,7 @@ tests_impls! let dictionary = &Dictionary::former() .command ( - wca::Command::former() + wca::grammar::Command::former() .hint( "hint" ) .long_hint( "long_hint" ) .phrase( "command" ) @@ -369,7 +378,7 @@ tests_impls! let dictionary = &Dictionary::former() .command ( - wca::Command::former() + wca::grammar::Command::former() .hint( "hint" ) .long_hint( "long_hint" ) .phrase( "command" ) diff --git a/module/move/wca/tests/inc/grammar/from_program.rs b/module/move/wca/tests/inc/grammar/from_program.rs index 670eaf178c..256fd6dcd9 100644 --- a/module/move/wca/tests/inc/grammar/from_program.rs +++ b/module/move/wca/tests/inc/grammar/from_program.rs @@ -1,5 +1,14 @@ use super::*; +use the_module:: +{ + parser::Parser, + + Type, Value, + grammar::Dictionary, + verifier::Verifier, +}; + // tests_impls! @@ -12,7 +21,7 @@ tests_impls! let dictionary = &Dictionary::former() .command ( - wca::Command::former() + wca::grammar::Command::former() .hint( "hint" ) .long_hint( "long_hint" ) .phrase( "command1" ) @@ -21,7 +30,7 @@ tests_impls! ) .command ( - wca::Command::former() + wca::grammar::Command::former() .hint( "hint" ) .long_hint( "long_hint" ) .phrase( "command2" ) diff --git a/module/move/wca/tests/inc/grammar/mod.rs b/module/move/wca/tests/inc/grammar/mod.rs index 38c94dc114..454495c496 100644 --- a/module/move/wca/tests/inc/grammar/mod.rs +++ b/module/move/wca/tests/inc/grammar/mod.rs @@ -1,12 +1,4 @@ use super::*; -use the_module:: -{ - Parser, - - Type, Value, - Dictionary, - Verifier, -}; mod from_command; mod from_program; diff --git a/module/move/wca/tests/inc/grammar/types.rs b/module/move/wca/tests/inc/grammar/types.rs index 7421fce48f..b04ab6c346 100644 --- a/module/move/wca/tests/inc/grammar/types.rs +++ b/module/move/wca/tests/inc/grammar/types.rs @@ -1,5 +1,5 @@ use super::*; -use wca::TryCast; +use the_module::{ TryCast, Type, Value }; // @@ -134,7 +134,7 @@ tests_impls! let string = Type::List( Type::String.into(), ',' ).try_cast( origin_string.into() ).unwrap(); a_id!( origin_string, string.to_string() ); - // xxx : qqq : that fails now. suggest solution + // xxx clarification is needed : qqq : that fails now. suggest solution // let origin_string = "100;3.14"; // let string = Type::List( Type::Number.into(), ';' ).try_cast( origin_string.into() ).unwrap(); // a_id!( origin_string, string.to_string() ); diff --git a/module/move/wca/tests/inc/mod.rs b/module/move/wca/tests/inc/mod.rs index c2617e9035..b51887947e 100644 --- a/module/move/wca/tests/inc/mod.rs +++ b/module/move/wca/tests/inc/mod.rs @@ -1,10 +1,6 @@ #[ allow( unused_imports ) ] use super::*; -#[ allow( unused_imports ) ] -use the_module::tool::*; -#[ allow( unused_imports ) ] -use std::collections::HashMap; #[ cfg( not( feature = "no_std" ) ) ] mod parser; @@ -15,6 +11,5 @@ mod executor; #[ cfg( not( feature = "no_std" ) ) ] mod commands_aggregator; -// qqq : for Bohdan : why commented out? resolve -// #[ cfg( not( feature = "no_std" ) ) ] -// mod adapter; +// aaa : for Bohdan : why commented out? resolve +// aaa : no longer relevant, so removed diff --git a/module/move/wca/tests/inc/parser/command.rs b/module/move/wca/tests/inc/parser/command.rs index 986ab1d0c0..7f5c1aecf4 100644 --- a/module/move/wca/tests/inc/parser/command.rs +++ b/module/move/wca/tests/inc/parser/command.rs @@ -1,4 +1,5 @@ use super::*; +use the_module::parser::{ ParsedCommand, Parser }; // diff --git a/module/move/wca/tests/inc/parser/mod.rs b/module/move/wca/tests/inc/parser/mod.rs index 456679d11a..617cf69b75 100644 --- a/module/move/wca/tests/inc/parser/mod.rs +++ b/module/move/wca/tests/inc/parser/mod.rs @@ -1,10 +1,4 @@ use super::*; -use wca:: -{ - Program, ParsedCommand, - - Parser, -}; mod command; mod program; diff --git a/module/move/wca/tests/inc/parser/program.rs b/module/move/wca/tests/inc/parser/program.rs index 081f8cc3e8..04b07c322f 100644 --- a/module/move/wca/tests/inc/parser/program.rs +++ b/module/move/wca/tests/inc/parser/program.rs @@ -1,4 +1,5 @@ use super::*; +use the_module::parser::{ Program, ParsedCommand, Parser }; // From 40f5e25c8b2f6673a2b86c11c4629ad022ef9fee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A1=D0=B5=D0=B2=D0=B0=20=D0=91=D0=B0=D0=BA=D1=83=D1=82?= =?UTF-8?q?=D0=BE=D0=B2?= <122987843+sevabakutov@users.noreply.github.com> Date: Wed, 11 Dec 2024 22:52:43 +0100 Subject: [PATCH 62/67] Fix gspread error handling (#1512) * secret description fixed * readme file fixed * secrets description changed * removed unused imports * fixed: wrong url error handling --------- Co-authored-by: Vsevolod --- module/move/gspread/.key/readme.md | 44 ------------------- module/move/gspread/.secret/readme.md | 42 ++++++++++++++++++ module/move/gspread/src/actions/gspread.rs | 17 +++++-- module/move/gspread/src/bin/main.rs | 3 +- .../move/gspread/src/commands/gspread_cell.rs | 20 ++++++++- .../gspread/src/commands/gspread_cells.rs | 10 ++++- .../gspread/src/commands/gspread_header.rs | 11 ++++- .../move/gspread/src/commands/gspread_rows.rs | 10 ++++- module/move/gspread/src/secret.rs | 14 +++--- 9 files changed, 109 insertions(+), 62 deletions(-) delete mode 100644 module/move/gspread/.key/readme.md create mode 100644 module/move/gspread/.secret/readme.md diff --git a/module/move/gspread/.key/readme.md b/module/move/gspread/.key/readme.md deleted file mode 100644 index cfd1cc23d2..0000000000 --- a/module/move/gspread/.key/readme.md +++ /dev/null @@ -1,44 +0,0 @@ -# Getting API Keys for OAuth Authentication - -Follow these steps to create and configure your OAuth credentials for using Google APIs. - -## 1. Create API Credentials - -1. Go to the [Google API Console](https://console.developers.google.com/). -2. From the projects list, select an existing project or create a new one. -3. In the left side menu, select **APIs & Services**. -4. On the left menu, click **Credentials**. -5. Click **Create Credentials** and select **OAuth client ID**. -6. In the **Application type** section, select **Desktop app**. -7. Provide an appropriate name for your client ID (e.g., "MyApp OAuth Client"). -8. Click **Create**. - -Once the credential is created, you will receive a **Client ID** and **Client Secret**. These are required for accessing the API. - -## 2. Store Your Credentials - -Save the **Client ID** and **Client Secret** in a `.sh` file (e.g., `-env.sh`) within a `key` directory. The file should look like this: - -```bash -CLIENT_ID=YOUR_CLIENT_ID -CLIENT_SECRET=YOUR_SECRET_KEY -``` - -Set also these keys with following values: -```bash -AUTH_URI=https://accounts.google.com/o/oauth2/auth -TOKEN_URI=https://oauth2.googleapis.com/token -``` -If you get problems, most likely you will need to change **AUTH_URI** or **TOKEN_URI** to the appropriate one. Try to download your API KEY that you created in JSON format. Then open it and you will see right links. Just copy them and past to file. -Otherwise, follow [Google OAuth Documentation](https://developers.google.com/identity/protocols/oauth2/) to solve them. -Most likely you will need to change **AUTH_URI** or **TOKEN_URI** to the appropriate one. - -## How to Use in Shell - -To apply these variables to your current shell session, use: - -```bash -. ./key/-env.sh -``` - -This command sources the script, making the variables available in your current session. Ensure `-env.sh` is in the `key` directory relative to your current location. \ No newline at end of file diff --git a/module/move/gspread/.secret/readme.md b/module/move/gspread/.secret/readme.md new file mode 100644 index 0000000000..e3e100f72d --- /dev/null +++ b/module/move/gspread/.secret/readme.md @@ -0,0 +1,42 @@ +# Getting API Keys for OAuth Authentication + +Follow these steps to create and configure your OAuth credentials for using Google APIs. + +## 1. Create API Credentials + +1. Go to the [Google API Console](https://console.developers.google.com/). +2. From the projects list, select an existing project or create a new one. +3. In the left side menu, select **APIs & Services**. +4. On the left menu, click **Credentials**. +5. Click **Create Credentials** and select **OAuth client ID**. +6. In the **Application type** section, select **Desktop app**. +7. Provide an appropriate name for your client ID (e.g., "Gspread OAuth Client"). +8. Click **Create**. + +Once the credential is created, you will receive a **Client ID** and **Client Secret**. These are required for accessing the API. + +## 2. Store Your Credentials + +Save the **Client ID** and **Client Secret** in a `.env` within a `.secret` directory. The file should look like this: + +```bash +CLIENT_ID=YOUR_CLIENT_ID +CLIENT_SECRET=YOUR_SECRET_KEY +``` + +## 3. Why do we need it? + +After executing each command, you need to grant the GSPREAD program access to the Google API. You will receive a link that begin with 'Please direct your browser to https://....' that will redirect you to your browser, where you must authorize the access. You will need to select the appropriate Google account that has the credentials for the application. The **CLIENT_ID** and **CLIENT_SECRET** are set up to do this process. + +## 4. Troubleshooting + +If you encounter a page displaying an error instead of the Google account selection screen, it is likely that you need to add **AUTH_URI** or **TOKEN_URI** to the .env file. In this case, all four secrets are required. To retrieve them, download the API key you created in JSON format. Open the file and copy the necessary keys into the .env file. After making these changes, your .env file should look like this: + +```bash +CLIENT_ID=YOUR_CLIENT_ID +CLIENT_SECRET=YOUR_SECRET_KEY +AUTH_URI=YOUR_AUTH_URI +TOKEN_URI=YOUR_TOKEN_URI +``` + +If you still get some issues, follow [Google OAuth Documentation](https://developers.google.com/identity/protocols/oauth2/). \ No newline at end of file diff --git a/module/move/gspread/src/actions/gspread.rs b/module/move/gspread/src/actions/gspread.rs index 3bee6a3e27..60b0fd980c 100644 --- a/module/move/gspread/src/actions/gspread.rs +++ b/module/move/gspread/src/actions/gspread.rs @@ -23,13 +23,19 @@ mod private #[ from ] #[ serde_as( as = "DisplayFromStr" ) ] google_sheets4::Error - ) + ), + + #[ error( "Invalid URL format: {0}" ) ] + InvalidUrl + ( + String + ), } pub fn get_spreadsheet_id_from_url ( url : &str - ) -> Option< &str > + ) -> Result< &str > { let re = Regex::new( r"d/([^/]+)/edit" ).unwrap(); @@ -37,11 +43,14 @@ mod private { if let Some( id ) = captures.get( 1 ) { - return Some( id.as_str() ); + return Ok( id.as_str() ); } } - None + Err + ( + Error::InvalidUrl( "Wrong url format.\nFix: copy sheet's the whole url from your browser. Usage: --url ''".to_string() ) + ) } pub type Result< T > = core::result::Result< T, Error >; diff --git a/module/move/gspread/src/bin/main.rs b/module/move/gspread/src/bin/main.rs index 71fd85c041..8f55f07f1c 100644 --- a/module/move/gspread/src/bin/main.rs +++ b/module/move/gspread/src/bin/main.rs @@ -1,5 +1,4 @@ use std::error::Error; -use std::env; use clap::Parser; use dotenv::dotenv; @@ -15,7 +14,7 @@ async fn main() -> Result< (), Box< dyn Error > > { dotenv().ok(); - let secret = Secret::load()?; + let secret = Secret::read(); let hub = hub( &secret ).await?; diff --git a/module/move/gspread/src/commands/gspread_cell.rs b/module/move/gspread/src/commands/gspread_cell.rs index e81f9d8595..057da2dd09 100644 --- a/module/move/gspread/src/commands/gspread_cell.rs +++ b/module/move/gspread/src/commands/gspread_cell.rs @@ -55,7 +55,15 @@ mod private { Commands::Get { url, tab, cel } => { - let spreadsheet_id = get_spreadsheet_id_from_url( url.as_str() ).unwrap(); + let spreadsheet_id = match get_spreadsheet_id_from_url( url.as_str() ) + { + Ok( id ) => id, + Err( error ) => + { + eprintln!( "Error extracting spreadsheet ID: {}", error ); + return; + } + }; let result = actions::gspread_cell_get::action ( @@ -74,7 +82,15 @@ mod private Commands::Set { url, tab, cel, val } => { - let spreadsheet_id = get_spreadsheet_id_from_url( url.as_str() ).unwrap(); + let spreadsheet_id = match get_spreadsheet_id_from_url( url.as_str() ) + { + Ok( id ) => id, + Err( error ) => + { + eprintln!( "Error extracting spreadsheet ID: {}", error ); + return; + } + }; let result = actions::gspread_cell_set::action ( diff --git a/module/move/gspread/src/commands/gspread_cells.rs b/module/move/gspread/src/commands/gspread_cells.rs index cd7a4cc555..13ecf1e378 100644 --- a/module/move/gspread/src/commands/gspread_cells.rs +++ b/module/move/gspread/src/commands/gspread_cells.rs @@ -41,7 +41,15 @@ mod private { Commands::Set { select_row_by_key, json, url, tab } => { - let spreadsheet_id = get_spreadsheet_id_from_url( url.as_str() ).unwrap(); + let spreadsheet_id = match get_spreadsheet_id_from_url( url.as_str() ) + { + Ok( id ) => id, + Err( error ) => + { + eprintln!( "Error extracting spreadsheet ID: {}", error ); + return; + } + }; let result = actions::gspread_cells_set::action ( diff --git a/module/move/gspread/src/commands/gspread_header.rs b/module/move/gspread/src/commands/gspread_header.rs index 49be5e6e86..5048d3e4ed 100644 --- a/module/move/gspread/src/commands/gspread_header.rs +++ b/module/move/gspread/src/commands/gspread_header.rs @@ -41,7 +41,16 @@ mod private { CommonArgs { url, tab } => { - let spreadsheet_id = get_spreadsheet_id_from_url( url.as_str() ).unwrap(); + let spreadsheet_id = match get_spreadsheet_id_from_url( url.as_str() ) + { + Ok( id ) => id, + Err( error ) => + { + eprintln!( "Error extracting spreadsheet ID: {}", error ); + return; + } + }; + let result = actions::gspread_get_header::action ( hub, diff --git a/module/move/gspread/src/commands/gspread_rows.rs b/module/move/gspread/src/commands/gspread_rows.rs index 86f0c41f59..426d7f2dde 100644 --- a/module/move/gspread/src/commands/gspread_rows.rs +++ b/module/move/gspread/src/commands/gspread_rows.rs @@ -40,7 +40,15 @@ mod private { CommonArgs { url, tab } => { - let spreadsheet_id = get_spreadsheet_id_from_url( url.as_str() ).unwrap(); + let spreadsheet_id = match get_spreadsheet_id_from_url( url.as_str() ) + { + Ok( id ) => id, + Err( error ) => + { + eprintln!( "Error extracting spreadsheet ID: {}", error ); + return; + } + }; let result = actions::gspread_get_rows::action ( diff --git a/module/move/gspread/src/secret.rs b/module/move/gspread/src/secret.rs index f70792b958..48567b77f4 100644 --- a/module/move/gspread/src/secret.rs +++ b/module/move/gspread/src/secret.rs @@ -52,7 +52,7 @@ mod private #[ allow( non_snake_case ) ] pub fn load() -> Result< Self > { - let path = "./.key/-env.sh"; + let path = "./.secret/.env"; let r = dotenv::from_path( path ); if let Err( ref err ) = r @@ -67,8 +67,8 @@ mod private { CLIENT_SECRET : var( "CLIENT_SECRET", None )?, CLIENT_ID : var( "CLIENT_ID", None )?, - AUTH_URI : var ( "AUTH_URI", None )?, - TOKEN_URI : var ( "TOKEN_URI", None )? + AUTH_URI : var ( "AUTH_URI", Some( "https://accounts.google.com/o/oauth2/auth" ) )?, + TOKEN_URI : var ( "TOKEN_URI", Some( "https://oauth2.googleapis.com/token" ) )? }; Ok( config ) } @@ -77,7 +77,7 @@ mod private { Self::load().unwrap_or_else( | err | { - let example = include_str!("../.key/readme.md"); + let example = include_str!("../.secret/readme.md"); let explanation = format! ( r#" = Lack of secrets @@ -87,7 +87,7 @@ Failed to load secret or some its parameters. = Fix -Either define missing environment variable or make sure `./.key/-env.toml` file has it defined. +Add missing secret to .env file in .secret directory. Example: MISSING_SECRET=YOUR_MISSING_SECRET = More information @@ -109,10 +109,10 @@ Either define missing environment variable or make sure `./.key/-env.toml` file fn var ( name : &'static str, - default : Option<&'static str>, + default : Option< &'static str >, ) -> Result < String > { - match env::var(name) + match env::var( name ) { Ok( val ) => Ok ( val ), Err( _ ) => From 9c2cbf470a274a04c7deb201dee9f9f78cae2e8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A1=D0=B5=D0=B2=D0=B0=20=D0=91=D0=B0=D0=BA=D1=83=D1=82?= =?UTF-8?q?=D0=BE=D0=B2?= <122987843+sevabakutov@users.noreply.github.com> Date: Wed, 11 Dec 2024 22:53:08 +0100 Subject: [PATCH 63/67] secret description fixed (#1510) * secret description fixed * readme file fixed * secrets description changed --------- Co-authored-by: Vsevolod From b0f9911759ef664d788e9f3b474733dcab8b6ab6 Mon Sep 17 00:00:00 2001 From: ".Barsik" Date: Wed, 11 Dec 2024 23:53:24 +0200 Subject: [PATCH 64/67] NOT READY: (willbe): `qqq` processing (#1511) * fix(willbe): imports related to `wca` changes * fix(willbe): long lines --- module/move/willbe/src/action/list.rs | 31 ++++++++++++++----- module/move/willbe/src/action/test.rs | 4 +-- module/move/willbe/src/command/list.rs | 4 +-- module/move/willbe/src/command/publish.rs | 4 +-- .../move/willbe/src/command/publish_diff.rs | 4 +-- module/move/willbe/src/command/test.rs | 6 ++-- .../willbe/src/command/workspace_renew.rs | 4 +-- module/move/willbe/src/entity/dependency.rs | 9 ++++-- module/move/willbe/src/entity/test.rs | 6 ++-- module/move/willbe/src/tool/template.rs | 2 +- .../willbe/tests/inc/action_tests/test.rs | 8 ----- 11 files changed, 47 insertions(+), 35 deletions(-) diff --git a/module/move/willbe/src/action/list.rs b/module/move/willbe/src/action/list.rs index 040c8ba12c..c747532f6d 100644 --- a/module/move/willbe/src/action/list.rs +++ b/module/move/willbe/src/action/list.rs @@ -462,17 +462,32 @@ mod private .package_find_by_manifest( manifest_file ) .ok_or_else( || format_err!( "Package not found in the workspace" ) ) .err_with_report( report )?; + let version = if args.info.contains( &PackageAdditionalInfo::Version ) + { + Some( package.version().to_string() ) + } + else + { + None + }; + let crate_dir = if args.info.contains( &PackageAdditionalInfo::Path ) + { + Some( package.crate_dir() ).transpose() + } + else + { + Ok( None ) + } + .err_with_report( report )?; let mut package_report = tool::ListNodeReport { name : package.name().to_string(), - // qqq : for Bohdan : too long lines - version : if args.info.contains( &PackageAdditionalInfo::Version ) { Some( package.version().to_string() ) } else { None }, - // qqq : for Bohdan : don't put multiline if into struct constructor - crate_dir : if args.info.contains( &PackageAdditionalInfo::Path ) - { Some( package.crate_dir() ).transpose() } - else - { Ok( None ) } - .err_with_report( report )?, + // aaa : for Bohdan : too long lines + // aaa : moved out + version, + // aaa : for Bohdan : don't put multiline if into struct constructor + // aaa : moved out + crate_dir, duplicate : false, normal_dependencies : vec![], dev_dependencies : vec![], diff --git a/module/move/willbe/src/action/test.rs b/module/move/willbe/src/action/test.rs index 8efcad2f5f..0d22053121 100644 --- a/module/move/willbe/src/action/test.rs +++ b/module/move/willbe/src/action/test.rs @@ -71,7 +71,7 @@ mod private // -> Result< TestsReport, ( TestsReport, Error ) > { - // qqq : incapsulate progress bar logic into some function of struct. don't keep it here + // aaa : incapsulate progress bar logic into some function of struct. don't keep it here // aaa : done let mut report = TestsReport::default(); @@ -164,7 +164,7 @@ Try to install it with `rustup install {}` command(-s)", ).err_with_report( &report )?; println!( "{plan}" ); - // aaa : split on two functions for create plan and for execute + // aaa : split on two functions for create plan and for execute // aaa : it's already separated, look line: 203 : let result = tests_run( &options ); let temp_path = if temp diff --git a/module/move/willbe/src/command/list.rs b/module/move/willbe/src/command/list.rs index 98e8b2d2be..7687b6db1d 100644 --- a/module/move/willbe/src/command/list.rs +++ b/module/move/willbe/src/command/list.rs @@ -97,10 +97,10 @@ mod private Ok( () ) } - impl TryFrom< wca::Props > for ListProperties + impl TryFrom< wca::executor::Props > for ListProperties { type Error = error::untyped::Error; - fn try_from( value : wca::Props ) -> Result< Self, Self::Error > + fn try_from( value : wca::executor::Props ) -> Result< Self, Self::Error > { let mut this = Self::former(); diff --git a/module/move/willbe/src/command/publish.rs b/module/move/willbe/src/command/publish.rs index 3af3fad60d..d02fa67bb5 100644 --- a/module/move/willbe/src/command/publish.rs +++ b/module/move/willbe/src/command/publish.rs @@ -101,10 +101,10 @@ mod private } } - impl TryFrom< wca::Props > for PublishProperties + impl TryFrom< wca::executor::Props > for PublishProperties { type Error = error::untyped::Error; - fn try_from( value : wca::Props ) -> Result< Self, Self::Error > + fn try_from( value : wca::executor::Props ) -> Result< Self, Self::Error > { let mut this = Self::former(); diff --git a/module/move/willbe/src/command/publish_diff.rs b/module/move/willbe/src/command/publish_diff.rs index 74cdbcbc48..6408de6de5 100644 --- a/module/move/willbe/src/command/publish_diff.rs +++ b/module/move/willbe/src/command/publish_diff.rs @@ -53,10 +53,10 @@ mod private Ok( () ) } - impl TryFrom< wca::Props > for PublishDiffProperties + impl TryFrom< wca::executor::Props > for PublishDiffProperties { type Error = error::untyped::Error; - fn try_from( value : wca::Props ) -> Result< Self, Self::Error > + fn try_from( value : wca::executor::Props ) -> Result< Self, Self::Error > { let mut this = Self::former(); diff --git a/module/move/willbe/src/command/test.rs b/module/move/willbe/src/command/test.rs index b56c84a79f..36fac78a27 100644 --- a/module/move/willbe/src/command/test.rs +++ b/module/move/willbe/src/command/test.rs @@ -147,10 +147,10 @@ Set at least one of them to true." ); } } - impl TryFrom< wca::Props > for TestsProperties + impl TryFrom< wca::executor::Props > for TestsProperties { type Error = error::untyped::Error; - fn try_from( value : wca::Props ) -> Result< Self, Self::Error > + fn try_from( value : wca::executor::Props ) -> Result< Self, Self::Error > { let mut this = Self::former(); @@ -192,4 +192,4 @@ crate::mod_interface! { /// run tests in specified crate exposed use test; -} \ No newline at end of file +} diff --git a/module/move/willbe/src/command/workspace_renew.rs b/module/move/willbe/src/command/workspace_renew.rs index 6e43d29eea..018046b146 100644 --- a/module/move/willbe/src/command/workspace_renew.rs +++ b/module/move/willbe/src/command/workspace_renew.rs @@ -33,11 +33,11 @@ mod private .context( "Fail to create workspace" ) } - impl TryFrom< wca::Props > for WorkspaceNewProperties + impl TryFrom< wca::executor::Props > for WorkspaceNewProperties { type Error = error::untyped::Error; - fn try_from( value : wca::Props ) -> std::result::Result< Self, Self::Error > + fn try_from( value : wca::executor::Props ) -> std::result::Result< Self, Self::Error > { let mut this = Self::former(); diff --git a/module/move/willbe/src/entity/dependency.rs b/module/move/willbe/src/entity/dependency.rs index 337ecb01a2..5d09c1cea0 100644 --- a/module/move/willbe/src/entity/dependency.rs +++ b/module/move/willbe/src/entity/dependency.rs @@ -260,8 +260,13 @@ mod private } DependenciesSort::Topological => { - // qqq : too long line - graph::toposort( graph::construct( &graph ) ).map_err( | err | format_err!( "{}", err ) )?.into_iter().filter( | x | x != &root ).collect() + // aaa : too long line + // aaa : splited + graph::toposort( graph::construct( &graph ) ) + .map_err( | err | format_err!( "{}", err ) )? + .into_iter() + .filter( | x | x != &root ) + .collect() }, }; diff --git a/module/move/willbe/src/entity/test.rs b/module/move/willbe/src/entity/test.rs index 938c5ca415..b8b7b67227 100644 --- a/module/move/willbe/src/entity/test.rs +++ b/module/move/willbe/src/entity/test.rs @@ -36,12 +36,12 @@ mod private /// Represents the optimization setting for the test variant. optimization : optimization::Optimization, /// Contains additional features or characteristics of the test variant. - features : collection::BTreeSet, + features : collection::BTreeSet< String >, } impl fmt::Display for TestVariant { - fn fmt( &self, f : &mut fmt::Formatter< '_ >) -> fmt::Result + fn fmt( &self, f : &mut fmt::Formatter< '_ > ) -> fmt::Result { let features = if self.features.is_empty() { " ".to_string() } else { self.features.iter().join( " " ) }; writeln!( f, "{} {} {}", self.optimization, self.channel, features )?; @@ -58,7 +58,7 @@ mod private impl fmt::Display for TestPlan { - fn fmt( &self, f : &mut fmt::Formatter< '_ >) -> std::fmt::Result + fn fmt( &self, f : &mut fmt::Formatter< '_ > ) -> std::fmt::Result { writeln!( f, "Plan: " )?; for plan in &self.packages_plan diff --git a/module/move/willbe/src/tool/template.rs b/module/move/willbe/src/tool/template.rs index 59040dca01..91c804c8ce 100644 --- a/module/move/willbe/src/tool/template.rs +++ b/module/move/willbe/src/tool/template.rs @@ -172,7 +172,7 @@ mod private impl TemplateParameters { /// Extracts template values from props for parameters required for this template. - pub fn values_from_props( &self, props : &wca::Props ) -> TemplateValues + pub fn values_from_props( &self, props : &wca::executor::Props ) -> TemplateValues { let values = self.descriptors .iter() diff --git a/module/move/willbe/tests/inc/action_tests/test.rs b/module/move/willbe/tests/inc/action_tests/test.rs index 16a4e8cd6a..67c926cb89 100644 --- a/module/move/willbe/tests/inc/action_tests/test.rs +++ b/module/move/willbe/tests/inc/action_tests/test.rs @@ -1,21 +1,13 @@ use super::*; -// use the_module::*; // qqq : for Bohdan : bad. don't import the_module::* use inc::helper:: { ProjectBuilder, WorkspaceBuilder, - // BINARY_NAME, }; use collection::BTreeSet; -// use std:: -// { -// fs::{ self, File }, -// io::Write, -// }; -// use path::{ Path, PathBuf }; use assert_fs::TempDir; use the_module::action::test::{ test, TestsCommandOptions }; From 33f1d1ab08d2eda6bac07f117835b8c3b57b3bcc Mon Sep 17 00:00:00 2001 From: SRetip <56289352+SRetip@users.noreply.github.com> Date: Mon, 16 Dec 2024 11:40:30 +0200 Subject: [PATCH 65/67] NOT READY : fix willbe (#1479) * fix: tests for readme_modules_headers_renew.rs * fix: lints & compile errors * feat: finish with clippy * fix: udeps --------- Co-authored-by: Barsik --- Cargo.toml | 8 +- module/core/clone_dyn_types/Readme.md | 2 +- module/core/clone_dyn_types/src/lib.rs | 24 ++- module/core/collection_tools/Readme.md | 2 +- .../src/collection/binary_heap.rs | 11 +- .../src/collection/btree_map.rs | 11 +- .../src/collection/btree_set.rs | 11 +- .../src/collection/hash_map.rs | 9 +- .../src/collection/hash_set.rs | 9 +- .../src/collection/linked_list.rs | 11 +- .../collection_tools/src/collection/mod.rs | 27 ++- .../src/collection/vec_deque.rs | 11 +- .../collection_tools/src/collection/vector.rs | 12 +- module/core/collection_tools/src/lib.rs | 15 +- module/core/data_type/Readme.md | 4 +- module/core/data_type/src/dt.rs | 3 + module/core/data_type/src/lib.rs | 3 + module/core/derive_tools/Readme.md | 2 +- module/core/derive_tools/src/lib.rs | 7 +- module/core/derive_tools_meta/Readme.md | 2 +- .../derive_tools_meta/src/derive/as_ref.rs | 1 + .../derive_tools_meta/src/derive/deref.rs | 28 +-- .../core/derive_tools_meta/src/derive/from.rs | 18 +- .../src/derive/from/field_attributes.rs | 11 +- .../src/derive/from/item_attributes.rs | 11 +- module/core/derive_tools_meta/src/lib.rs | 2 +- module/core/error_tools/Readme.md | 2 +- module/core/error_tools/src/error/assert.rs | 24 ++- module/core/error_tools/src/error/mod.rs | 49 ++++- module/core/error_tools/src/error/typed.rs | 12 ++ module/core/error_tools/src/error/untyped.rs | 10 + module/core/error_tools/src/lib.rs | 22 ++- module/core/former/Readme.md | 4 +- module/core/former/src/lib.rs | 3 + module/core/former_meta/Readme.md | 6 +- .../src/component/component_assign.rs | 1 + .../src/component/component_from.rs | 2 +- .../src/component/components_assign.rs | 8 +- .../src/component/from_components.rs | 3 +- module/core/former_meta/src/derive_former.rs | 33 ++-- .../former_meta/src/derive_former/field.rs | 100 +++++----- .../src/derive_former/field_attrs.rs | 21 +- .../src/derive_former/struct_attrs.rs | 25 +-- module/core/former_types/Readme.md | 2 +- module/core/former_types/src/collection.rs | 13 ++ .../src/collection/binary_heap.rs | 2 + .../former_types/src/collection/btree_map.rs | 3 +- .../former_types/src/collection/btree_set.rs | 3 +- .../former_types/src/collection/hash_map.rs | 11 ++ .../former_types/src/collection/hash_set.rs | 14 +- .../src/collection/linked_list.rs | 3 +- .../former_types/src/collection/vector.rs | 3 +- .../src/collection/vector_deque.rs | 3 +- module/core/former_types/src/component.rs | 13 +- module/core/former_types/src/forming.rs | 1 + module/core/former_types/src/lib.rs | 8 +- module/core/impls_index/Cargo.toml | 2 +- module/core/interval_adapter/Readme.md | 4 +- module/core/interval_adapter/src/lib.rs | 96 +++++++-- module/core/iter_tools/Readme.md | 4 +- module/core/iter_tools/src/iter.rs | 8 +- module/core/iter_tools/src/lib.rs | 3 + module/core/macro_tools/Readme.md | 10 +- module/core/macro_tools/src/attr.rs | 23 ++- module/core/macro_tools/src/attr_prop.rs | 3 + .../core/macro_tools/src/attr_prop/boolean.rs | 7 +- .../src/attr_prop/boolean_optional.rs | 12 +- .../macro_tools/src/attr_prop/singletone.rs | 7 +- .../src/attr_prop/singletone_optional.rs | 15 +- module/core/macro_tools/src/attr_prop/syn.rs | 5 +- .../macro_tools/src/attr_prop/syn_optional.rs | 13 +- module/core/macro_tools/src/components.rs | 3 + module/core/macro_tools/src/container_kind.rs | 15 +- module/core/macro_tools/src/ct.rs | 3 + module/core/macro_tools/src/derive.rs | 9 +- module/core/macro_tools/src/diag.rs | 18 +- module/core/macro_tools/src/equation.rs | 15 +- module/core/macro_tools/src/generic_args.rs | 6 +- module/core/macro_tools/src/generic_params.rs | 38 ++-- module/core/macro_tools/src/item.rs | 11 +- module/core/macro_tools/src/item_struct.rs | 27 ++- module/core/macro_tools/src/iter.rs | 3 + module/core/macro_tools/src/kw.rs | 4 + module/core/macro_tools/src/lib.rs | 11 +- module/core/macro_tools/src/name.rs | 12 +- module/core/macro_tools/src/phantom.rs | 8 +- module/core/macro_tools/src/punctuated.rs | 2 + module/core/macro_tools/src/quantifier.rs | 8 + module/core/macro_tools/src/struct_like.rs | 65 +++--- module/core/macro_tools/src/tokens.rs | 9 +- module/core/macro_tools/src/typ.rs | 21 +- module/core/macro_tools/src/typed.rs | 3 + module/core/mod_interface/Readme.md | 44 +---- module/core/mod_interface/src/lib.rs | 3 + module/core/mod_interface_meta/Readme.md | 6 +- module/core/mod_interface_meta/src/impls.rs | 11 +- module/core/mod_interface_meta/src/lib.rs | 3 + module/core/mod_interface_meta/src/record.rs | 12 +- .../core/mod_interface_meta/src/use_tree.rs | 10 +- .../core/mod_interface_meta/src/visibility.rs | 10 +- module/core/process_tools/Readme.md | 2 +- module/core/process_tools/src/environment.rs | 5 +- module/core/process_tools/src/process.rs | 32 +-- module/core/pth/Readme.md | 6 +- module/core/pth/src/as_path.rs | 2 +- module/core/pth/src/lib.rs | 1 + module/core/pth/src/path.rs | 62 +++--- module/core/pth/src/path/absolute_path.rs | 15 +- module/core/pth/src/path/canonical_path.rs | 8 +- module/core/pth/src/path/current_path.rs | 3 +- module/core/pth/src/path/joining.rs | 5 + module/core/pth/src/path/native_path.rs | 8 +- module/core/pth/src/transitive.rs | 5 + module/core/pth/src/try_into_cow_path.rs | 4 + module/core/pth/src/try_into_path.rs | 4 +- module/core/strs_tools/Readme.md | 2 +- module/core/strs_tools/src/lib.rs | 2 + .../core/strs_tools/src/string/indentation.rs | 11 +- module/core/strs_tools/src/string/isolate.rs | 6 +- module/core/strs_tools/src/string/mod.rs | 3 + module/core/strs_tools/src/string/number.rs | 7 +- .../strs_tools/src/string/parse_request.rs | 17 +- module/core/strs_tools/src/string/split.rs | 37 ++-- module/move/crates_tools/Readme.md | 3 +- module/move/crates_tools/src/lib.rs | 76 ++++--- module/move/optimization_tools/Cargo.toml | 6 +- module/move/unitore/Cargo.toml | 2 +- module/move/wca/src/ca/aggregator.rs | 20 +- module/move/wca/src/ca/executor/context.rs | 4 +- module/move/wca/src/ca/executor/executor.rs | 9 +- module/move/wca/src/ca/executor/routine.rs | 17 +- module/move/wca/src/ca/formatter.rs | 14 +- module/move/wca/src/ca/grammar/command.rs | 10 +- module/move/wca/src/ca/grammar/dictionary.rs | 7 +- module/move/wca/src/ca/grammar/types.rs | 12 +- module/move/wca/src/ca/help.rs | 28 ++- module/move/wca/src/ca/input.rs | 3 +- module/move/wca/src/ca/parser/command.rs | 2 +- module/move/wca/src/ca/parser/parser.rs | 7 +- module/move/wca/src/ca/tool/table.rs | 5 +- module/move/wca/src/ca/verifier/command.rs | 3 +- module/move/wca/src/ca/verifier/verifier.rs | 16 +- module/move/willbe/Cargo.toml | 4 +- module/move/willbe/Readme.md | 2 +- module/move/willbe/src/action/cicd_renew.rs | 19 +- module/move/willbe/src/action/deploy_renew.rs | 13 +- module/move/willbe/src/action/features.rs | 7 +- module/move/willbe/src/action/list.rs | 29 +-- module/move/willbe/src/action/main_header.rs | 12 ++ module/move/willbe/src/action/publish.rs | 8 +- module/move/willbe/src/action/publish_diff.rs | 13 +- .../src/action/readme_health_table_renew.rs | 185 +++++++++--------- .../action/readme_modules_headers_renew.rs | 27 ++- module/move/willbe/src/action/test.rs | 9 + .../move/willbe/src/action/workspace_renew.rs | 13 +- module/move/willbe/src/bin/cargo-will.rs | 4 +- module/move/willbe/src/bin/will.rs | 4 +- module/move/willbe/src/bin/willbe.rs | 4 +- module/move/willbe/src/command/cicd_renew.rs | 3 + .../move/willbe/src/command/deploy_renew.rs | 5 + module/move/willbe/src/command/features.rs | 4 + module/move/willbe/src/command/list.rs | 7 +- module/move/willbe/src/command/main_header.rs | 4 + module/move/willbe/src/command/mod.rs | 2 + module/move/willbe/src/command/publish.rs | 24 +-- .../move/willbe/src/command/publish_diff.rs | 4 + .../src/command/readme_headers_renew.rs | 4 + .../src/command/readme_health_table_renew.rs | 3 + .../command/readme_modules_headers_renew.rs | 4 + module/move/willbe/src/command/test.rs | 24 +-- .../willbe/src/command/workspace_renew.rs | 4 + module/move/willbe/src/entity/channel.rs | 6 + module/move/willbe/src/entity/code.rs | 6 +- module/move/willbe/src/entity/dependency.rs | 26 ++- module/move/willbe/src/entity/diff.rs | 23 ++- module/move/willbe/src/entity/features.rs | 8 +- module/move/willbe/src/entity/files.rs | 2 + .../move/willbe/src/entity/files/crate_dir.rs | 6 + module/move/willbe/src/entity/files/either.rs | 6 +- .../willbe/src/entity/files/manifest_file.rs | 5 + .../willbe/src/entity/files/source_file.rs | 11 +- module/move/willbe/src/entity/git.rs | 5 + module/move/willbe/src/entity/manifest.rs | 18 +- module/move/willbe/src/entity/package.rs | 23 ++- .../willbe/src/entity/package_md_extension.rs | 30 ++- module/move/willbe/src/entity/packages.rs | 3 + module/move/willbe/src/entity/packed_crate.rs | 14 +- module/move/willbe/src/entity/progress_bar.rs | 4 +- module/move/willbe/src/entity/publish.rs | 25 ++- module/move/willbe/src/entity/table.rs | 11 +- module/move/willbe/src/entity/test.rs | 66 +++++-- module/move/willbe/src/entity/version.rs | 32 ++- module/move/willbe/src/entity/workspace.rs | 23 ++- .../move/willbe/src/entity/workspace_graph.rs | 3 + .../src/entity/workspace_md_extension.rs | 12 +- .../willbe/src/entity/workspace_package.rs | 29 ++- module/move/willbe/src/lib.rs | 4 + module/move/willbe/src/tool/cargo.rs | 12 +- module/move/willbe/src/tool/files.rs | 7 +- module/move/willbe/src/tool/git.rs | 15 +- module/move/willbe/src/tool/graph.rs | 37 +++- module/move/willbe/src/tool/http.rs | 11 +- module/move/willbe/src/tool/query.rs | 19 +- module/move/willbe/src/tool/repository.rs | 5 +- module/move/willbe/src/tool/template.rs | 46 +++-- module/move/willbe/src/tool/tree.rs | 24 ++- module/move/willbe/src/tool/url.rs | 11 +- .../tests/asset/single_module/Cargo.toml | 1 + .../readme_modules_headers_renew.rs | 20 +- 209 files changed, 1936 insertions(+), 870 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 0e22e76fa4..cfbea9f300 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,9 +29,9 @@ discord_url = "https://discord.gg/m3YfbXpUUY" # Source :: https://github.com/obox-systems/conventions/blob/master/code_style.md#lints-and-warnings # Denies non-idiomatic code for Rust 2018 edition. -rust_2018_idioms = "deny" +rust_2018_idioms = { level = "deny", priority = -1 } # Denies using features that may break in future Rust versions. -future_incompatible = "deny" +future_incompatible = { level = "deny", priority = -1 } # Warns if public items lack documentation. missing_docs = "warn" # Warns for public types not implementing Debug. @@ -41,9 +41,9 @@ unsafe-code = "warn" [workspace.lints.clippy] # Denies restrictive lints, limiting certain language features/patterns. -restriction = "warn" +#restriction = { level = "deny", priority = -1 } # Denies pedantic lints, enforcing strict coding styles and conventions. -pedantic = "warn" +pedantic = { level = "warn", priority = -1 } # Denies undocumented unsafe blocks. undocumented_unsafe_blocks = "deny" # xxx : check diff --git a/module/core/clone_dyn_types/Readme.md b/module/core/clone_dyn_types/Readme.md index 12cc1e5f46..0a3caaae26 100644 --- a/module/core/clone_dyn_types/Readme.md +++ b/module/core/clone_dyn_types/Readme.md @@ -1,5 +1,5 @@ -# Module :: clone_dyn_types +# Module :: `clone_dyn_types` [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) [![rust-status](https://github.com/Wandalen/wTools/actions/workflows/module_clone_dyn_push.yml/badge.svg)](https://github.com/Wandalen/wTools/actions/workflows/module_clone_dyn_push.yml) [![docs.rs](https://img.shields.io/docsrs/clone_dyn_types?color=e3e8f0&logo=docs.rs)](https://docs.rs/clone_dyn_types) [![Open in Gitpod](https://raster.shields.io/static/v1?label=try&message=online&color=eee&logo=gitpod&logoColor=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=module%2Fcore%2Fclone_dyn%2Fexamples%2Fclone_dyn_trivial.rs,RUN_POSTFIX=--example%20clone_dyn_trivial/https://github.com/Wandalen/wTools) [![discord](https://img.shields.io/discord/872391416519737405?color=eee&logo=discord&logoColor=eee&label=ask)](https://discord.gg/m3YfbXpUUY) diff --git a/module/core/clone_dyn_types/src/lib.rs b/module/core/clone_dyn_types/src/lib.rs index cc55badce4..2642542d03 100644 --- a/module/core/clone_dyn_types/src/lib.rs +++ b/module/core/clone_dyn_types/src/lib.rs @@ -39,6 +39,7 @@ mod private T : Clone, { #[ inline ] + #[ allow( clippy::implicit_return, clippy::as_conversions, clippy::ptr_as_ptr ) ] fn __clone_dyn( &self, _ : DontCallMe ) -> *mut () { Box::< T >::into_raw( Box::new( self.clone() ) ) as *mut () @@ -51,6 +52,7 @@ mod private T : Clone, { #[ inline ] + #[ allow( clippy::implicit_return, clippy::as_conversions, clippy::ptr_as_ptr ) ] fn __clone_dyn( &self, _ : DontCallMe ) -> *mut () { Box::< [ T ] >::into_raw( self.iter().cloned().collect() ) as *mut () @@ -61,6 +63,7 @@ mod private impl CloneDyn for str { #[ inline ] + #[ allow( clippy::as_conversions, clippy::ptr_as_ptr, clippy::implicit_return ) ] fn __clone_dyn( &self, _ : DontCallMe ) -> *mut () { Box::< str >::into_raw( Box::from( self ) ) as *mut () @@ -68,7 +71,7 @@ mod private } /// - /// True clone which is applicable not only to clonable entities, but to trait objects implementing CloneDyn. + /// True clone which is applicable not only to clonable entities, but to trait objects implementing `CloneDyn`. /// /// # Example /// @@ -100,7 +103,7 @@ mod private // that the `CloneDyn` trait is correctly implemented for the given type `T`, ensuring that `__clone_dyn` returns a // valid pointer to a cloned instance of `T`. // - #[ allow( unsafe_code ) ] + #[ allow( unsafe_code, clippy::as_conversions, clippy::ptr_as_ptr, clippy::implicit_return, clippy::undocumented_unsafe_blocks ) ] unsafe { *Box::from_raw( < T as CloneDyn >::__clone_dyn( src, DontCallMe ) as *mut T ) @@ -185,7 +188,7 @@ mod private // The safety of this function relies on the correct implementation of the `CloneDyn` trait for the given type `T`. // Specifically, `__clone_dyn` must return a valid pointer to a cloned instance of `T`. // - #[ allow( unsafe_code ) ] + #[ allow( unsafe_code, clippy::implicit_return, clippy::as_conversions, clippy::ptr_cast_constness, clippy::ptr_as_ptr, clippy::multiple_unsafe_ops_per_block, clippy::undocumented_unsafe_blocks, clippy::ref_as_ptr ) ] unsafe { let mut ptr = ref_dyn as *const T; @@ -207,13 +210,14 @@ mod private impl< T : Clone > Sealed for [ T ] {} impl Sealed for str {} } - use sealed::*; + use sealed::{ DontCallMe, Sealed }; } #[ cfg( feature = "enabled" ) ] #[ doc( inline ) ] #[ allow( unused_imports ) ] +#[ allow( clippy::pub_use ) ] pub use own::*; /// Own namespace of the module. @@ -221,8 +225,9 @@ pub use own::*; #[ allow( unused_imports ) ] pub mod own { - use super::*; + use super::orphan; #[ doc( inline ) ] + #[ allow( clippy::useless_attribute, clippy::pub_use ) ] pub use orphan::*; } @@ -231,8 +236,9 @@ pub mod own #[ allow( unused_imports ) ] pub mod orphan { - use super::*; + use super::exposed; #[ doc( inline ) ] + #[ allow( clippy::useless_attribute, clippy::pub_use ) ] pub use exposed::*; } @@ -241,8 +247,9 @@ pub mod orphan #[ allow( unused_imports ) ] pub mod exposed { - use super::*; + use super::prelude; #[ doc( inline ) ] + #[ allow( clippy::useless_attribute, clippy::pub_use ) ] pub use prelude::*; } @@ -251,8 +258,9 @@ pub mod exposed #[ allow( unused_imports ) ] pub mod prelude { - use super::*; + use super::private; #[ doc( inline ) ] + #[ allow( clippy::useless_attribute, clippy::pub_use ) ] pub use private:: { CloneDyn, diff --git a/module/core/collection_tools/Readme.md b/module/core/collection_tools/Readme.md index 2b6f2b0ab6..b247a692b0 100644 --- a/module/core/collection_tools/Readme.md +++ b/module/core/collection_tools/Readme.md @@ -1,6 +1,6 @@ -# Module :: collection_tools +# Module :: `collection_tools` [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) [![rust-status](https://github.com/Wandalen/wTools/actions/workflows/module_collection_tools_push.yml/badge.svg)](https://github.com/Wandalen/wTools/actions/workflows/module_collection_tools_push.yml) [![docs.rs](https://img.shields.io/docsrs/collection_tools?color=e3e8f0&logo=docs.rs)](https://docs.rs/collection_tools) [![Open in Gitpod](https://raster.shields.io/static/v1?label=try&message=online&color=eee&logo=gitpod&logoColor=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=module%2Fcore%2Fcollection_tools%2Fexamples%2Fcollection_tools_trivial.rs,RUN_POSTFIX=--example%20collection_tools_trivial/https://github.com/Wandalen/wTools) [![discord](https://img.shields.io/discord/872391416519737405?color=eee&logo=discord&logoColor=eee&label=ask)](https://discord.gg/m3YfbXpUUY) diff --git a/module/core/collection_tools/src/collection/binary_heap.rs b/module/core/collection_tools/src/collection/binary_heap.rs index 965f5804c5..faaa934427 100644 --- a/module/core/collection_tools/src/collection/binary_heap.rs +++ b/module/core/collection_tools/src/collection/binary_heap.rs @@ -1,8 +1,9 @@ -#[ allow( unused_imports ) ] +#[ allow( unused_imports, clippy::wildcard_imports ) ] use super::*; #[ doc( inline ) ] #[ allow( unused_imports ) ] +#[ allow( clippy::pub_use ) ] pub use alloc::collections::binary_heap::*; /// Creates a `BinaryHeap` from a list of elements. @@ -32,8 +33,8 @@ pub use alloc::collections::binary_heap::*; /// # Parameters /// /// - `$( $key:expr ),* $( , )?`: A comma-separated list of elements to insert into the `BinaryHeap`. -/// Each element can be of any type that implements the `Into` trait, where `T` is the -/// type stored in the `BinaryHeap`. +/// Each element can be of any type that implements the `Into` trait, where `T` is the +/// type stored in the `BinaryHeap`. /// /// # Returns /// @@ -101,8 +102,8 @@ macro_rules! heap /// # Parameters /// /// - `$( $key:expr ),* $( , )?`: A comma-separated list of elements to insert into the `BinaryHeap`. -/// Each element can be of any type that implements the `Into` trait, where `T` is the -/// type stored in the `BinaryHeap`. +/// Each element can be of any type that implements the `Into` trait, where `T` is the +/// type stored in the `BinaryHeap`. /// /// # Returns /// diff --git a/module/core/collection_tools/src/collection/btree_map.rs b/module/core/collection_tools/src/collection/btree_map.rs index 2e4ec94f13..fc79de564b 100644 --- a/module/core/collection_tools/src/collection/btree_map.rs +++ b/module/core/collection_tools/src/collection/btree_map.rs @@ -1,8 +1,9 @@ -#[ allow( unused_imports ) ] +#[ allow( unused_imports, clippy::wildcard_imports ) ] use super::*; #[ doc( inline ) ] #[ allow( unused_imports ) ] +#[ allow( clippy::pub_use ) ] pub use alloc::collections::btree_map::*; /// Creates a `BTreeMap` from a list of key-value pairs. @@ -32,8 +33,8 @@ pub use alloc::collections::btree_map::*; /// # Parameters /// /// - `$( $key:expr => $value:expr ),* $( , )?`: A comma-separated list of key-value pairs to insert into the `BTreeMap`. -/// Each key and value can be of any type that implements the `Into< K >` and `Into< V >` traits, where `K` and `V` are the -/// types stored in the `BTreeMap` as keys and values, respectively. +/// Each key and value can be of any type that implements the `Into< K >` and `Into< V >` traits, where `K` and `V` are the +/// types stored in the `BTreeMap` as keys and values, respectively. /// /// # Returns /// @@ -114,8 +115,8 @@ macro_rules! bmap /// # Parameters /// /// - `$( $key:expr => $value:expr ),* $( , )?`: A comma-separated list of key-value pairs to insert into the `BTreeMap`. -/// Each key and value can be of any type that implements the `Into< K >` and `Into< V >` traits, where `K` and `V` are the -/// types stored in the `BTreeMap` as keys and values, respectively. +/// Each key and value can be of any type that implements the `Into< K >` and `Into< V >` traits, where `K` and `V` are the +/// types stored in the `BTreeMap` as keys and values, respectively. /// /// # Returns /// diff --git a/module/core/collection_tools/src/collection/btree_set.rs b/module/core/collection_tools/src/collection/btree_set.rs index 7111811c2e..d7b22ababc 100644 --- a/module/core/collection_tools/src/collection/btree_set.rs +++ b/module/core/collection_tools/src/collection/btree_set.rs @@ -1,8 +1,9 @@ -#[ allow( unused_imports ) ] +#[ allow( unused_imports, clippy::wildcard_imports ) ] use super::*; #[ doc( inline ) ] #[ allow( unused_imports ) ] +#[ allow( clippy::pub_use ) ] pub use alloc::collections::btree_set::*; /// Creates a `BTreeSet` from a list of elements. @@ -29,8 +30,8 @@ pub use alloc::collections::btree_set::*; /// # Parameters /// /// - `$( $key:expr ),* $( , )?`: A comma-separated list of elements to insert into the `BTreeSet`. -/// Each element can be of any type that implements the `Into` trait, where `T` is the -/// type stored in the `BTreeSet`. +/// Each element can be of any type that implements the `Into` trait, where `T` is the +/// type stored in the `BTreeSet`. /// /// # Returns /// @@ -100,8 +101,8 @@ macro_rules! bset /// # Parameters /// /// - `$( $key:expr ),* $( , )?`: A comma-separated list of elements to insert into the `BTreeSet`. -/// Each element can be of any type that implements the `Into` trait, where `T` is the -/// type stored in the `BTreeSet`. +/// Each element can be of any type that implements the `Into` trait, where `T` is the +/// type stored in the `BTreeSet`. /// /// # Returns /// diff --git a/module/core/collection_tools/src/collection/hash_map.rs b/module/core/collection_tools/src/collection/hash_map.rs index 8ebbc9f90f..2b2a8226a6 100644 --- a/module/core/collection_tools/src/collection/hash_map.rs +++ b/module/core/collection_tools/src/collection/hash_map.rs @@ -10,6 +10,7 @@ pub use crate::dependency::hashbrown::hash_map::*; #[ cfg( not( feature = "no_std" ) ) ] #[ doc( inline ) ] #[ allow( unused_imports ) ] +#[ allow( clippy::pub_use ) ] pub use std::collections::hash_map::*; /// Creates a `HashMap` from a list of key-value pairs. @@ -41,8 +42,8 @@ pub use std::collections::hash_map::*; /// # Parameters /// /// - `$( $key:expr => $value:expr ),* $( , )?`: A comma-separated list of key-value pairs to insert into the `HashMap`. -/// Each key and value can be of any type that implements the `Into` and `Into` traits, where `K` and `V` are the -/// types stored in the `HashMap` as keys and values, respectively. +/// Each key and value can be of any type that implements the `Into` and `Into` traits, where `K` and `V` are the +/// types stored in the `HashMap` as keys and values, respectively. /// /// # Returns /// @@ -125,8 +126,8 @@ macro_rules! hmap /// # Parameters /// /// - `$( $key:expr => $value:expr ),* $( , )?`: A comma-separated list of key-value pairs to insert into the `HashMap`. -/// Each key and value can be of any type that implements the `Into` and `Into` traits, where `K` and `V` are the -/// types stored in the `HashMap` as keys and values, respectively. +/// Each key and value can be of any type that implements the `Into` and `Into` traits, where `K` and `V` are the +/// types stored in the `HashMap` as keys and values, respectively. /// /// # Returns /// diff --git a/module/core/collection_tools/src/collection/hash_set.rs b/module/core/collection_tools/src/collection/hash_set.rs index 6fe8d8287a..f2a73c5faf 100644 --- a/module/core/collection_tools/src/collection/hash_set.rs +++ b/module/core/collection_tools/src/collection/hash_set.rs @@ -9,6 +9,7 @@ pub use crate::dependency::hashbrown::hash_set::*; #[ cfg( not( feature = "no_std" ) ) ] #[ doc( inline ) ] #[ allow( unused_imports ) ] +#[ allow( clippy::pub_use ) ] pub use std::collections::hash_set::*; /// Creates a `HashSet` from a list of elements. @@ -40,8 +41,8 @@ pub use std::collections::hash_set::*; /// # Parameters /// /// - `$( $key:expr ),* $( , )?`: A comma-separated list of elements to insert into the `HashSet`. -/// Each element can be of any type that implements the `Into< T >` trait, where `T` is the -/// type stored in the `HashSet`. +/// Each element can be of any type that implements the `Into< T >` trait, where `T` is the +/// type stored in the `HashSet`. /// /// # Returns /// @@ -124,8 +125,8 @@ macro_rules! hset /// # Parameters /// /// - `$( $key:expr ),* $( , )?`: A comma-separated list of elements to insert into the `HashSet`. -/// Each element can be of any type that implements the `Into< T >` trait, where `T` is the -/// type stored in the `HashSet`. +/// Each element can be of any type that implements the `Into< T >` trait, where `T` is the +/// type stored in the `HashSet`. /// /// # Returns /// diff --git a/module/core/collection_tools/src/collection/linked_list.rs b/module/core/collection_tools/src/collection/linked_list.rs index cc23637be1..7fbaba79fa 100644 --- a/module/core/collection_tools/src/collection/linked_list.rs +++ b/module/core/collection_tools/src/collection/linked_list.rs @@ -1,8 +1,9 @@ -#[ allow( unused_imports ) ] +#[ allow( unused_imports, clippy::wildcard_imports ) ] use super::*; #[ doc( inline ) ] #[ allow( unused_imports ) ] +#[ allow( clippy::pub_use ) ] pub use alloc::collections::linked_list::*; /// Creates a `LinkedList` from a llist of elements. @@ -32,8 +33,8 @@ pub use alloc::collections::linked_list::*; /// # Parameters /// /// - `$( $key:expr ),* $( , )?`: A comma-separated llist of elements to insert into the `LinkedList`. -/// Each element can be of any type that implements the `Into` trait, where `T` is the -/// type stored in the `LinkedList`. +/// Each element can be of any type that implements the `Into` trait, where `T` is the +/// type stored in the `LinkedList`. /// /// # Returns /// @@ -114,8 +115,8 @@ macro_rules! llist /// # Parameters /// /// - `$( $key:expr ),* $( , )?`: A comma-separated llist of elements to insert into the `LinkedList`. -/// Each element can be of any type that implements the `Into` trait, where `T` is the -/// type stored in the `LinkedList`. +/// Each element can be of any type that implements the `Into` trait, where `T` is the +/// type stored in the `LinkedList`. /// /// # Returns /// diff --git a/module/core/collection_tools/src/collection/mod.rs b/module/core/collection_tools/src/collection/mod.rs index 0f74158835..22e59e8aae 100644 --- a/module/core/collection_tools/src/collection/mod.rs +++ b/module/core/collection_tools/src/collection/mod.rs @@ -18,26 +18,27 @@ macro_rules! count #[ cfg( any( feature = "use_alloc", not( feature = "no_std" ) ) ) ] extern crate alloc; -/// [std::collections::BTreeMap] macros +/// [`std::collections::BTreeMap`] macros pub mod btree_map; -/// [std::collections::BTreeSet] macros +/// [`std::collections::BTreeSet`] macros pub mod btree_set; -/// [std::collections::BinaryHeap] macros +/// [`std::collections::BinaryHeap`] macros pub mod binary_heap; -/// [std::collections::HashMap] macros +/// [`std::collections::HashMap`] macros pub mod hash_map; -/// [std::collections::HashSet] macros +/// [`std::collections::HashSet`] macros pub mod hash_set; -/// [std::collections::LinkedList] macros +/// [`std::collections::LinkedList`] macros pub mod linked_list; /// [Vec] macros pub mod vector; -/// [std::collections::VecDeque] macros +/// [`std::collections::VecDeque`] macros pub mod vec_deque; #[ doc( inline ) ] #[ allow( unused_imports ) ] #[ cfg( feature = "enabled" ) ] +#[ allow( clippy::pub_use ) ] pub use own::*; /// Own namespace of the module. @@ -45,8 +46,10 @@ pub use own::*; #[ allow( unused_imports ) ] pub mod own { + #[ allow( clippy::wildcard_imports ) ] use super::*; + #[ allow( clippy::useless_attribute, clippy::pub_use ) ] pub use super:: { btree_map, @@ -60,6 +63,7 @@ pub mod own }; #[ doc( inline ) ] + #[ allow( clippy::useless_attribute, clippy::pub_use ) ] pub use orphan::*; } @@ -69,8 +73,10 @@ pub mod own #[ allow( unused_imports ) ] pub mod orphan { + #[ allow( clippy::wildcard_imports ) ] use super::*; #[ doc( inline ) ] + #[ allow( clippy::useless_attribute, clippy::pub_use ) ] pub use exposed::*; } @@ -79,17 +85,21 @@ pub mod orphan #[ allow( unused_imports ) ] pub mod exposed { + #[ allow( clippy::wildcard_imports ) ] use super::*; #[ doc( inline ) ] + #[ allow( clippy::useless_attribute, clippy::pub_use ) ] pub use prelude::*; #[ doc( inline ) ] + #[ allow( clippy::useless_attribute, clippy::pub_use ) ] pub use super::super::collection; #[ doc( inline ) ] #[ cfg( any( feature = "use_alloc", not( feature = "no_std" ) ) ) ] #[ cfg( feature = "collection_constructors" ) ] + #[ allow( clippy::useless_attribute, clippy::pub_use ) ] pub use crate:: { vec as dlist, @@ -104,6 +114,7 @@ pub mod exposed #[ doc( inline ) ] #[ cfg( any( feature = "use_alloc", not( feature = "no_std" ) ) ) ] #[ cfg( feature = "collection_into_constructors" ) ] + #[ allow( clippy::useless_attribute, clippy::pub_use ) ] pub use crate:: { into_vec, @@ -119,6 +130,7 @@ pub mod exposed // #[ cfg( feature = "reexports" ) ] #[ cfg( any( feature = "use_alloc", not( feature = "no_std" ) ) ) ] #[ doc( inline ) ] + #[ allow( clippy::useless_attribute, clippy::pub_use ) ] pub use { btree_map::BTreeMap, @@ -134,6 +146,7 @@ pub mod exposed // #[ cfg( feature = "reexports" ) ] #[ cfg( any( feature = "use_alloc", not( feature = "no_std" ) ) ) ] #[ doc( inline ) ] + #[ allow( clippy::useless_attribute, clippy::pub_use ) ] pub use { LinkedList as Llist, diff --git a/module/core/collection_tools/src/collection/vec_deque.rs b/module/core/collection_tools/src/collection/vec_deque.rs index 1060d01c0b..218f64e7ed 100644 --- a/module/core/collection_tools/src/collection/vec_deque.rs +++ b/module/core/collection_tools/src/collection/vec_deque.rs @@ -1,8 +1,9 @@ -#[ allow( unused_imports ) ] +#[ allow( unused_imports, clippy::wildcard_imports ) ] use super::*; #[ doc( inline ) ] #[ allow( unused_imports ) ] +#[ allow( clippy::pub_use ) ] pub use alloc::collections::vec_deque::*; /// Creates a `VecDeque` from a list of elements. @@ -38,8 +39,8 @@ pub use alloc::collections::vec_deque::*; /// # Parameters /// /// - `$( $key:expr ),* $( , )?`: A comma-separated list of elements to insert into the `VecDeque`. -/// Each element can be of any type that implements the `Into< T >` trait, where `T` is the -/// type stored in the `VecDeque`. +/// Each element can be of any type that implements the `Into< T >` trait, where `T` is the +/// type stored in the `VecDeque`. /// /// # Returns /// @@ -119,8 +120,8 @@ macro_rules! deque /// # Parameters /// /// - `$( $key:expr ),* $( , )?`: A comma-separated list of elements to insert into the `VecDeque`. -/// Each element can be of any type that implements the `Into< T >` trait, where `T` is the -/// type stored in the `VecDeque`. +/// Each element can be of any type that implements the `Into< T >` trait, where `T` is the +/// type stored in the `VecDeque`. /// /// # Returns /// diff --git a/module/core/collection_tools/src/collection/vector.rs b/module/core/collection_tools/src/collection/vector.rs index 0ae9ccf6dd..568642d0c4 100644 --- a/module/core/collection_tools/src/collection/vector.rs +++ b/module/core/collection_tools/src/collection/vector.rs @@ -1,12 +1,14 @@ -#[ allow( unused_imports ) ] +#[ allow( unused_imports, clippy::wildcard_imports ) ] use super::*; #[ doc( inline ) ] #[ allow( unused_imports ) ] +#[ allow( clippy::pub_use ) ] pub use alloc::vec::*; #[ doc( inline ) ] #[ allow( unused_imports ) ] +#[ allow( clippy::pub_use ) ] pub use core::slice::{ Iter, IterMut }; /// Creates a `Vec` from a list of elements. @@ -36,8 +38,8 @@ pub use core::slice::{ Iter, IterMut }; /// # Parameters /// /// - `$( $key : expr ),* $( , )?`: A comma-separated list of elements to insert into the `Vec`. -/// Each element can be of any type that implements the `Into` trait, where `T` is the -/// type stored in the `Vec`. +/// Each element can be of any type that implements the `Into` trait, where `T` is the +/// type stored in the `Vec`. /// /// # Returns /// @@ -118,8 +120,8 @@ macro_rules! vec /// # Parameters /// /// - `$( $key : expr ),* $( , )?`: A comma-separated list of elements to insert into the `Vec`. -/// Each element can be of any type that implements the `Into` trait, where `T` is the -/// type stored in the `Vec`. +/// Each element can be of any type that implements the `Into` trait, where `T` is the +/// type stored in the `Vec`. /// /// # Returns /// diff --git a/module/core/collection_tools/src/lib.rs b/module/core/collection_tools/src/lib.rs index bcbbb4bdbc..18b8e84037 100644 --- a/module/core/collection_tools/src/lib.rs +++ b/module/core/collection_tools/src/lib.rs @@ -3,7 +3,7 @@ #![ doc( html_favicon_url = "https://raw.githubusercontent.com/Wandalen/wTools/alpha/asset/img/logo_v3_trans_square_icon_small_v2.ico" ) ] #![ doc( html_root_url = "https://docs.rs/collection_tools/latest/collection_tools/" ) ] #![ doc = include_str!( concat!( env!( "CARGO_MANIFEST_DIR" ), "/", "Readme.md" ) ) ] - +#![ allow( clippy::mod_module_files ) ] // #[ cfg( feature = "enabled" ) ] // #[ cfg( any( feature = "use_alloc", not( feature = "no_std" ) ) ) ] // extern crate alloc; @@ -30,6 +30,7 @@ pub mod dependency #[ doc( inline ) ] #[ allow( unused_imports ) ] #[ cfg( feature = "enabled" ) ] +#[ allow( clippy::pub_use ) ] pub use own::*; /// Own namespace of the module. @@ -40,9 +41,11 @@ pub mod own // use super::*; #[ doc( inline ) ] + #[ allow( clippy::useless_attribute, clippy::pub_use ) ] pub use super::orphan::*; #[ doc( inline ) ] + #[ allow( clippy::useless_attribute, clippy::pub_use ) ] pub use super::collection::own::*; } @@ -52,11 +55,14 @@ pub mod own #[ allow( unused_imports ) ] pub mod orphan { + #[ allow( clippy::wildcard_imports ) ] use super::*; #[ doc( inline ) ] + #[ allow( clippy::useless_attribute, clippy::pub_use ) ] pub use exposed::*; #[ doc( inline ) ] + #[ allow( clippy::useless_attribute, clippy::pub_use ) ] pub use collection::orphan::*; } @@ -66,24 +72,29 @@ pub mod orphan #[ allow( unused_imports ) ] pub mod exposed { + #[ allow( clippy::wildcard_imports ) ] use super::*; #[ doc( inline ) ] + #[ allow( clippy::useless_attribute, clippy::pub_use ) ] pub use prelude::*; #[ doc( inline ) ] + #[ allow( clippy::useless_attribute, clippy::pub_use ) ] pub use collection::exposed::*; } /// Prelude to use essentials: `use my_module::prelude::*`. #[ cfg( feature = "enabled" ) ] +#[ cfg( any( feature = "use_alloc", not( feature = "no_std" ) ) ) ] #[ allow( unused_imports ) ] pub mod prelude { - use super::*; + use super::collection; #[ doc( inline ) ] + #[ allow( clippy::useless_attribute, clippy::pub_use ) ] pub use collection::prelude::*; } diff --git a/module/core/data_type/Readme.md b/module/core/data_type/Readme.md index 62c1031498..ddadd7fb57 100644 --- a/module/core/data_type/Readme.md +++ b/module/core/data_type/Readme.md @@ -1,6 +1,6 @@ -# Module :: data_type +# Module :: `data_type` [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) [![rust-status](https://github.com/Wandalen/wTools/actions/workflows/module_data_type_push.yml/badge.svg)](https://github.com/Wandalen/wTools/actions/workflows/module_data_type_push.yml) [![docs.rs](https://img.shields.io/docsrs/data_type?color=e3e8f0&logo=docs.rs)](https://docs.rs/data_type) [![Open in Gitpod](https://raster.shields.io/static/v1?label=try&message=online&color=eee&logo=gitpod&logoColor=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=module%2Fcore%2Fdata_type%2Fexamples%2Fdata_type_trivial.rs,RUN_POSTFIX=--example%20data_type_trivial/https://github.com/Wandalen/wTools) [![discord](https://img.shields.io/discord/872391416519737405?color=eee&logo=discord&logoColor=eee&label=ask)](https://discord.gg/m3YfbXpUUY) @@ -30,7 +30,7 @@ Macro [types](https://docs.rs/type_constructor/latest/type_constructor/types/mac ### Basic Use Case :: make - variadic constructor -Implement traits [From_0], [From1] up to MakeN to provide the interface to construct your structure with a different set of arguments. +Implement traits [`From_0`], [From1] up to `MakeN` to provide the interface to construct your structure with a different set of arguments. In this example structure, Struct1 could be constructed either without arguments, with a single argument, or with two arguments. - Constructor without arguments fills fields with zero. - Constructor with a single argument sets both fields to the value of the argument. diff --git a/module/core/data_type/src/dt.rs b/module/core/data_type/src/dt.rs index f384f56072..91b3babd3d 100644 --- a/module/core/data_type/src/dt.rs +++ b/module/core/data_type/src/dt.rs @@ -11,6 +11,7 @@ pub use own::*; #[ allow( unused_imports ) ] pub mod own { + #[ allow( clippy::wildcard_imports ) ] use super::*; #[ doc( inline ) ] pub use orphan::*; @@ -20,6 +21,7 @@ pub mod own #[ allow( unused_imports ) ] pub mod orphan { + #[ allow( clippy::wildcard_imports ) ] use super::*; #[ doc( inline ) ] pub use exposed::*; @@ -29,6 +31,7 @@ pub mod orphan #[ allow( unused_imports ) ] pub mod exposed { + #[ allow( clippy::wildcard_imports ) ] use super::*; #[ doc( inline ) ] diff --git a/module/core/data_type/src/lib.rs b/module/core/data_type/src/lib.rs index 7cdff4fae2..b645eb9a71 100644 --- a/module/core/data_type/src/lib.rs +++ b/module/core/data_type/src/lib.rs @@ -33,6 +33,7 @@ pub use own::*; #[ allow( unused_imports ) ] pub mod own { + #[ allow( clippy::wildcard_imports ) ] use super::*; #[ doc( inline ) ] pub use orphan::*; @@ -45,6 +46,7 @@ pub mod own #[ allow( unused_imports ) ] pub mod orphan { + #[ allow( clippy::wildcard_imports ) ] use super::*; #[ doc( inline ) ] pub use exposed::*; @@ -54,6 +56,7 @@ pub mod orphan #[ allow( unused_imports ) ] pub mod exposed { + #[ allow( clippy::wildcard_imports ) ] use super::*; #[ doc( inline ) ] diff --git a/module/core/derive_tools/Readme.md b/module/core/derive_tools/Readme.md index 746b6e4ec7..f5e6fddf2e 100644 --- a/module/core/derive_tools/Readme.md +++ b/module/core/derive_tools/Readme.md @@ -1,4 +1,4 @@ -# Module :: derive_tools +# Module :: `derive_tools` diff --git a/module/core/derive_tools/src/lib.rs b/module/core/derive_tools/src/lib.rs index 62468ed1dc..fe3c51ebbf 100644 --- a/module/core/derive_tools/src/lib.rs +++ b/module/core/derive_tools/src/lib.rs @@ -31,7 +31,7 @@ // #[ cfg( feature = "enabled" ) ] // pub mod wtools; -#[ cfg( all( feature = "derive_more" ) ) ] +#[ cfg( feature = "derive_more" ) ] #[ allow( unused_imports ) ] mod derive_more { @@ -110,6 +110,7 @@ pub use own::*; #[ allow( unused_imports ) ] pub mod own { + #[ allow( clippy::wildcard_imports ) ] use super::*; #[ doc( inline ) ] pub use orphan::*; @@ -123,6 +124,7 @@ pub mod own #[ allow( unused_imports ) ] pub mod orphan { + #[ allow( clippy::wildcard_imports ) ] use super::*; #[ doc( inline ) ] pub use exposed::*; @@ -133,11 +135,12 @@ pub mod orphan #[ allow( unused_imports ) ] pub mod exposed { + #[ allow( clippy::wildcard_imports ) ] use super::*; #[ doc( inline ) ] pub use prelude::*; - #[ cfg( all( feature = "derive_more" ) ) ] + #[ cfg( feature = "derive_more" ) ] #[ doc( inline ) ] pub use super::derive_more::*; diff --git a/module/core/derive_tools_meta/Readme.md b/module/core/derive_tools_meta/Readme.md index 53f7fba9f0..91790856f2 100644 --- a/module/core/derive_tools_meta/Readme.md +++ b/module/core/derive_tools_meta/Readme.md @@ -1,5 +1,5 @@ -# Module :: derive_tools_meta +# Module :: `derive_tools_meta` [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) [![rust-status](https://github.com/Wandalen/wTools/actions/workflows/module_derive_tools_meta_push.yml/badge.svg)](https://github.com/Wandalen/wTools/actions/workflows/module_derive_tools_meta_push.yml) [![docs.rs](https://img.shields.io/docsrs/derive_tools_meta?color=e3e8f0&logo=docs.rs)](https://docs.rs/derive_tools_meta) [![discord](https://img.shields.io/discord/872391416519737405?color=eee&logo=discord&logoColor=eee&label=ask)](https://discord.gg/m3YfbXpUUY) diff --git a/module/core/derive_tools_meta/src/derive/as_ref.rs b/module/core/derive_tools_meta/src/derive/as_ref.rs index dba4eacacf..7a02d29b9b 100644 --- a/module/core/derive_tools_meta/src/derive/as_ref.rs +++ b/module/core/derive_tools_meta/src/derive/as_ref.rs @@ -1,4 +1,5 @@ +#[ allow( clippy::wildcard_imports ) ] use super::*; use macro_tools::{ attr, diag, item_struct, Result }; diff --git a/module/core/derive_tools_meta/src/derive/deref.rs b/module/core/derive_tools_meta/src/derive/deref.rs index ac2217c1c8..ad5489bd03 100644 --- a/module/core/derive_tools_meta/src/derive/deref.rs +++ b/module/core/derive_tools_meta/src/derive/deref.rs @@ -1,3 +1,4 @@ +#[ allow( clippy::wildcard_imports ) ] use super::*; use macro_tools::{ attr, diag, generic_params, Result, struct_like::StructLike }; @@ -11,7 +12,7 @@ pub fn deref( input : proc_macro::TokenStream ) -> Result< proc_macro2::TokenStr let item_name = &parsed.ident(); let ( _generics_with_defaults, generics_impl, generics_ty, generics_where ) - = generic_params::decompose( &parsed.generics() ); + = generic_params::decompose( parsed.generics() ); let result = match parsed { @@ -84,6 +85,7 @@ pub fn deref( input : proc_macro::TokenStream ) -> Result< proc_macro2::TokenStr /// } /// ``` /// +#[ allow( clippy::unnecessary_wraps ) ] fn generate_unit ( item_name : &syn::Ident, @@ -322,9 +324,9 @@ fn generate_enum None => return generate_unit ( item_name, - &generics_impl, - &generics_ty, - &generics_where, + generics_impl, + generics_ty, + generics_where, ), }; @@ -343,18 +345,18 @@ fn generate_enum generate_unit ( item_name, - &generics_impl, - &generics_ty, - &generics_where, + generics_impl, + generics_ty, + generics_where, ), syn::Fields::Unnamed( ref item ) => generate_enum_tuple_variants ( item_name, - &generics_impl, - &generics_ty, - &generics_where, + generics_impl, + generics_ty, + generics_where, &idents, item, ), @@ -363,9 +365,9 @@ fn generate_enum generate_enum_named_variants ( item_name, - &generics_impl, - &generics_ty, - &generics_where, + generics_impl, + generics_ty, + generics_where, &idents, item, ), diff --git a/module/core/derive_tools_meta/src/derive/from.rs b/module/core/derive_tools_meta/src/derive/from.rs index 585df90183..59a7462435 100644 --- a/module/core/derive_tools_meta/src/derive/from.rs +++ b/module/core/derive_tools_meta/src/derive/from.rs @@ -1,3 +1,4 @@ +#[ allow( clippy::wildcard_imports ) ] use super::*; use macro_tools:: { @@ -10,8 +11,10 @@ use macro_tools:: }; mod field_attributes; +#[ allow( clippy::wildcard_imports ) ] use field_attributes::*; mod item_attributes; +#[ allow( clippy::wildcard_imports ) ] use item_attributes::*; // @@ -27,15 +30,15 @@ pub fn from( input : proc_macro::TokenStream ) -> Result< proc_macro2::TokenStre let item_name = &parsed.ident(); let ( _generics_with_defaults, generics_impl, generics_ty, generics_where ) - = generic_params::decompose( &parsed.generics() ); + = generic_params::decompose( parsed.generics() ); let result = match parsed { StructLike::Unit( ref item ) | StructLike::Struct( ref item ) => { - let mut field_types = item_struct::field_types( &item ); - let field_names = item_struct::field_names( &item ); + let mut field_types = item_struct::field_types( item ); + let field_names = item_struct::field_names( item ); match ( field_types.len(), field_names ) { @@ -55,7 +58,7 @@ pub fn from( input : proc_macro::TokenStream ) -> Result< proc_macro2::TokenStre &generics_ty, &generics_where, field_names.next().unwrap(), - &field_types.next().unwrap(), + field_types.next().unwrap(), ), ( 1, None ) => generate_single_field @@ -64,7 +67,7 @@ pub fn from( input : proc_macro::TokenStream ) -> Result< proc_macro2::TokenStre &generics_impl, &generics_ty, &generics_where, - &field_types.next().unwrap(), + field_types.next().unwrap(), ), ( _, Some( field_names ) ) => generate_multiple_fields_named @@ -252,7 +255,7 @@ fn generate_single_field_named } // qqq : document, add example of generated code -- done -/// Generates `From`` implementation for structs with a single named field +/// Generates `From` implementation for structs with a single named field /// /// # Example of generated code /// @@ -441,6 +444,7 @@ fn generate_multiple_fields< 'a > } // qqq : document, add example of generated code +#[ allow ( clippy::format_in_format_args ) ] fn variant_generate ( item_name : &syn::Ident, @@ -462,7 +466,7 @@ fn variant_generate return Ok( qt!{} ) } - if fields.len() <= 0 + if fields.is_empty() { return Ok( qt!{} ) } diff --git a/module/core/derive_tools_meta/src/derive/from/field_attributes.rs b/module/core/derive_tools_meta/src/derive/from/field_attributes.rs index 5aeb72bd56..53d1c60393 100644 --- a/module/core/derive_tools_meta/src/derive/from/field_attributes.rs +++ b/module/core/derive_tools_meta/src/derive/from/field_attributes.rs @@ -1,3 +1,4 @@ +#[ allow( clippy::wildcard_imports ) ] use super::*; use macro_tools:: { @@ -25,6 +26,7 @@ pub struct FieldAttributes impl FieldAttributes { + #[ allow( clippy::single_match ) ] pub fn from_attrs< 'a >( attrs : impl Iterator< Item = &'a syn::Attribute > ) -> Result< Self > { let mut result = Self::default(); @@ -50,7 +52,7 @@ impl FieldAttributes { let key_ident = attr.path().get_ident().ok_or_else( || error( attr ) )?; - let key_str = format!( "{}", key_ident ); + let key_str = format!( "{key_ident}" ); // attributes does not have to be known // if attr::is_standard( &key_str ) @@ -61,7 +63,7 @@ impl FieldAttributes match key_str.as_ref() { FieldAttributeConfig::KEYWORD => result.assign( FieldAttributeConfig::from_meta( attr )? ), - "debug" => {}, + // "debug" => {}, _ => {}, // _ => return Err( error( attr ) ), } @@ -95,17 +97,18 @@ impl AttributeComponent for FieldAttributeConfig { const KEYWORD : &'static str = "from"; + #[ allow( clippy::match_wildcard_for_single_variants ) ] fn from_meta( attr : &syn::Attribute ) -> Result< Self > { match attr.meta { syn::Meta::List( ref meta_list ) => { - return syn::parse2::< FieldAttributeConfig >( meta_list.tokens.clone() ); + syn::parse2::< FieldAttributeConfig >( meta_list.tokens.clone() ) }, syn::Meta::Path( ref _path ) => { - return Ok( Default::default() ) + Ok( FieldAttributeConfig::default() ) }, _ => return_syn_err!( attr, "Expects an attribute of format `#[ from( on ) ]`. \nGot: {}", qt!{ #attr } ), } diff --git a/module/core/derive_tools_meta/src/derive/from/item_attributes.rs b/module/core/derive_tools_meta/src/derive/from/item_attributes.rs index f60b4fbbe4..4c81f3bcf1 100644 --- a/module/core/derive_tools_meta/src/derive/from/item_attributes.rs +++ b/module/core/derive_tools_meta/src/derive/from/item_attributes.rs @@ -1,3 +1,4 @@ +#[ allow( clippy::wildcard_imports ) ] use super::*; use macro_tools:: { @@ -23,6 +24,7 @@ pub struct ItemAttributes impl ItemAttributes { + #[ allow( clippy::single_match ) ] pub fn from_attrs< 'a >( attrs : impl Iterator< Item = &'a syn::Attribute > ) -> Result< Self > { let mut result = Self::default(); @@ -48,7 +50,7 @@ impl ItemAttributes { let key_ident = attr.path().get_ident().ok_or_else( || error( attr ) )?; - let key_str = format!( "{}", key_ident ); + let key_str = format!( "{key_ident}" ); // attributes does not have to be known // if attr::is_standard( &key_str ) @@ -59,7 +61,7 @@ impl ItemAttributes match key_str.as_ref() { ItemAttributeConfig::KEYWORD => result.assign( ItemAttributeConfig::from_meta( attr )? ), - "debug" => {} + // "debug" => {} _ => {}, // _ => return Err( error( attr ) ), // attributes does not have to be known @@ -92,17 +94,18 @@ impl AttributeComponent for ItemAttributeConfig { const KEYWORD : &'static str = "from"; + #[ allow( clippy::match_wildcard_for_single_variants ) ] fn from_meta( attr : &syn::Attribute ) -> Result< Self > { match attr.meta { syn::Meta::List( ref meta_list ) => { - return syn::parse2::< ItemAttributeConfig >( meta_list.tokens.clone() ); + syn::parse2::< ItemAttributeConfig >( meta_list.tokens.clone() ) }, syn::Meta::Path( ref _path ) => { - return Ok( Default::default() ) + Ok( ItemAttributeConfig::default() ) }, _ => return_syn_err!( attr, "Expects an attribute of format `#[ from( on ) ]`. \nGot: {}", qt!{ #attr } ), } diff --git a/module/core/derive_tools_meta/src/lib.rs b/module/core/derive_tools_meta/src/lib.rs index 2b323bbdc0..310fb2bf09 100644 --- a/module/core/derive_tools_meta/src/lib.rs +++ b/module/core/derive_tools_meta/src/lib.rs @@ -345,7 +345,7 @@ pub fn deref_mut( input : proc_macro::TokenStream ) -> proc_macro::TokenStream } /// -/// Derive macro to implement AsRef when-ever it's possible to do automatically. +/// Derive macro to implement `AsRef` when-ever it's possible to do automatically. /// /// ### Sample :: struct instead of macro. /// diff --git a/module/core/error_tools/Readme.md b/module/core/error_tools/Readme.md index 727ed9d8b7..f5e679a074 100644 --- a/module/core/error_tools/Readme.md +++ b/module/core/error_tools/Readme.md @@ -1,6 +1,6 @@ -# Module :: error_tools +# Module :: `error_tools` [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) [![rust-status](https://github.com/Wandalen/wTools/actions/workflows/module_error_tools_push.yml/badge.svg)](https://github.com/Wandalen/wTools/actions/workflows/module_error_tools_push.yml) [![docs.rs](https://img.shields.io/docsrs/error_tools?color=e3e8f0&logo=docs.rs)](https://docs.rs/error_tools) [![Open in Gitpod](https://raster.shields.io/static/v1?label=try&message=online&color=eee&logo=gitpod&logoColor=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=module%2Fcore%2Ferror_tools%2Fexamples%2Ferror_tools_trivial.rs,RUN_POSTFIX=--example%20error_tools_trivial/https://github.com/Wandalen/wTools) [![discord](https://img.shields.io/discord/872391416519737405?color=eee&logo=discord&logoColor=eee&label=ask)](https://discord.gg/m3YfbXpUUY) diff --git a/module/core/error_tools/src/error/assert.rs b/module/core/error_tools/src/error/assert.rs index 52f45d29bf..18ba821186 100644 --- a/module/core/error_tools/src/error/assert.rs +++ b/module/core/error_tools/src/error/assert.rs @@ -2,7 +2,7 @@ mod private { /// - /// Macro asserts that two expressions are identical to each other. Unlike std::assert_eq it is removed from a release build. + /// Macro asserts that two expressions are identical to each other. Unlike `std::assert_eq` it is removed from a release build. /// #[ macro_export ] @@ -58,7 +58,7 @@ mod private // }}; } - /// Macro asserts that two expressions are identical to each other. Unlike std::assert_eq it is removed from a release build. Alias of debug_assert_id. + /// Macro asserts that two expressions are identical to each other. Unlike `std::assert_eq` it is removed from a release build. Alias of `debug_assert_id`. #[ macro_export ] macro_rules! debug_assert_identical @@ -70,7 +70,7 @@ mod private }; } - /// Macro asserts that two expressions are not identical to each other. Unlike std::assert_eq it is removed from a release build. + /// Macro asserts that two expressions are not identical to each other. Unlike `std::assert_eq` it is removed from a release build. #[ macro_export ] macro_rules! debug_assert_ni @@ -83,7 +83,7 @@ mod private }; } - /// Macro asserts that two expressions are not identical to each other. Unlike std::assert_eq it is removed from a release build. + /// Macro asserts that two expressions are not identical to each other. Unlike `std::assert_eq` it is removed from a release build. #[ macro_export ] macro_rules! debug_assert_not_identical @@ -108,9 +108,13 @@ mod private // }; // } + #[ allow( clippy::useless_attribute, clippy::pub_use ) ] pub use debug_assert_id; + #[ allow( clippy::useless_attribute, clippy::pub_use ) ] pub use debug_assert_identical; + #[ allow( clippy::useless_attribute, clippy::pub_use ) ] pub use debug_assert_ni; + #[ allow( clippy::useless_attribute, clippy::pub_use ) ] pub use debug_assert_not_identical; } @@ -118,21 +122,26 @@ mod private #[ allow( unused_imports ) ] pub mod own { + #[ allow( clippy::wildcard_imports ) ] use super::*; #[ doc( inline ) ] + #[ allow( clippy::useless_attribute, clippy::pub_use ) ] pub use orphan::*; } #[ doc( inline ) ] #[ allow( unused_imports ) ] +#[ allow( clippy::pub_use ) ] pub use own::*; /// Shared with parent namespace of the module #[ allow( unused_imports ) ] pub mod orphan { + #[ allow( clippy::wildcard_imports ) ] use super::*; #[ doc( inline ) ] + #[ allow( clippy::useless_attribute, clippy::pub_use ) ] pub use exposed::*; } @@ -140,8 +149,10 @@ pub mod orphan #[ allow( unused_imports ) ] pub mod exposed { + #[ allow( clippy::wildcard_imports ) ] use super::*; #[ doc( inline ) ] + #[ allow( clippy::useless_attribute, clippy::pub_use ) ] pub use prelude::*; } @@ -149,9 +160,14 @@ pub mod exposed #[ allow( unused_imports ) ] pub mod prelude { + #[ allow( clippy::wildcard_imports ) ] use super::*; + #[ allow( clippy::useless_attribute, clippy::pub_use ) ] pub use private::debug_assert_id; + #[ allow( clippy::useless_attribute, clippy::pub_use ) ] pub use private::debug_assert_identical; + #[ allow( clippy::useless_attribute, clippy::pub_use ) ] pub use private::debug_assert_ni; + #[ allow( clippy::useless_attribute, clippy::pub_use ) ] pub use private::debug_assert_not_identical; } diff --git a/module/core/error_tools/src/error/mod.rs b/module/core/error_tools/src/error/mod.rs index 4d9c79ddaa..0de7d2fafd 100644 --- a/module/core/error_tools/src/error/mod.rs +++ b/module/core/error_tools/src/error/mod.rs @@ -1,6 +1,7 @@ /// Define a private namespace for all its items. mod private { + #[ allow( clippy::useless_attribute, clippy::pub_use ) ] pub use std::error::Error as ErrorTrait; /// This trait allows adding extra context or information to an error, creating a tuple of the additional @@ -26,6 +27,10 @@ mod private /// /// A `Result` of type `ReportOk` if the original result is `Ok`, or a tuple `(ReportErr, E)` containing the additional /// context and the original error if the original result is `Err`. + /// + /// # Errors + /// + /// qqq: errors /// /// # Example /// @@ -34,7 +39,7 @@ mod private /// let result : Result< (), std::io::Error > = Err( std::io::Error::new( std::io::ErrorKind::Other, "an error occurred" ) ); /// let result_with_context : Result< (), ( &str, std::io::Error ) > = result.err_with( || "additional context" ); /// ``` - fn err_with< F >( self, f : F ) -> std::result::Result< ReportOk, ( ReportErr, E ) > + fn err_with< F >( self, f : F ) -> core::result::Result< ReportOk, ( ReportErr, E ) > where F : FnOnce() -> ReportErr; @@ -51,6 +56,10 @@ mod private /// /// A `Result` of type `ReportOk` if the original result is `Ok`, or a tuple `(ReportErr, E)` containing the additional /// context and the original error if the original result is `Err`. + /// + /// # Errors + /// + /// qqq: Errors /// /// # Example /// @@ -60,32 +69,35 @@ mod private /// let report = "additional context"; /// let result_with_report : Result< (), ( &str, std::io::Error ) > = result.err_with_report( &report ); /// ``` - fn err_with_report( self, report : &ReportErr ) -> std::result::Result< ReportOk, ( ReportErr, E ) > + fn err_with_report( self, report : &ReportErr ) -> core::result::Result< ReportOk, ( ReportErr, E ) > where ReportErr : Clone; } impl< ReportErr, ReportOk, E, IntoError > ErrWith< ReportErr, ReportOk, E > - for std::result::Result< ReportOk, IntoError > + for core::result::Result< ReportOk, IntoError > where IntoError : Into< E >, { - fn err_with< F >( self, f : F ) -> std::result::Result< ReportOk, ( ReportErr, E ) > + #[ allow( clippy::implicit_return, clippy::min_ident_chars ) ] + #[ inline ] + fn err_with< F >( self, f : F ) -> core::result::Result< ReportOk, ( ReportErr, E ) > where F : FnOnce() -> ReportErr, { - self.map_err( | e | ( f(), e.into() ) ) + self.map_err( | error | ( f(), error.into() ) ) } #[ inline( always ) ] - fn err_with_report( self, report : &ReportErr ) -> std::result::Result< ReportOk, ( ReportErr, E ) > + #[ allow( clippy::implicit_return ) ] + fn err_with_report( self, report : &ReportErr ) -> core::result::Result< ReportOk, ( ReportErr, E ) > where ReportErr : Clone, Self : Sized, { - self.map_err( | e | ( report.clone(), e.into() ) ) + self.map_err( | error | ( report.clone(), error.into() ) ) } } @@ -226,6 +238,7 @@ pub mod untyped; #[ cfg( feature = "enabled" ) ] #[ doc( inline ) ] #[ allow( unused_imports ) ] +#[ allow( clippy::pub_use ) ] pub use own::*; /// Own namespace of the module. @@ -233,23 +246,29 @@ pub use own::*; #[ allow( unused_imports ) ] pub mod own { + #[ allow( clippy::wildcard_imports ) ] use super::*; #[ doc( inline ) ] + #[ allow( clippy::useless_attribute, clippy::pub_use ) ] pub use orphan::*; #[ doc( inline ) ] + #[ allow( clippy::useless_attribute, clippy::pub_use ) ] pub use assert::orphan::*; #[ cfg( feature = "error_untyped" ) ] #[ doc( inline ) ] + #[ allow( clippy::useless_attribute, clippy::pub_use ) ] pub use untyped::orphan::*; #[ cfg( feature = "error_typed" ) ] #[ doc( inline ) ] + #[ allow( clippy::useless_attribute, clippy::pub_use ) ] pub use typed::orphan::*; #[ doc( inline ) ] + #[ allow( clippy::useless_attribute, clippy::pub_use ) ] pub use private:: { // err, @@ -258,10 +277,13 @@ pub mod own // BasicError, }; + #[ allow( clippy::useless_attribute, clippy::pub_use ) ] pub use super::assert; #[ cfg( feature = "error_typed" ) ] + #[ allow( clippy::useless_attribute, clippy::pub_use ) ] pub use super::typed; #[ cfg( feature = "error_untyped" ) ] + #[ allow( clippy::useless_attribute, clippy::pub_use ) ] pub use super::untyped; } @@ -271,8 +293,10 @@ pub mod own #[ allow( unused_imports ) ] pub mod orphan { + #[ allow( clippy::wildcard_imports ) ] use super::*; #[ doc( inline ) ] + #[ allow( clippy::useless_attribute, clippy::pub_use ) ] pub use exposed::*; } @@ -281,15 +305,19 @@ pub mod orphan #[ allow( unused_imports ) ] pub mod exposed { + #[ allow( clippy::wildcard_imports ) ] use super::*; #[ doc( inline ) ] + #[ allow( clippy::useless_attribute, clippy::pub_use ) ] pub use prelude::*; // Expose itself. + #[ allow( clippy::useless_attribute, clippy::pub_use ) ] pub use super::super::error; #[ doc( inline ) ] + #[ allow( clippy::useless_attribute, clippy::pub_use ) ] pub use private:: { ErrWith, @@ -297,14 +325,17 @@ pub mod exposed }; #[ doc( inline ) ] + #[ allow( clippy::useless_attribute, clippy::pub_use ) ] pub use assert::exposed::*; #[ cfg( feature = "error_untyped" ) ] #[ doc( inline ) ] + #[ allow( clippy::useless_attribute, clippy::pub_use ) ] pub use untyped::exposed::*; #[ cfg( feature = "error_typed" ) ] #[ doc( inline ) ] + #[ allow( clippy::useless_attribute, clippy::pub_use ) ] pub use typed::exposed::*; } @@ -314,6 +345,7 @@ pub mod exposed #[ allow( unused_imports ) ] pub mod prelude { + #[ allow( clippy::wildcard_imports ) ] use super::*; // #[ doc( inline ) ] @@ -326,14 +358,17 @@ pub mod prelude // }; #[ doc( inline ) ] + #[ allow( clippy::useless_attribute, clippy::pub_use ) ] pub use assert::prelude::*; #[ cfg( feature = "error_untyped" ) ] #[ doc( inline ) ] + #[ allow( clippy::useless_attribute, clippy::pub_use ) ] pub use untyped::prelude::*; #[ cfg( feature = "error_typed" ) ] #[ doc( inline ) ] + #[ allow( clippy::useless_attribute, clippy::pub_use ) ] pub use typed::prelude::*; } diff --git a/module/core/error_tools/src/error/typed.rs b/module/core/error_tools/src/error/typed.rs index 074fb5b61a..0845523e35 100644 --- a/module/core/error_tools/src/error/typed.rs +++ b/module/core/error_tools/src/error/typed.rs @@ -6,14 +6,17 @@ mod private #[ doc( inline ) ] #[ allow( unused_imports ) ] +#[ allow( clippy::pub_use ) ] pub use own::*; /// Own namespace of the module. #[ allow( unused_imports ) ] pub mod own { + #[ allow( clippy::wildcard_imports ) ] use super::*; #[ doc( inline ) ] + #[ allow( clippy::useless_attribute, clippy::pub_use ) ] pub use orphan::*; } @@ -21,15 +24,20 @@ pub mod own #[ allow( unused_imports ) ] pub mod orphan { + #[ allow( clippy::wildcard_imports ) ] use super::*; + #[ allow( clippy::useless_attribute, clippy::pub_use ) ] pub use super::super::typed; + #[ allow( clippy::useless_attribute, clippy::pub_use ) ] pub use super::super::typed as for_lib; #[ doc( inline ) ] + #[ allow( clippy::useless_attribute, clippy::pub_use ) ] pub use exposed::*; #[ doc( inline ) ] #[ allow( unused_imports ) ] + #[ allow( clippy::pub_use ) ] pub use ::thiserror:: { Error, @@ -41,9 +49,11 @@ pub mod orphan #[ allow( unused_imports ) ] pub mod exposed { + #[ allow( clippy::wildcard_imports ) ] use super::*; #[ doc( inline ) ] + #[ allow( clippy::useless_attribute, clippy::pub_use ) ] pub use prelude::*; } @@ -52,10 +62,12 @@ pub mod exposed #[ allow( unused_imports ) ] pub mod prelude { + #[ allow( clippy::wildcard_imports ) ] use super::*; #[ doc( inline ) ] #[ allow( unused_imports ) ] + #[ allow( clippy::pub_use ) ] pub use thiserror; } \ No newline at end of file diff --git a/module/core/error_tools/src/error/untyped.rs b/module/core/error_tools/src/error/untyped.rs index 8288f32866..d4d65afe67 100644 --- a/module/core/error_tools/src/error/untyped.rs +++ b/module/core/error_tools/src/error/untyped.rs @@ -6,18 +6,22 @@ mod private #[ doc( inline ) ] #[ allow( unused_imports ) ] +#[ allow( clippy::pub_use ) ] pub use own::*; /// Own namespace of the module. #[ allow( unused_imports ) ] pub mod own { + #[ allow( clippy::wildcard_imports ) ] use super::*; #[ doc( inline ) ] + #[ allow( clippy::useless_attribute, clippy::pub_use ) ] pub use orphan::*; #[ doc( inline ) ] + #[ allow( clippy::useless_attribute, clippy::pub_use ) ] pub use ::anyhow:: { Chain, @@ -37,11 +41,15 @@ pub mod own #[ allow( unused_imports ) ] pub mod orphan { + #[ allow( clippy::wildcard_imports ) ] use super::*; + #[ allow( clippy::useless_attribute, clippy::pub_use ) ] pub use super::super::untyped; + #[ allow( clippy::useless_attribute, clippy::pub_use ) ] pub use super::super::untyped as for_app; #[ doc( inline ) ] + #[ allow( clippy::useless_attribute, clippy::pub_use ) ] pub use exposed::*; // #[ doc( inline ) ] @@ -58,9 +66,11 @@ pub mod orphan #[ allow( unused_imports ) ] pub mod exposed { + #[ allow( clippy::wildcard_imports ) ] use super::*; #[ doc( inline ) ] + #[ allow( clippy::useless_attribute, clippy::pub_use ) ] pub use prelude::*; } diff --git a/module/core/error_tools/src/lib.rs b/module/core/error_tools/src/lib.rs index 9ba2500d42..dbf07d7fb2 100644 --- a/module/core/error_tools/src/lib.rs +++ b/module/core/error_tools/src/lib.rs @@ -3,8 +3,10 @@ #![ doc( html_favicon_url = "https://raw.githubusercontent.com/Wandalen/wTools/alpha/asset/img/logo_v3_trans_square_icon_small_v2.ico" ) ] #![ doc( html_root_url = "https://docs.rs/error_tools/latest/error_tools/" ) ] #![ doc = include_str!( concat!( env!( "CARGO_MANIFEST_DIR" ), "/", "Readme.md" ) ) ] +#![ allow( clippy::mod_module_files ) ] -/// Alias for std::error::BasicError. +/// Alias for `std::error::BasicError`. +#[ allow( clippy::pub_use ) ] #[ cfg( feature = "enabled" ) ] #[ cfg( not( feature = "no_std" ) ) ] pub mod error; @@ -16,69 +18,85 @@ pub mod dependency #[ doc( inline ) ] #[ cfg( feature = "error_typed" ) ] + #[ allow( clippy::useless_attribute, clippy::pub_use ) ] pub use ::thiserror; #[ doc( inline ) ] #[ cfg( feature = "error_untyped" ) ] + #[ allow( clippy::useless_attribute, clippy::pub_use ) ] pub use ::anyhow; } #[ cfg( feature = "enabled" ) ] +#[ cfg( not( feature = "no_std" ) ) ] #[ doc( inline ) ] #[ allow( unused_imports ) ] +#[ allow( clippy::pub_use ) ] pub use own::*; /// Own namespace of the module. #[ cfg( feature = "enabled" ) ] +#[ cfg( not( feature = "no_std" ) ) ] #[ allow( unused_imports ) ] pub mod own { use super::*; #[ doc( inline ) ] + #[ allow( clippy::useless_attribute, clippy::pub_use ) ] pub use error::own::*; } /// Shared with parent namespace of the module #[ cfg( feature = "enabled" ) ] +#[ cfg( not( feature = "no_std" ) ) ] #[ allow( unused_imports ) ] pub mod orphan { + #[ allow( clippy::wildcard_imports ) ] use super::*; #[ doc( inline ) ] + #[ allow( clippy::useless_attribute, clippy::pub_use ) ] pub use exposed::*; #[ doc( inline ) ] + #[ allow( clippy::useless_attribute, clippy::pub_use ) ] pub use error::orphan::*; } /// Exposed namespace of the module. #[ cfg( feature = "enabled" ) ] +#[ cfg( not( feature = "no_std" ) ) ] #[ allow( unused_imports ) ] pub mod exposed { + #[ allow( clippy::wildcard_imports ) ] use super::*; #[ doc( inline ) ] + #[ allow( clippy::useless_attribute, clippy::pub_use ) ] pub use prelude::*; #[ doc( inline ) ] + #[ allow( clippy::useless_attribute, clippy::pub_use ) ] pub use error::exposed::*; } /// Prelude to use essentials: `use my_module::prelude::*`. #[ cfg( feature = "enabled" ) ] +#[ cfg( not( feature = "no_std" ) ) ] #[ allow( unused_imports ) ] pub mod prelude { - use super::*; + use super::error; #[ doc( inline ) ] + #[ allow( clippy::useless_attribute, clippy::pub_use ) ] pub use error::prelude::*; } diff --git a/module/core/former/Readme.md b/module/core/former/Readme.md index d36ce4b061..0e429ab46b 100644 --- a/module/core/former/Readme.md +++ b/module/core/former/Readme.md @@ -23,7 +23,7 @@ This approach abstracts away the need for manually implementing a builder for ea ## Comparison -The Former crate and the abstract Builder pattern concept share a common goal: to construct complex objects step-by-step, ensuring they are always in a valid state and hiding internal structures. Both use a fluent interface for setting fields and support default values for fields that aren't explicitly set. They also have a finalization method to return the constructed object (.form() in Former, build() in [traditional Builder](https://refactoring.guru/design-patterns/builder)). +The Former crate and the abstract Builder pattern concept share a common goal: to construct complex objects step-by-step, ensuring they are always in a valid state and hiding internal structures. Both use a fluent interface for setting fields and support default values for fields that aren't explicitly set. They also have a finalization method to return the constructed object (`.form()` in Former, `build()` in [traditional Builder](https://refactoring.guru/design-patterns/builder)). However, the Former crate extends the traditional Builder pattern by automating the generation of builder methods using macros. This eliminates the need for manual implementation, which is often required in the abstract concept. Additionally, Former supports nested builders and subformers for complex data structures, allowing for more sophisticated object construction. @@ -660,7 +660,7 @@ Storage is not just a passive collection; it is an active part of a larger ecosy - **Former as an Active Manager**: The former is responsible for managing the storage, utilizing it to keep track of the object's evolving configuration. It orchestrates the formation process by handling intermediate states and preparing the object for its final form. - **Contextual Flexibility**: The context associated with the former adds an additional layer of flexibility, allowing the former to adjust its behavior based on the broader circumstances of the object's formation. This is particularly useful when the forming process involves conditions or states external to the object itself. -- **FormingEnd Callback**: The `FormingEnd` callback is a dynamic component that defines the final steps of the forming process. It can modify the storage based on final adjustments, validate the object's readiness, or integrate the object into a larger structure, such as embedding it as a subformer within another structure. +- **`FormingEnd` Callback**: The `FormingEnd` callback is a dynamic component that defines the final steps of the forming process. It can modify the storage based on final adjustments, validate the object's readiness, or integrate the object into a larger structure, such as embedding it as a subformer within another structure. These elements work in concert to ensure that the forming process is not only about building an object step-by-step but also about integrating it seamlessly into larger, more complex structures or systems. diff --git a/module/core/former/src/lib.rs b/module/core/former/src/lib.rs index 635a8e85e0..b692c6e9b6 100644 --- a/module/core/former/src/lib.rs +++ b/module/core/former/src/lib.rs @@ -22,6 +22,7 @@ pub use own::*; #[ allow( unused_imports ) ] pub mod own { + #[ allow( clippy::wildcard_imports ) ] use super::*; #[ doc( inline ) ] pub use orphan::*; @@ -35,6 +36,7 @@ pub mod own #[ allow( unused_imports ) ] pub mod orphan { + #[ allow( clippy::wildcard_imports ) ] use super::*; #[ doc( inline ) ] pub use exposed::*; @@ -45,6 +47,7 @@ pub mod orphan #[ allow( unused_imports ) ] pub mod exposed { + #[ allow( clippy::wildcard_imports ) ] use super::*; #[ doc( inline ) ] diff --git a/module/core/former_meta/Readme.md b/module/core/former_meta/Readme.md index 716940ba96..1fa3cb805f 100644 --- a/module/core/former_meta/Readme.md +++ b/module/core/former_meta/Readme.md @@ -1,13 +1,13 @@ -# Module :: former_meta +# Module :: `former_meta` [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) [![rust-status](https://github.com/Wandalen/wTools/actions/workflows/module_former_meta_push.yml/badge.svg)](https://github.com/Wandalen/wTools/actions/workflows/module_former_meta_push.yml) [![docs.rs](https://img.shields.io/docsrs/former_meta?color=e3e8f0&logo=docs.rs)](https://docs.rs/former_meta) [![discord](https://img.shields.io/discord/872391416519737405?color=eee&logo=discord&logoColor=eee&label=ask)](https://discord.gg/m3YfbXpUUY) -A flexible implementation of the Builder pattern supporting nested builders and collection-specific subformers. Implementation of its derive macro. Should not be used independently, instead use module::former which relies on the module. +A flexible implementation of the Builder pattern supporting nested builders and collection-specific subformers. Implementation of its derive macro. Should not be used independently, instead use `module::former` which relies on the module. -Not intended to be used without runtime. This module and runtime is aggregate in module::former is [here](https://github.com/Wandalen/wTools/tree/master/module/core/former). +Not intended to be used without runtime. This module and runtime is aggregate in `module::former` is [here](https://github.com/Wandalen/wTools/tree/master/module/core/former). ### To add to your project diff --git a/module/core/former_meta/src/component/component_assign.rs b/module/core/former_meta/src/component/component_assign.rs index de12fc7f5f..951e385778 100644 --- a/module/core/former_meta/src/component/component_assign.rs +++ b/module/core/former_meta/src/component/component_assign.rs @@ -1,3 +1,4 @@ +#[ allow( clippy::wildcard_imports ) ] use super::*; use macro_tools::{ attr, diag, Result }; diff --git a/module/core/former_meta/src/component/component_from.rs b/module/core/former_meta/src/component/component_from.rs index c5613a48fa..c366ef3b1c 100644 --- a/module/core/former_meta/src/component/component_from.rs +++ b/module/core/former_meta/src/component/component_from.rs @@ -1,4 +1,4 @@ - +#[ allow( clippy::wildcard_imports ) ] use super::*; use macro_tools::{ attr, diag, Result }; diff --git a/module/core/former_meta/src/component/components_assign.rs b/module/core/former_meta/src/component/components_assign.rs index 6b495e7629..3716ef2161 100644 --- a/module/core/former_meta/src/component/components_assign.rs +++ b/module/core/former_meta/src/component/components_assign.rs @@ -1,3 +1,4 @@ +#[ allow( clippy::wildcard_imports ) ] use super::*; use macro_tools::{ attr, diag, Result, format_ident }; use iter_tools::{ Itertools }; @@ -44,7 +45,7 @@ pub fn components_assign( input : proc_macro::TokenStream ) -> Result< proc_macr let component_assigns : Vec< _ > = component_assigns.into_iter().collect::< Result< _ > >()?; // code - let doc = format!( "Interface to assign instance from set of components exposed by a single argument." ); + let doc = "Interface to assign instance from set of components exposed by a single argument.".to_string(); let trait_bounds = qt! { #( #bounds1 )* IntoT : Clone }; let impl_bounds = qt! { #( #bounds2 )* #( #bounds1 )* IntoT : Clone }; let component_assigns = qt! { #( #component_assigns )* }; @@ -75,7 +76,7 @@ pub fn components_assign( input : proc_macro::TokenStream ) -> Result< proc_macr if has_debug { - let about = format!( "derive : ComponentsAssign\nstructure : {0}", item_name ); + let about = format!( "derive : ComponentsAssign\nstructure : {item_name}" ); diag::report_print( about, &original_input, &result ); } @@ -96,6 +97,7 @@ pub fn components_assign( input : proc_macro::TokenStream ) -> Result< proc_macr /// IntoT : Into< i32 > /// ``` /// +#[ allow( clippy::unnecessary_wraps ) ] fn generate_trait_bounds( field_type : &syn::Type ) -> Result< proc_macro2::TokenStream > { Ok @@ -116,6 +118,7 @@ fn generate_trait_bounds( field_type : &syn::Type ) -> Result< proc_macro2::Toke /// T : former::Assign< i32, IntoT >, /// ``` /// +#[ allow( clippy::unnecessary_wraps ) ] fn generate_impl_bounds( field_type : &syn::Type ) -> Result< proc_macro2::TokenStream > { Ok @@ -137,6 +140,7 @@ fn generate_impl_bounds( field_type : &syn::Type ) -> Result< proc_macro2::Token /// former::Assign::< i32, _ >::assign( self.component.clone() ); /// ``` /// +#[ allow( clippy::unnecessary_wraps ) ] fn generate_component_assign_call( field : &syn::Field ) -> Result< proc_macro2::TokenStream > { // let field_name = field.ident.as_ref().expect( "Expected the field to have a name" ); diff --git a/module/core/former_meta/src/component/from_components.rs b/module/core/former_meta/src/component/from_components.rs index d76029ca0a..c496ea15d9 100644 --- a/module/core/former_meta/src/component/from_components.rs +++ b/module/core/former_meta/src/component/from_components.rs @@ -1,3 +1,4 @@ +#[ allow( clippy::wildcard_imports ) ] use super::*; use macro_tools::{ attr, diag, item_struct, Result }; @@ -78,7 +79,7 @@ pub fn from_components( input : proc_macro::TokenStream ) -> Result< proc_macro2 // diag::report_print( "derive : FromComponents", original_input, &result ); // } - Ok( result.into() ) + Ok( result ) } /// Generates trait bounds for the `From< T >` implementation, ensuring that `T` diff --git a/module/core/former_meta/src/derive_former.rs b/module/core/former_meta/src/derive_former.rs index b4d163608e..4a4515c199 100644 --- a/module/core/former_meta/src/derive_former.rs +++ b/module/core/former_meta/src/derive_former.rs @@ -1,4 +1,4 @@ - +#[ allow( clippy::wildcard_imports ) ] use super::*; use iter_tools::{ Itertools }; use macro_tools::{ attr, diag, generic_params, generic_args, typ, derive, Result }; @@ -7,10 +7,13 @@ use proc_macro2::TokenStream; // qqq : implement interfaces for other collections mod field_attrs; +#[ allow( clippy::wildcard_imports ) ] use field_attrs::*; mod field; +#[ allow( clippy::wildcard_imports ) ] use field::*; mod struct_attrs; +#[ allow( clippy::wildcard_imports ) ] use struct_attrs::*; /// Generates the code for implementing the `FormerMutator` trait for a specified former definition type. @@ -40,7 +43,7 @@ use struct_attrs::*; /// } /// ``` /// - +#[ allow( clippy::format_in_format_args, clippy::unnecessary_wraps ) ] pub fn mutator ( item : &syn::Ident, @@ -114,20 +117,18 @@ fn doc_generate( item : &syn::Ident ) -> ( String, String ) let doc_former_mod = format! ( -r#" Implementation of former for [{}]. -"#, - item +r#" Implementation of former for [{item}]. +"# ); let doc_former_struct = format! ( r#" -Structure to form [{}]. Represents a forming entity designed to construct objects through a builder pattern. +Structure to form [{item}]. Represents a forming entity designed to construct objects through a builder pattern. This structure holds temporary storage and context during the formation process and utilizes a defined end strategy to finalize the object creation. -"#, - item +"# ); ( doc_former_mod, doc_former_struct ) @@ -138,7 +139,7 @@ utilizes a defined end strategy to finalize the object creation. /// /// Output examples can be found in [docs to former crate](https://docs.rs/former/latest/former/) /// - +#[ allow( clippy::too_many_lines ) ] pub fn former( input : proc_macro::TokenStream ) -> Result< TokenStream > { use macro_tools::IntoGenericArgs; @@ -185,7 +186,7 @@ specific needs of the broader forming context. It mandates the implementation of { < (), #item < #struct_generics_ty >, former::ReturnPreformed > }; - let former_definition_args = generic_args::merge( &generics.into_generic_args(), &extra.into() ).args; + let former_definition_args = generic_args::merge( &generics.into_generic_args(), &extra ).args; /* parameters for former */ @@ -196,7 +197,7 @@ specific needs of the broader forming context. It mandates the implementation of Definition : former::FormerDefinition< Storage = #former_storage < #struct_generics_ty > >, Definition::Types : former::FormerDefinitionTypes< Storage = #former_storage < #struct_generics_ty > >, }; - let extra = generic_params::merge( &generics, &extra.into() ); + let extra = generic_params::merge( generics, &extra.into() ); let ( former_generics_with_defaults, former_generics_impl, former_generics_ty, former_generics_where ) = generic_params::decompose( &extra ); @@ -218,7 +219,7 @@ specific needs of the broader forming context. It mandates the implementation of Formed = #item < #struct_generics_ty >, >, }; - let extra = generic_params::merge( &generics, &extra.into() ); + let extra = generic_params::merge( generics, &extra.into() ); let ( _former_perform_generics_with_defaults, former_perform_generics_impl, former_perform_generics_ty, former_perform_generics_where ) = generic_params::decompose( &extra ); @@ -229,7 +230,7 @@ specific needs of the broader forming context. It mandates the implementation of { < __Context = (), __Formed = #item < #struct_generics_ty > > }; - let former_definition_types_generics = generic_params::merge( &generics, &extra.into() ); + let former_definition_types_generics = generic_params::merge( generics, &extra.into() ); let ( former_definition_types_generics_with_defaults, former_definition_types_generics_impl, former_definition_types_generics_ty, former_definition_types_generics_where ) = generic_params::decompose( &former_definition_types_generics ); @@ -241,7 +242,7 @@ specific needs of the broader forming context. It mandates the implementation of { < __Context = (), __Formed = #item < #struct_generics_ty >, __End = former::ReturnPreformed > }; - let generics_of_definition = generic_params::merge( &generics, &extra.into() ); + let generics_of_definition = generic_params::merge( generics, &extra.into() ); let ( former_definition_generics_with_defaults, former_definition_generics_impl, former_definition_generics_ty, former_definition_generics_where ) = generic_params::decompose( &generics_of_definition ); @@ -294,7 +295,7 @@ specific needs of the broader forming context. It mandates the implementation of field.storage_field_preform(), field.former_field_setter ( - &item, + item, &original_input, &struct_generics_impl, &struct_generics_ty, @@ -317,7 +318,7 @@ specific needs of the broader forming context. It mandates the implementation of let former_mutator_code = mutator ( - &item, + item, &original_input, &struct_attrs.mutator, &former_definition_types, diff --git a/module/core/former_meta/src/derive_former/field.rs b/module/core/former_meta/src/derive_former/field.rs index 089470ea84..98edaccf49 100644 --- a/module/core/former_meta/src/derive_former/field.rs +++ b/module/core/former_meta/src/derive_former/field.rs @@ -1,4 +1,4 @@ - +#[ allow( clippy::wildcard_imports ) ] use super::*; use macro_tools::{ container_kind }; @@ -26,22 +26,22 @@ impl< 'a > FormerField< 'a > /** methods -from_syn +`from_syn` -storage_fields_none -storage_field_optional -storage_field_preform -storage_field_name -former_field_setter -scalar_setter -subform_entry_setter -subform_collection_setter +`storage_fields_none` +`storage_field_optional` +`storage_field_preform` +`storage_field_name` +`former_field_setter` +`scalar_setter` +`subform_entry_setter` +`subform_collection_setter` -scalar_setter_name -subform_scalar_setter_name, -subform_collection_setter_name -subform_entry_setter_name -scalar_setter_required +`scalar_setter_name` +`subform_scalar_setter_name`, +`subform_collection_setter_name` +`subform_entry_setter_name` +`scalar_setter_required` */ @@ -173,6 +173,7 @@ scalar_setter_required /// #[ inline( always ) ] + #[ allow( clippy::unnecessary_wraps ) ] pub fn storage_field_preform( &self ) -> Result< TokenStream > { @@ -228,7 +229,7 @@ scalar_setter_required { None => { - let panic_msg = format!( "Field '{}' isn't initialized", ident ); + let panic_msg = format!( "Field '{ident}' isn't initialized" ); qt! { { @@ -327,6 +328,7 @@ scalar_setter_required /// #[ inline ] + #[ allow( clippy::too_many_arguments ) ] pub fn former_field_setter ( &self, @@ -376,7 +378,7 @@ scalar_setter_required }; // subform collection setter - let ( setters_code, namespace_code ) = if let Some( _ ) = &self.attrs.subform_collection + let ( setters_code, namespace_code ) = if self.attrs.subform_collection.is_some() { let ( setters_code2, namespace_code2 ) = self.subform_collection_setter ( @@ -421,9 +423,9 @@ scalar_setter_required } /// - /// Generate a single scalar setter for the 'field_ident' with the 'setter_name' name. + /// Generate a single scalar setter for the '`field_ident`' with the '`setter_name`' name. /// - /// Used as a helper function for former_field_setter(), which generates alias setters + /// Used as a helper function for `former_field_setter()`, which generates alias setters /// /// # Example of generated code /// @@ -441,6 +443,7 @@ scalar_setter_required /// ``` #[ inline ] + #[ allow( clippy::format_in_format_args ) ] pub fn scalar_setter ( &self, @@ -494,8 +497,7 @@ field : {field_ident}"#, let doc = format! ( - "Scalar setter for the '{}' field.", - field_ident, + "Scalar setter for the '{field_ident}' field.", ); qt! @@ -515,12 +517,13 @@ field : {field_ident}"#, } /// - /// Generate a collection setter for the 'field_ident' with the 'setter_name' name. + /// Generate a collection setter for the '`field_ident`' with the '`setter_name`' name. /// /// See `tests/inc/former_tests/subform_collection_manual.rs` for example of generated code. /// #[ inline ] + #[ allow( clippy::too_many_lines, clippy::too_many_arguments ) ] pub fn subform_collection_setter ( &self, @@ -537,8 +540,9 @@ field : {field_ident}"#, let attr = self.attrs.subform_collection.as_ref().unwrap(); let field_ident = &self.ident; let field_typ = &self.non_optional_ty; - let params = typ::type_parameters( &field_typ, .. ); + let params = typ::type_parameters( field_typ, &( .. ) ); + #[ allow( clippy::useless_attribute, clippy::items_after_statements ) ] use convert_case::{ Case, Casing }; // example : `ParentSubformCollectionChildrenEnd` @@ -584,10 +588,7 @@ field : {field_ident}"#, let doc = format! ( - "Collection setter for the '{}' field. Method {} unlike method {} accept custom collection subformer.", - field_ident, - subform_collection, - field_ident, + "Collection setter for the '{field_ident}' field. Method {subform_collection} unlike method {field_ident} accept custom collection subformer." ); let setter1 = @@ -754,7 +755,7 @@ with the new content generated during the subforming process. format!( "{}", qt!{ #field_typ } ), ); - let subformer_definition_types = if let Some( ref _subformer_definition ) = subformer_definition + let subformer_definition_types = if let Some( _subformer_definition ) = subformer_definition { let subformer_definition_types_string = format!( "{}Types", qt!{ #subformer_definition } ); let subformer_definition_types : syn::Type = syn::parse_str( &subformer_definition_types_string )?; @@ -856,6 +857,7 @@ with the new content generated during the subforming process. /// #[ inline ] + #[ allow( clippy::format_in_format_args, clippy::too_many_lines, clippy::too_many_arguments ) ] pub fn subform_entry_setter ( &self, @@ -1146,6 +1148,7 @@ formation process of the `{item}`. /// See `tests/inc/former_tests/subform_scalar_manual.rs` for example of generated code. #[ inline ] + #[ allow( clippy::format_in_format_args, clippy::unnecessary_wraps, clippy::too_many_lines, clippy::too_many_arguments ) ] pub fn subform_scalar_setter ( &self, @@ -1490,12 +1493,12 @@ Essentially, this end action integrates the individually formed scalar value bac { if let Some( ref attr ) = self.attrs.scalar { - if let Some( ref name ) = attr.name.ref_internal() + if let Some( name ) = attr.name.ref_internal() { return name } } - return &self.ident; + self.ident } /// Get name of setter for subform scalar if such setter should be generated. @@ -1505,17 +1508,14 @@ Essentially, this end action integrates the individually formed scalar value bac { if attr.setter() { - if let Some( ref name ) = attr.name.ref_internal() + if let Some( name ) = attr.name.ref_internal() { - return Some( &name ) - } - else - { - return Some( &self.ident ) + return Some( name ) } + return Some( self.ident ) } } - return None; + None } /// Get name of setter for collection if such setter should be generated. @@ -1525,17 +1525,14 @@ Essentially, this end action integrates the individually formed scalar value bac { if attr.setter() { - if let Some( ref name ) = attr.name.ref_internal() - { - return Some( &name ) - } - else + if let Some( name ) = attr.name.ref_internal() { - return Some( &self.ident ) + return Some( name ) } + return Some( self.ident ) } } - return None; + None } /// Get name of setter for subform if such setter should be generated. @@ -1547,15 +1544,12 @@ Essentially, this end action integrates the individually formed scalar value bac { if let Some( ref name ) = attr.name.as_ref() { - return Some( &name ) - } - else - { - return Some( &self.ident ) + return Some( name ) } + return Some( self.ident ) } } - return None; + None } /// Is scalar setter required. Does not if collection of subformer setter requested. @@ -1567,13 +1561,13 @@ Essentially, this end action integrates the individually formed scalar value bac { if let Some( setter ) = attr.setter.internal() { - if setter == false + if !setter { return false } explicit = true; } - if let Some( ref _name ) = attr.name.ref_internal() + if let Some( _name ) = attr.name.ref_internal() { explicit = true; } @@ -1594,7 +1588,7 @@ Essentially, this end action integrates the individually formed scalar value bac return false; } - return true; + true } } diff --git a/module/core/former_meta/src/derive_former/field_attrs.rs b/module/core/former_meta/src/derive_former/field_attrs.rs index a662fe20ae..7f97619d9a 100644 --- a/module/core/former_meta/src/derive_former/field_attrs.rs +++ b/module/core/former_meta/src/derive_former/field_attrs.rs @@ -1,5 +1,5 @@ //! Attributes of a field. - +#[ allow( clippy::wildcard_imports ) ] use super::*; use macro_tools:: { @@ -85,7 +85,7 @@ impl FieldAttributes { // Get the attribute key as a string let key_ident = attr.path().get_ident().ok_or_else( || error( attr ) )?; - let key_str = format!( "{}", key_ident ); + let key_str = format!( "{key_ident}" ); // // Skip standard attributes // if attr::is_standard( &key_str ) @@ -102,7 +102,7 @@ impl FieldAttributes AttributeSubformScalarSetter::KEYWORD => result.assign( AttributeSubformScalarSetter::from_meta( attr )? ), AttributeSubformCollectionSetter::KEYWORD => result.assign( AttributeSubformCollectionSetter::from_meta( attr )? ), AttributeSubformEntrySetter::KEYWORD => result.assign( AttributeSubformEntrySetter::from_meta( attr )? ), - "debug" => {}, + // "debug" => {}, _ => {}, // _ => return Err( error( attr ) ), // attributes does not have to be known @@ -134,6 +134,7 @@ impl AttributeComponent for AttributeConfig const KEYWORD : &'static str = "former"; + #[ allow( clippy::match_wildcard_for_single_variants ) ] fn from_meta( attr : &syn::Attribute ) -> Result< Self > { match attr.meta @@ -144,7 +145,7 @@ impl AttributeComponent for AttributeConfig }, syn::Meta::Path( ref _path ) => { - syn::parse2::< AttributeConfig >( Default::default() ) + syn::parse2::< AttributeConfig >( TokenStream::default() ) }, _ => return_syn_err!( attr, "Expects an attribute of format #[ former( default = 13 ) ].\nGot: {}", qt!{ #attr } ), } @@ -270,6 +271,7 @@ impl AttributeComponent for AttributeScalarSetter const KEYWORD : &'static str = "scalar"; + #[ allow( clippy::match_wildcard_for_single_variants ) ] fn from_meta( attr : &syn::Attribute ) -> Result< Self > { match attr.meta @@ -280,7 +282,7 @@ impl AttributeComponent for AttributeScalarSetter }, syn::Meta::Path( ref _path ) => { - syn::parse2::< AttributeScalarSetter >( Default::default() ) + syn::parse2::< AttributeScalarSetter >( TokenStream::default() ) }, _ => return_syn_err!( attr, "Expects an attribute of format `#[ scalar( setter = false ) ]` or `#[ scalar( setter = true, name = my_name ) ]`. \nGot: {}", qt!{ #attr } ), } @@ -445,6 +447,7 @@ impl AttributeComponent for AttributeSubformScalarSetter const KEYWORD : &'static str = "subform_scalar"; + #[ allow( clippy::match_wildcard_for_single_variants ) ] fn from_meta( attr : &syn::Attribute ) -> Result< Self > { match attr.meta @@ -455,7 +458,7 @@ impl AttributeComponent for AttributeSubformScalarSetter }, syn::Meta::Path( ref _path ) => { - syn::parse2::< AttributeSubformScalarSetter >( Default::default() ) + syn::parse2::< AttributeSubformScalarSetter >( TokenStream::default() ) }, _ => return_syn_err!( attr, "Expects an attribute of format `#[ subform_scalar( setter = false ) ]` or `#[ subform_scalar( setter = true, name = my_name ) ]`. \nGot: {}", qt!{ #attr } ), } @@ -622,6 +625,7 @@ impl AttributeComponent for AttributeSubformCollectionSetter const KEYWORD : &'static str = "subform_collection"; + #[ allow( clippy::match_wildcard_for_single_variants ) ] fn from_meta( attr : &syn::Attribute ) -> Result< Self > { match attr.meta @@ -632,7 +636,7 @@ impl AttributeComponent for AttributeSubformCollectionSetter }, syn::Meta::Path( ref _path ) => { - syn::parse2::< AttributeSubformCollectionSetter >( Default::default() ) + syn::parse2::< AttributeSubformCollectionSetter >( TokenStream::default() ) }, _ => return_syn_err!( attr, "Expects an attribute of format `#[ subform_collection ]` or `#[ subform_collection( definition = former::VectorDefinition ) ]` if you want to use default collection defition. \nGot: {}", qt!{ #attr } ), } @@ -818,6 +822,7 @@ impl AttributeComponent for AttributeSubformEntrySetter const KEYWORD : &'static str = "subform_entry"; + #[ allow( clippy::match_wildcard_for_single_variants ) ] fn from_meta( attr : &syn::Attribute ) -> Result< Self > { match attr.meta @@ -828,7 +833,7 @@ impl AttributeComponent for AttributeSubformEntrySetter }, syn::Meta::Path( ref _path ) => { - syn::parse2::< AttributeSubformEntrySetter >( Default::default() ) + syn::parse2::< AttributeSubformEntrySetter >( TokenStream::default() ) }, _ => return_syn_err!( attr, "Expects an attribute of format `#[ subform_entry ]` or `#[ subform_entry( name : child )` ], \nGot: {}", qt!{ #attr } ), } diff --git a/module/core/former_meta/src/derive_former/struct_attrs.rs b/module/core/former_meta/src/derive_former/struct_attrs.rs index 31d6ab3491..f62fff702f 100644 --- a/module/core/former_meta/src/derive_former/struct_attrs.rs +++ b/module/core/former_meta/src/derive_former/struct_attrs.rs @@ -1,7 +1,7 @@ //! //! Attributes of the whole item. //! - +#[ allow( clippy::wildcard_imports ) ] use super::*; use macro_tools:: @@ -63,7 +63,7 @@ impl ItemAttributes { let key_ident = attr.path().get_ident().ok_or_else( || error( attr ) )?; - let key_str = format!( "{}", key_ident ); + let key_str = format!( "{key_ident}" ); // attributes does not have to be known // if attr::is_standard( &key_str ) @@ -76,7 +76,7 @@ impl ItemAttributes AttributeStorageFields::KEYWORD => result.assign( AttributeStorageFields::from_meta( attr )? ), AttributeMutator::KEYWORD => result.assign( AttributeMutator::from_meta( attr )? ), AttributePerform::KEYWORD => result.assign( AttributePerform::from_meta( attr )? ), - "debug" => {} + // "debug" => {} _ => {}, // _ => return Err( error( attr ) ), // attributes does not have to be known @@ -87,7 +87,7 @@ impl ItemAttributes } /// - /// Generate parts, used for generating `perform()`` method. + /// Generate parts, used for generating `perform()` method. /// /// Similar to `form()`, but will also invoke function from `perform` attribute, if specified. /// @@ -96,13 +96,13 @@ impl ItemAttributes /// ## perform : /// return result; /// - /// ## perform_output : - /// < T : ::core::default::Default > + /// ## `perform_output` : + /// < T : `::core::default::Default` > /// - /// ## perform_generics : + /// ## `perform_generics` : /// Vec< T > /// - + #[ allow( clippy::unnecessary_wraps ) ] pub fn performer( &self ) -> Result< ( TokenStream, TokenStream, TokenStream ) > { @@ -190,7 +190,7 @@ impl AttributeComponent for AttributeStorageFields { syn::Meta::List( ref meta_list ) => { - return syn::parse2::< AttributeStorageFields >( meta_list.tokens.clone() ); + syn::parse2::< AttributeStorageFields >( meta_list.tokens.clone() ) }, _ => return_syn_err!( attr, "Expects an attribute of format #[ storage_fields( a : i32, b : Option< String > ) ] .\nGot: {}", qt!{ #attr } ), @@ -260,6 +260,7 @@ pub struct AttributeMutator pub debug : AttributePropertyDebug, } +#[ allow( clippy::match_wildcard_for_single_variants ) ] impl AttributeComponent for AttributeMutator { const KEYWORD : &'static str = "mutator"; @@ -270,11 +271,11 @@ impl AttributeComponent for AttributeMutator { syn::Meta::List( ref meta_list ) => { - return syn::parse2::< AttributeMutator >( meta_list.tokens.clone() ); + syn::parse2::< AttributeMutator >( meta_list.tokens.clone() ) }, syn::Meta::Path( ref _path ) => { - return Ok( Default::default() ) + Ok( AttributeMutator::default() ) }, _ => return_syn_err!( attr, "Expects an attribute of format `#[ mutator( custom ) ]`. \nGot: {}", qt!{ #attr } ), } @@ -407,7 +408,7 @@ impl AttributeComponent for AttributePerform { syn::Meta::List( ref meta_list ) => { - return syn::parse2::< AttributePerform >( meta_list.tokens.clone() ); + syn::parse2::< AttributePerform >( meta_list.tokens.clone() ) }, _ => return_syn_err!( attr, "Expects an attribute of format #[ perform( fn parse( mut self ) -> Request ) ] .\nGot: {}", qt!{ #attr } ), diff --git a/module/core/former_types/Readme.md b/module/core/former_types/Readme.md index 30e14aaf08..742285fb8b 100644 --- a/module/core/former_types/Readme.md +++ b/module/core/former_types/Readme.md @@ -1,6 +1,6 @@ -# Module :: former_types +# Module :: `former_types` [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) [![rust-status](https://github.com/Wandalen/wTools/actions/workflows/module_former_types_push.yml/badge.svg)](https://github.com/Wandalen/wTools/actions/workflows/module_former_types_push.yml) [![docs.rs](https://img.shields.io/docsrs/former_types?color=e3e8f0&logo=docs.rs)](https://docs.rs/former_types) [![Open in Gitpod](https://raster.shields.io/static/v1?label=try&message=online&color=eee&logo=gitpod&logoColor=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=module%2Fcore%2Fformer_types%2Fexamples%2Fformer_types_trivial.rs,RUN_POSTFIX=--example%20former_types_trivial/https://github.com/Wandalen/wTools) [![discord](https://img.shields.io/discord/872391416519737405?color=eee&logo=discord&logoColor=eee&label=ask)](https://discord.gg/m3YfbXpUUY) diff --git a/module/core/former_types/src/collection.rs b/module/core/former_types/src/collection.rs index c7b25889b7..8df9c34554 100644 --- a/module/core/former_types/src/collection.rs +++ b/module/core/former_types/src/collection.rs @@ -9,6 +9,7 @@ mod private { + #[ allow( clippy::wildcard_imports ) ] use crate::*; /// Facilitates the conversion of collection entries to their corresponding value representations. @@ -347,6 +348,8 @@ mod private { /// Begins the construction process of a collection with optional initial storage and context, /// setting up an `on_end` completion handler to finalize the collection's construction. + /// # Panics + /// qqq: doc #[ inline( always ) ] pub fn begin ( @@ -370,6 +373,8 @@ mod private /// Provides a variation of the `begin` method allowing for coercion of the end handler, /// facilitating ease of integration with different end conditions. + /// # Panics + /// qqq: docs #[ inline( always ) ] pub fn begin_coercing< IntoEnd > ( @@ -394,6 +399,8 @@ mod private } /// Finalizes the building process, returning the formed or a context incorporating it. + /// # Panics + /// qqq: doc #[ inline( always ) ] pub fn end( mut self ) -> Definition::Formed { @@ -412,6 +419,7 @@ mod private /// Replaces the current storage with a provided storage, allowing for resetting or /// redirection of the building process. #[ inline( always ) ] + #[ must_use ] pub fn replace( mut self, storage : Definition::Storage ) -> Self { self.storage = storage; @@ -462,6 +470,8 @@ mod private /// Appends an entry to the end of the storage, expanding the internal collection. #[ inline( always ) ] + #[ must_use ] + #[ allow( clippy::should_implement_trait ) ] pub fn add< IntoElement >( mut self, entry : IntoElement ) -> Self where IntoElement : core::convert::Into< E >, { @@ -521,6 +531,7 @@ pub use own::*; #[ allow( unused_imports ) ] pub mod own { + #[ allow( clippy::wildcard_imports ) ] use super::*; #[ doc( inline ) ] pub use orphan::*; @@ -530,6 +541,7 @@ pub mod own #[ allow( unused_imports ) ] pub mod orphan { + #[ allow( clippy::wildcard_imports ) ] use super::*; #[ doc( inline ) ] pub use exposed::*; @@ -539,6 +551,7 @@ pub mod orphan #[ allow( unused_imports ) ] pub mod exposed { + #[ allow( clippy::wildcard_imports ) ] use super::*; #[ doc( inline ) ] diff --git a/module/core/former_types/src/collection/binary_heap.rs b/module/core/former_types/src/collection/binary_heap.rs index ae76f5e4f8..86d350d967 100644 --- a/module/core/former_types/src/collection/binary_heap.rs +++ b/module/core/former_types/src/collection/binary_heap.rs @@ -5,6 +5,7 @@ //! as subformer, enabling fluid and intuitive manipulation of binary heaps via builder patterns. //! +#[ allow( clippy::wildcard_imports ) ] use crate::*; #[ allow( unused ) ] use collection_tools::BinaryHeap; @@ -241,6 +242,7 @@ impl< E > BinaryHeapExt< E > for BinaryHeap< E > where E : Ord { + #[ allow( clippy::default_constructed_unit_structs ) ] fn former() -> BinaryHeapFormer< E, (), BinaryHeap< E >, ReturnStorage > { BinaryHeapFormer::< E, (), BinaryHeap< E >, ReturnStorage >::new( ReturnStorage::default() ) diff --git a/module/core/former_types/src/collection/btree_map.rs b/module/core/former_types/src/collection/btree_map.rs index d1d97bfde8..9f3d01e698 100644 --- a/module/core/former_types/src/collection/btree_map.rs +++ b/module/core/former_types/src/collection/btree_map.rs @@ -4,7 +4,7 @@ //! this module abstracts the operations on binary tree map-like data structures, making them more flexible and easier to integrate as //! as subformer, enabling fluid and intuitive manipulation of binary tree maps via builder patterns. //! - +#[ allow( clippy::wildcard_imports ) ] use crate::*; use collection_tools::BTreeMap; @@ -238,6 +238,7 @@ impl< K, E > BTreeMapExt< K, E > for BTreeMap< K, E > where K : Ord, { + #[ allow( clippy::default_constructed_unit_structs ) ] fn former() -> BTreeMapFormer< K, E, (), BTreeMap< K, E >, ReturnStorage > { BTreeMapFormer::< K, E, (), BTreeMap< K, E >, ReturnStorage >::new( ReturnStorage::default() ) diff --git a/module/core/former_types/src/collection/btree_set.rs b/module/core/former_types/src/collection/btree_set.rs index 360c9484ae..d09e1793d6 100644 --- a/module/core/former_types/src/collection/btree_set.rs +++ b/module/core/former_types/src/collection/btree_set.rs @@ -4,7 +4,7 @@ //! this module abstracts the operations on binary tree set-like data structures, making them more flexible and easier to integrate as //! as subformer, enabling fluid and intuitive manipulation of binary tree sets via builder patterns. //! - +#[ allow( clippy::wildcard_imports ) ] use crate::*; #[ allow( unused ) ] use collection_tools::BTreeSet; @@ -230,6 +230,7 @@ impl< E > BTreeSetExt< E > for BTreeSet< E > where E : Ord { + #[ allow( clippy::default_constructed_unit_structs ) ] fn former() -> BTreeSetFormer< E, (), BTreeSet< E >, ReturnStorage > { BTreeSetFormer::< E, (), BTreeSet< E >, ReturnStorage >::new( ReturnStorage::default() ) diff --git a/module/core/former_types/src/collection/hash_map.rs b/module/core/former_types/src/collection/hash_map.rs index f6d6f1b58d..1c4444fa4a 100644 --- a/module/core/former_types/src/collection/hash_map.rs +++ b/module/core/former_types/src/collection/hash_map.rs @@ -5,9 +5,11 @@ //! as subformer, enabling fluid and intuitive manipulation of hashmaps via builder patterns. //! +#[ allow( clippy::wildcard_imports ) ] use crate::*; use collection_tools::HashMap; +#[ allow( clippy::implicit_hasher ) ] impl< K, V > Collection for HashMap< K, V > where K : core::cmp::Eq + core::hash::Hash, @@ -23,6 +25,7 @@ where } +#[ allow( clippy::implicit_hasher ) ] impl< K, V > CollectionAdd for HashMap< K, V > where K : core::cmp::Eq + core::hash::Hash, @@ -36,6 +39,7 @@ where } +#[ allow( clippy::implicit_hasher ) ] impl< K, V > CollectionAssign for HashMap< K, V > where K : core::cmp::Eq + core::hash::Hash, @@ -53,6 +57,7 @@ where // = storage +#[ allow( clippy::implicit_hasher ) ] impl< K, E > Storage for HashMap< K, E > where @@ -61,6 +66,7 @@ where type Preformed = HashMap< K, E >; } +#[ allow( clippy::implicit_hasher ) ] impl< K, E > StoragePreform for HashMap< K, E > where @@ -155,6 +161,7 @@ where // = Entity To +#[ allow( clippy::implicit_hasher ) ] impl< K, E, Definition > EntityToFormer< Definition > for HashMap< K, E > where K : ::core::cmp::Eq + ::core::hash::Hash, @@ -174,6 +181,7 @@ where type Former = HashMapFormer< K, E, Definition::Context, Definition::Formed, Definition::End >; } +#[ allow( clippy::implicit_hasher ) ] impl< K, E > crate::EntityToStorage for HashMap< K, E > where @@ -182,6 +190,7 @@ where type Storage = HashMap< K, E >; } +#[ allow( clippy::implicit_hasher ) ] impl< K, E, Context, Formed, End > crate::EntityToDefinition< Context, Formed, End > for HashMap< K, E > where @@ -192,6 +201,7 @@ where type Types = HashMapDefinitionTypes< K, E, Context, Formed >; } +#[ allow( clippy::implicit_hasher ) ] impl< K, E, Context, Formed > crate::EntityToDefinitionTypes< Context, Formed > for HashMap< K, E > where @@ -234,6 +244,7 @@ where fn former() -> HashMapFormer< K, E, (), HashMap< K, E >, ReturnStorage >; } +#[ allow( clippy::default_constructed_unit_structs, clippy::implicit_hasher ) ] impl< K, E > HashMapExt< K, E > for HashMap< K, E > where K : ::core::cmp::Eq + ::core::hash::Hash, diff --git a/module/core/former_types/src/collection/hash_set.rs b/module/core/former_types/src/collection/hash_set.rs index 16d5dec6c0..97d7ddd66e 100644 --- a/module/core/former_types/src/collection/hash_set.rs +++ b/module/core/former_types/src/collection/hash_set.rs @@ -1,8 +1,9 @@ //! This module provides a builder pattern implementation (`HashSetFormer`) for `HashSet`-like collections. It is designed to extend the builder pattern, allowing for fluent and dynamic construction of sets within custom data structures. - +#[ allow( clippy::wildcard_imports ) ] use crate::*; use collection_tools::HashSet; +#[ allow( clippy::implicit_hasher ) ] impl< K > Collection for HashSet< K > where K : core::cmp::Eq + core::hash::Hash, @@ -18,6 +19,7 @@ where } +#[ allow( clippy::implicit_hasher ) ] impl< K > CollectionAdd for HashSet< K > where K : core::cmp::Eq + core::hash::Hash, @@ -33,6 +35,7 @@ where } +#[ allow( clippy::implicit_hasher ) ] impl< K > CollectionAssign for HashSet< K > where K : core::cmp::Eq + core::hash::Hash, @@ -49,6 +52,7 @@ where } } +#[ allow( clippy::implicit_hasher ) ] impl< K > CollectionValToEntry< K > for HashSet< K > where K : core::cmp::Eq + core::hash::Hash, @@ -91,6 +95,7 @@ where // = storage +#[ allow( clippy::implicit_hasher ) ] impl< K > Storage for HashSet< K > where @@ -100,6 +105,7 @@ where type Preformed = HashSet< K >; } +#[ allow( clippy::implicit_hasher ) ] impl< K > StoragePreform for HashSet< K > where @@ -187,6 +193,7 @@ where // = entity to +#[ allow( clippy::implicit_hasher ) ] impl< K, Definition > EntityToFormer< Definition > for HashSet< K > where K : ::core::cmp::Eq + ::core::hash::Hash, @@ -205,6 +212,7 @@ where type Former = HashSetFormer< K, Definition::Context, Definition::Formed, Definition::End >; } +#[ allow( clippy::implicit_hasher ) ] impl< K > crate::EntityToStorage for HashSet< K > where @@ -213,6 +221,7 @@ where type Storage = HashSet< K >; } +#[ allow( clippy::implicit_hasher ) ] impl< K, Context, Formed, End > crate::EntityToDefinition< Context, Formed, End > for HashSet< K > where @@ -223,6 +232,7 @@ where type Types = HashSetDefinitionTypes< K, Context, Formed >; } +#[ allow( clippy::implicit_hasher ) ] impl< K, Context, Formed > crate::EntityToDefinitionTypes< Context, Formed > for HashSet< K > where @@ -260,10 +270,12 @@ where fn former() -> HashSetFormer< K, (), HashSet< K >, ReturnStorage >; } +#[ allow( clippy::implicit_hasher ) ] impl< K > HashSetExt< K > for HashSet< K > where K : ::core::cmp::Eq + ::core::hash::Hash, { + #[ allow( clippy::default_constructed_unit_structs ) ] fn former() -> HashSetFormer< K, (), HashSet< K >, ReturnStorage > { HashSetFormer::< K, (), HashSet< K >, ReturnStorage >::new( ReturnStorage::default() ) diff --git a/module/core/former_types/src/collection/linked_list.rs b/module/core/former_types/src/collection/linked_list.rs index abdb327074..1ace9637a9 100644 --- a/module/core/former_types/src/collection/linked_list.rs +++ b/module/core/former_types/src/collection/linked_list.rs @@ -4,7 +4,7 @@ //! this module abstracts the operations on list-like data structures, making them more flexible and easier to integrate as //! as subformer, enabling fluid and intuitive manipulation of lists via builder patterns. //! - +#[ allow( clippy::wildcard_imports ) ] use crate::*; #[ allow( unused ) ] use collection_tools::LinkedList; @@ -221,6 +221,7 @@ pub trait LinkedListExt< E > : sealed::Sealed impl< E > LinkedListExt< E > for LinkedList< E > { + #[ allow( clippy::default_constructed_unit_structs ) ] fn former() -> LinkedListFormer< E, (), LinkedList< E >, ReturnStorage > { LinkedListFormer::< E, (), LinkedList< E >, ReturnStorage >::new( ReturnStorage::default() ) diff --git a/module/core/former_types/src/collection/vector.rs b/module/core/former_types/src/collection/vector.rs index 96f7e577f1..5e88250511 100644 --- a/module/core/former_types/src/collection/vector.rs +++ b/module/core/former_types/src/collection/vector.rs @@ -4,7 +4,7 @@ //! this module abstracts the operations on vector-like data structures, making them more flexible and easier to integrate as //! as subformer, enabling fluid and intuitive manipulation of vectors via builder patterns. //! - +#[ allow( clippy::wildcard_imports ) ] use crate::*; #[ allow( unused ) ] use collection_tools::Vec; @@ -221,6 +221,7 @@ pub trait VecExt< E > : sealed::Sealed impl< E > VecExt< E > for Vec< E > { + #[ allow( clippy::default_constructed_unit_structs ) ] fn former() -> VectorFormer< E, (), Vec< E >, ReturnStorage > { VectorFormer::< E, (), Vec< E >, ReturnStorage >::new( ReturnStorage::default() ) diff --git a/module/core/former_types/src/collection/vector_deque.rs b/module/core/former_types/src/collection/vector_deque.rs index f3b08c6c01..ae207c612e 100644 --- a/module/core/former_types/src/collection/vector_deque.rs +++ b/module/core/former_types/src/collection/vector_deque.rs @@ -4,7 +4,7 @@ //! this module abstracts the operations on vector deque-like data structures, making them more flexible and easier to integrate as //! as subformer, enabling fluid and intuitive manipulation of vector deques via builder patterns. //! - +#[ allow( clippy::wildcard_imports ) ] use crate::*; #[ allow( unused ) ] use collection_tools::VecDeque; @@ -221,6 +221,7 @@ pub trait VecDequeExt< E > : sealed::Sealed impl< E > VecDequeExt< E > for VecDeque< E > { + #[ allow( clippy::default_constructed_unit_structs ) ] fn former() -> VecDequeFormer< E, (), VecDeque< E >, ReturnStorage > { VecDequeFormer::< E, (), VecDeque< E >, ReturnStorage >::new( ReturnStorage::default() ) diff --git a/module/core/former_types/src/component.rs b/module/core/former_types/src/component.rs index 9e846e2673..001619ad1e 100644 --- a/module/core/former_types/src/component.rs +++ b/module/core/former_types/src/component.rs @@ -37,7 +37,7 @@ /// obj.assign( "New Name" ); /// assert_eq!( obj.name, "New Name" ); /// ``` -#[ cfg( any( feature = "types_component_assign" ) ) ] +#[ cfg( feature = "types_component_assign" ) ] pub trait Assign< T, IntoT > where IntoT : Into< T >, @@ -51,6 +51,7 @@ where /// Sets or replaces the component on the object with the given value. /// Unlike function (`assing`) function (`impute`) also consumes self and return it what is useful for builder pattern. #[ inline( always ) ] + #[ must_use ] fn impute( mut self, component : IntoT ) -> Self where Self : Sized, @@ -94,7 +95,7 @@ where /// opt_struct.option_assign( MyStruct { name: "New Name".to_string() } ); /// assert_eq!( opt_struct.unwrap().name, "New Name" ); /// ``` -#[ cfg( any( feature = "types_component_assign" ) ) ] +#[ cfg( feature = "types_component_assign" ) ] pub trait OptionExt< T > : sealed::Sealed where T : Sized + Assign< T, T >, @@ -109,7 +110,7 @@ where fn option_assign( & mut self, src : T ); } -#[ cfg( any( feature = "types_component_assign" ) ) ] +#[ cfg( feature = "types_component_assign" ) ] impl< T > OptionExt< T > for Option< T > where T : Sized + Assign< T, T >, @@ -125,7 +126,7 @@ where } } -#[ cfg( any( feature = "types_component_assign" ) ) ] +#[ cfg( feature = "types_component_assign" ) ] mod sealed { pub trait Sealed {} @@ -172,7 +173,7 @@ mod sealed /// /// assert_eq!( user_profile.username, "john_doe" ); /// ``` -#[ cfg( any( feature = "types_component_assign" ) ) ] +#[ cfg( feature = "types_component_assign" ) ] pub trait AssignWithType { /// Sets the value of a component by its type. @@ -196,7 +197,7 @@ pub trait AssignWithType Self : Assign< T, IntoT >; } -#[ cfg( any( feature = "types_component_assign" ) ) ] +#[ cfg( feature = "types_component_assign" ) ] impl< S > AssignWithType for S { #[ inline( always ) ] diff --git a/module/core/former_types/src/forming.rs b/module/core/former_types/src/forming.rs index d95bea8666..46c9b87aff 100644 --- a/module/core/former_types/src/forming.rs +++ b/module/core/former_types/src/forming.rs @@ -163,6 +163,7 @@ use alloc::boxed::Box; /// a closure needs to be stored or passed around as an object implementing /// `FormingEnd`. #[ cfg( any( not( feature = "no_std" ), feature = "use_alloc" ) ) ] +#[ allow( clippy::type_complexity ) ] pub struct FormingEndClosure< Definition : crate::FormerDefinitionTypes > { closure : Box< dyn Fn( Definition::Storage, Option< Definition::Context > ) -> Definition::Formed >, diff --git a/module/core/former_types/src/lib.rs b/module/core/former_types/src/lib.rs index 39196a30e7..f4c1ac346c 100644 --- a/module/core/former_types/src/lib.rs +++ b/module/core/former_types/src/lib.rs @@ -29,7 +29,7 @@ mod collection; /// Component-based forming. #[ cfg( feature = "enabled" ) ] -#[ cfg( any( feature = "types_component_assign" ) ) ] +#[ cfg( feature = "types_component_assign" ) ] mod component; /// Namespace with dependencies. @@ -49,6 +49,7 @@ pub use own::*; #[ allow( unused_imports ) ] pub mod own { + #[ allow( clippy::wildcard_imports ) ] use super::*; #[ doc( inline ) ] pub use orphan::*; @@ -59,6 +60,7 @@ pub mod own #[ allow( unused_imports ) ] pub mod orphan { + #[ allow( clippy::wildcard_imports ) ] use super::*; #[ doc( inline ) ] @@ -76,6 +78,7 @@ pub mod orphan #[ allow( unused_imports ) ] pub mod exposed { + #[ allow( clippy::wildcard_imports ) ] use super::*; #[ doc( inline ) ] @@ -103,10 +106,11 @@ pub mod exposed #[ allow( unused_imports ) ] pub mod prelude { + #[ allow( clippy::wildcard_imports ) ] use super::*; #[ doc( inline ) ] - #[ cfg( any( feature = "types_component_assign" ) ) ] + #[ cfg( feature = "types_component_assign" ) ] pub use component::*; #[ doc( inline ) ] diff --git a/module/core/impls_index/Cargo.toml b/module/core/impls_index/Cargo.toml index 392f5bf2f0..fc8c02eca0 100644 --- a/module/core/impls_index/Cargo.toml +++ b/module/core/impls_index/Cargo.toml @@ -34,4 +34,4 @@ impls_index_meta = { workspace = true } [dev-dependencies] test_tools = { workspace = true } -tempdir = { workspace = true } +#tempdir = { version = "0.3.7" } diff --git a/module/core/interval_adapter/Readme.md b/module/core/interval_adapter/Readme.md index 19cfc05f9e..f1d171804d 100644 --- a/module/core/interval_adapter/Readme.md +++ b/module/core/interval_adapter/Readme.md @@ -1,11 +1,11 @@ -# Module :: interval_adapter +# Module :: `interval_adapter` [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) [![rust-status](https://github.com/Wandalen/wTools/actions/workflows/module_interval_adapter_push.yml/badge.svg)](https://github.com/Wandalen/wTools/actions/workflows/module_interval_adapter_push.yml) [![docs.rs](https://img.shields.io/docsrs/interval_adapter?color=e3e8f0&logo=docs.rs)](https://docs.rs/interval_adapter) [![Open in Gitpod](https://raster.shields.io/static/v1?label=try&message=online&color=eee&logo=gitpod&logoColor=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=module%2Fcore%2Finterval_adapter%2Fexamples%2Finterval_adapter_trivial.rs,RUN_POSTFIX=--example%20interval_adapter_trivial/https://github.com/Wandalen/wTools) [![discord](https://img.shields.io/discord/872391416519737405?color=eee&logo=discord&logoColor=eee&label=ask)](https://discord.gg/m3YfbXpUUY) -Integer interval adapter for both Range and RangeInclusive. +Integer interval adapter for both Range and `RangeInclusive`. Let's assume you have a function which should accept Interval. But you don't want to limit caller of the function to either half-open interval `core::ops::Range` or closed one `core::ops::RangeInclusive` you want allow to use anyone of iterable interval. To make that work smoothly use `IterableInterval`. Both `core::ops::Range` and `core::ops::RangeInclusive` implement the trait, also it's possible to work with non-iterable intervals, like ( -Infinity .. +Infinity ). diff --git a/module/core/interval_adapter/src/lib.rs b/module/core/interval_adapter/src/lib.rs index 3fcc39e3f1..19fb7c2537 100644 --- a/module/core/interval_adapter/src/lib.rs +++ b/module/core/interval_adapter/src/lib.rs @@ -11,9 +11,11 @@ mod private #[ doc( inline ) ] #[ allow( unused_imports ) ] + #[ allow( clippy::pub_use ) ] pub use core::ops::Bound; #[ doc( inline ) ] #[ allow( unused_imports ) ] + #[ allow( clippy::pub_use ) ] pub use core::ops::RangeBounds; use core::cmp::{ PartialEq, Eq }; @@ -21,6 +23,7 @@ mod private // xxx : seal it + #[ allow( clippy::wrong_self_convention ) ] /// Extend bound adding few methods. pub trait BoundExt< T > where @@ -39,23 +42,25 @@ mod private isize : Into< T >, { #[ inline( always ) ] + #[ allow( clippy::arithmetic_side_effects, clippy::implicit_return, clippy::pattern_type_mismatch ) ] fn into_left_closed( &self ) -> T { match self { - Bound::Included( v ) => *v, - Bound::Excluded( v ) => *v + 1.into(), + Bound::Included( value ) => *value, + Bound::Excluded( value ) => *value + 1.into(), Bound::Unbounded => 0.into(), // Bound::Unbounded => isize::MIN.into(), } } #[ inline( always ) ] + #[ allow( clippy::arithmetic_side_effects, clippy::implicit_return, clippy::pattern_type_mismatch ) ] fn into_right_closed( &self ) -> T { match self { - Bound::Included( v ) => *v, - Bound::Excluded( v ) => *v - 1.into(), + Bound::Included( value ) => *value, + Bound::Excluded( value ) => *value - 1.into(), Bound::Unbounded => isize::MAX.into(), } } @@ -97,6 +102,7 @@ mod private fn right( &self ) -> Bound< T >; /// Interval in closed format as pair of numbers. /// To convert open endpoint to closed add or subtract one. + #[ allow( clippy::implicit_return ) ] #[ inline( always ) ] fn bounds( &self ) -> ( Bound< T >, Bound< T > ) { @@ -104,18 +110,21 @@ mod private } /// The left endpoint of the interval, converting interval into closed one. + #[ allow( clippy::implicit_return ) ] #[ inline( always ) ] fn closed_left( &self ) -> T { self.left().into_left_closed() } /// The right endpoint of the interval, converting interval into closed one. + #[ allow( clippy::implicit_return ) ] #[ inline( always ) ] fn closed_right( &self ) -> T { self.right().into_right_closed() } /// Length of the interval, converting interval into closed one. + #[ allow( clippy::implicit_return, clippy::arithmetic_side_effects ) ] #[ inline( always ) ] fn closed_len( &self ) -> T { @@ -123,6 +132,7 @@ mod private self.closed_right() - self.closed_left() + one } /// Interval in closed format as pair of numbers, converting interval into closed one. + #[ allow( clippy::implicit_return ) ] #[ inline( always ) ] fn closed( &self ) -> ( T, T ) { @@ -130,6 +140,7 @@ mod private } /// Convert to interval in canonical format. + #[ allow( unknown_lints, clippy::implicit_return ) ] #[ inline( always ) ] fn canonical( &self ) -> Interval< T > { @@ -166,16 +177,19 @@ mod private /// /// Canonical implementation of interval. Other implementations of interval is convertible to it. /// - /// Both [core::ops::Range], [core::ops::RangeInclusive] are convertable to [crate::Interval] + /// Both [`core::ops::Range`], [`core::ops::RangeInclusive`] are convertable to [`crate::Interval`] /// + #[ allow( clippy::used_underscore_binding ) ] #[ derive( PartialEq, Eq, Debug, Clone, Copy ) ] pub struct Interval< T = isize > where T : EndPointTrait< T >, isize : Into< T >, { + /// Left _left : Bound< T >, + /// Right _right : Bound< T >, } @@ -185,15 +199,18 @@ mod private isize : Into< T >, { /// Constructor of an interval. Expects closed interval in arguments. + #[ allow( unknown_lints, clippy::implicit_return ) ] + #[ inline ] pub fn new( left : Bound< T >, right : Bound< T > ) -> Self { Self { _left : left, _right : right } } /// Convert to interval in canonical format. + #[ allow( clippy::implicit_return ) ] #[ inline( always ) ] pub fn iter< It >( &self ) -> impl Iterator< Item = T > { - ( &self ).into_iter() + self.into_iter() } } @@ -208,6 +225,7 @@ mod private { type Item = T; type IntoIter = IntervalIterator< T >; + #[ allow( clippy::implicit_return ) ] #[ inline( always ) ] fn into_iter( self ) -> Self::IntoIter { @@ -222,6 +240,7 @@ mod private { type Item = T; type IntoIter = IntervalIterator< T >; + #[ allow( unknown_lints, clippy::implicit_return ) ] #[ inline( always ) ] fn into_iter( self ) -> Self::IntoIter { @@ -229,13 +248,16 @@ mod private } } + /// qqq: Documentation #[ derive( Debug ) ] pub struct IntervalIterator< T > where T : EndPointTrait< T >, isize : Into< T >, { + /// current current : T, + /// right right : T, } @@ -245,6 +267,7 @@ mod private isize : Into< T >, { /// Constructor. + #[ allow( clippy::used_underscore_binding, clippy::implicit_return ) ] pub fn new( ins : Interval< T > ) -> Self { let current = ins._left.into_left_closed(); @@ -253,12 +276,14 @@ mod private } } + #[ allow( clippy::missing_trait_methods ) ] impl< T > Iterator for IntervalIterator< T > where T : EndPointTrait< T >, isize : Into< T >, { type Item = T; + #[ allow( clippy::implicit_return, clippy::arithmetic_side_effects ) ] #[ inline( always ) ] fn next( &mut self ) -> Option< Self::Item > { @@ -298,17 +323,20 @@ mod private // } // } + #[ allow( clippy::used_underscore_binding, clippy::missing_trait_methods ) ] impl< T > NonIterableInterval< T > for Interval< T > where T : EndPointTrait< T >, isize : Into< T >, { + #[ allow( clippy::implicit_return ) ] #[ inline( always ) ] fn left( &self ) -> Bound< T > { self._left } + #[ allow( clippy::implicit_return ) ] #[ inline( always ) ] fn right( &self ) -> Bound< T > { @@ -316,17 +344,20 @@ mod private } } + #[ allow( clippy::missing_trait_methods ) ] impl< T > NonIterableInterval< T > for core::ops::Range< T > where T : EndPointTrait< T >, isize : Into< T >, { + #[ allow( clippy::implicit_return ) ] #[ inline( always ) ] fn left( &self ) -> Bound< T > { Bound::Included( self.start ) } + #[ allow( clippy::implicit_return ) ] #[ inline( always ) ] fn right( &self ) -> Bound< T > { @@ -334,17 +365,20 @@ mod private } } + #[ allow( clippy::missing_trait_methods ) ] impl< T > NonIterableInterval< T > for core::ops::RangeInclusive< T > where T : EndPointTrait< T >, isize : Into< T >, { + #[ allow( clippy::implicit_return ) ] #[ inline( always ) ] fn left( &self ) -> Bound< T > { Bound::Included( *self.start() ) } + #[ allow( clippy::implicit_return ) ] #[ inline( always ) ] fn right( &self ) -> Bound< T > { @@ -352,17 +386,20 @@ mod private } } + #[ allow( clippy::missing_trait_methods ) ] impl< T > NonIterableInterval< T > for core::ops::RangeTo< T > where T : EndPointTrait< T >, isize : Into< T >, { + #[ allow( clippy::implicit_return ) ] #[ inline( always ) ] fn left( &self ) -> Bound< T > { Bound::Unbounded } + #[ allow( clippy::implicit_return ) ] #[ inline( always ) ] fn right( &self ) -> Bound< T > { @@ -370,17 +407,20 @@ mod private } } + #[ allow( clippy::missing_trait_methods ) ] impl< T > NonIterableInterval< T > for core::ops::RangeToInclusive< T > where T : EndPointTrait< T >, isize : Into< T >, { + #[ allow( clippy::implicit_return ) ] #[ inline( always ) ] fn left( &self ) -> Bound< T > { Bound::Unbounded } + #[ allow( clippy::implicit_return )] #[ inline( always ) ] fn right( &self ) -> Bound< T > { @@ -388,17 +428,20 @@ mod private } } + #[allow( clippy::missing_trait_methods ) ] impl< T > NonIterableInterval< T > for core::ops::RangeFrom< T > where T : EndPointTrait< T >, isize : Into< T >, { + #[ allow( clippy::implicit_return )] #[ inline( always ) ] fn left( &self ) -> Bound< T > { Bound::Included( self.start ) } + #[ allow( clippy::implicit_return )] #[ inline( always ) ] fn right( &self ) -> Bound< T > { @@ -406,17 +449,20 @@ mod private } } + #[ allow( clippy::missing_trait_methods ) ] impl< T > NonIterableInterval< T > for core::ops::RangeFull where T : EndPointTrait< T >, isize : Into< T >, { + #[ allow( clippy::implicit_return )] #[ inline( always ) ] fn left( &self ) -> Bound< T > { Bound::Unbounded } + #[ allow( clippy::implicit_return )] #[ inline( always ) ] fn right( &self ) -> Bound< T > { @@ -424,17 +470,20 @@ mod private } } + #[ allow( clippy::missing_trait_methods ) ] impl< T > NonIterableInterval< T > for ( T, T ) where T : EndPointTrait< T >, isize : Into< T >, { + #[ allow( clippy::implicit_return )] #[ inline( always ) ] fn left( &self ) -> Bound< T > { Bound::Included( self.0 ) } + #[ allow( clippy::implicit_return )] #[ inline( always ) ] fn right( &self ) -> Bound< T > { @@ -442,17 +491,22 @@ mod private } } + #[ allow( clippy::missing_trait_methods ) ] impl< T > NonIterableInterval< T > for ( Bound< T >, Bound< T > ) where T : EndPointTrait< T >, isize : Into< T >, { + #[ allow( unknown_lints )] + #[ allow( clippy::implicit_return )] #[ inline( always ) ] fn left( &self ) -> Bound< T > { self.0 } + #[ allow( unknown_lints )] + #[ allow( clippy::implicit_return )] #[ inline( always ) ] fn right( &self ) -> Bound< T > { @@ -460,17 +514,21 @@ mod private } } + #[ allow( clippy::missing_trait_methods ) ] impl< T > NonIterableInterval< T > for [ T ; 2 ] where T : EndPointTrait< T >, isize : Into< T >, { + #[ allow( clippy::implicit_return )] #[ inline( always ) ] fn left( &self ) -> Bound< T > { Bound::Included( self[ 0 ] ) } + #[ allow( unknown_lints )] + #[ allow( clippy::implicit_return )] #[ inline( always ) ] fn right( &self ) -> Bound< T > { @@ -478,17 +536,20 @@ mod private } } + #[ allow( clippy::missing_trait_methods ) ] impl< T > NonIterableInterval< T > for [ Bound< T > ; 2 ] where T : EndPointTrait< T >, isize : Into< T >, { + #[ allow( clippy::implicit_return )] #[ inline( always ) ] fn left( &self ) -> Bound< T > { self[ 0 ] } + #[ allow( clippy::implicit_return )] #[ inline( always ) ] fn right( &self ) -> Bound< T > { @@ -499,7 +560,7 @@ mod private // = // from for std // = - + /// qqq: documentation macro_rules! impl_interval_from { {} => {}; @@ -519,7 +580,7 @@ mod private { let _left = NonIterableInterval::left( &src ); let _right = NonIterableInterval::right( &src ); - Self { _left, _right } + return Self { _left, _right } } } }; @@ -564,6 +625,9 @@ mod private isize : Into< T >, Interval< T > : From< Self >, { + #[ allow( unknown_lints )] + #[ allow( clippy::implicit_return )] + #[ inline ] fn into_interval( self ) -> Interval< T > { From::from( self ) @@ -576,6 +640,7 @@ mod private #[ allow( unused_imports ) ] #[ cfg( feature = "enabled" ) ] // #[ allow( unused_imports ) ] +#[ allow( clippy::pub_use ) ] pub use own::*; /// Own namespace of the module. @@ -583,7 +648,8 @@ pub use own::*; #[ allow( unused_imports ) ] pub mod own { - use super::*; + use super::orphan; + #[ allow( clippy::useless_attribute, clippy::pub_use ) ] #[ doc( inline ) ] pub use orphan::*; } @@ -593,8 +659,9 @@ pub mod own #[ allow( unused_imports ) ] pub mod orphan { - use super::*; + use super::exposed; #[ doc( inline ) ] + #[ allow( clippy::useless_attribute, clippy::pub_use ) ] pub use exposed::*; } @@ -603,10 +670,12 @@ pub mod orphan #[ allow( unused_imports ) ] pub mod exposed { - use super::*; + use super::{ prelude, private }; #[ doc( inline ) ] + #[ allow( clippy::useless_attribute, clippy::pub_use ) ] pub use prelude::*; #[ doc( inline ) ] + #[ allow( clippy::useless_attribute, clippy::pub_use ) ] pub use private:: { Bound, @@ -620,7 +689,7 @@ pub mod exposed } // #[ doc( inline ) ] -#[ allow( unused_imports ) ] +// #[ allow( unused_imports ) ] // #[ cfg( feature = "enabled" ) ] // #[ allow( unused_imports ) ] // pub use exposed::*; @@ -630,8 +699,9 @@ pub mod exposed #[ allow( unused_imports ) ] pub mod prelude { - use super::*; + use super::private; #[ doc( inline ) ] + #[ allow( clippy::useless_attribute, clippy::pub_use ) ] pub use private:: { IterableInterval, diff --git a/module/core/iter_tools/Readme.md b/module/core/iter_tools/Readme.md index 4aaebd7c0f..cafefb8cc7 100644 --- a/module/core/iter_tools/Readme.md +++ b/module/core/iter_tools/Readme.md @@ -1,6 +1,6 @@ -# Module :: iter_tools +# Module :: `iter_tools` [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) [![rust-status](https://github.com/Wandalen/wTools/actions/workflows/module_iter_tools_push.yml/badge.svg)](https://github.com/Wandalen/wTools/actions/workflows/module_iter_tools_push.yml) [![docs.rs](https://img.shields.io/docsrs/iter_tools?color=e3e8f0&logo=docs.rs)](https://docs.rs/iter_tools) [![Open in Gitpod](https://raster.shields.io/static/v1?label=try&message=online&color=eee&logo=gitpod&logoColor=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=module%2Fcore%2Fiter_tools%2Fexamples%2Fiter_tools_trivial.rs,RUN_POSTFIX=--example%20iter_tools_trivial/https://github.com/Wandalen/wTools) [![discord](https://img.shields.io/discord/872391416519737405?color=eee&logo=discord&logoColor=eee&label=ask)](https://discord.gg/m3YfbXpUUY) @@ -50,7 +50,7 @@ cd wTools cd examples/iter_tools_trivial cargo run ``` -` + ### Try out from the repository diff --git a/module/core/iter_tools/src/iter.rs b/module/core/iter_tools/src/iter.rs index 727e18409f..62b0294989 100644 --- a/module/core/iter_tools/src/iter.rs +++ b/module/core/iter_tools/src/iter.rs @@ -173,6 +173,8 @@ mod private Self : core::iter::Iterator, { /// Iterate each element and return `core::Result::Err` if any element is error. + /// # Errors + /// qqq: errors fn map_result< F, RE, El >( self, f : F ) -> core::result::Result< Vec< El >, RE > where Self : Sized + Clone, @@ -209,6 +211,7 @@ pub use own::*; #[ allow( unused_imports ) ] pub mod own { + #[ allow( clippy::wildcard_imports ) ] use super::*; #[ doc( inline ) ] pub use orphan::*; @@ -219,6 +222,7 @@ pub mod own #[ allow( unused_imports ) ] pub mod orphan { + #[ allow( clippy::wildcard_imports ) ] use super::*; #[ doc( inline ) ] @@ -269,7 +273,7 @@ pub mod orphan #[ cfg( not( feature = "no_std" ) ) ] #[ doc( inline ) ] - pub use std::iter::zip; + pub use core::iter::zip; } @@ -277,6 +281,7 @@ pub mod orphan #[ allow( unused_imports ) ] pub mod exposed { + #[ allow( clippy::wildcard_imports ) ] use super::*; #[ doc( inline ) ] @@ -306,6 +311,7 @@ pub mod exposed #[ allow( unused_imports ) ] pub mod prelude { + #[ allow( clippy::wildcard_imports ) ] use super::*; #[ doc( inline ) ] diff --git a/module/core/iter_tools/src/lib.rs b/module/core/iter_tools/src/lib.rs index caa22f5593..e4b744172f 100644 --- a/module/core/iter_tools/src/lib.rs +++ b/module/core/iter_tools/src/lib.rs @@ -32,6 +32,7 @@ pub use own::*; #[ allow( unused_imports ) ] pub mod own { + #[ allow( clippy::wildcard_imports ) ] use super::*; #[ doc( inline ) ] @@ -48,6 +49,7 @@ pub mod own #[ allow( unused_imports ) ] pub mod orphan { + #[ allow( clippy::wildcard_imports ) ] use super::*; #[ doc( inline ) ] pub use exposed::*; @@ -58,6 +60,7 @@ pub mod orphan #[ allow( unused_imports ) ] pub mod exposed { + #[ allow( clippy::wildcard_imports ) ] use super::*; #[ doc( inline ) ] pub use prelude::*; diff --git a/module/core/macro_tools/Readme.md b/module/core/macro_tools/Readme.md index 6d148200b3..8cb337367c 100644 --- a/module/core/macro_tools/Readme.md +++ b/module/core/macro_tools/Readme.md @@ -1,6 +1,6 @@ -# Module :: proc_macro_tools +# Module :: `proc_macro_tools` [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) [![rust-status](https://github.com/Wandalen/wTools/actions/workflows/module_macro_tools_push.yml/badge.svg)](https://github.com/Wandalen/wTools/actions/workflows/module_macro_tools_push.yml) [![docs.rs](https://img.shields.io/docsrs/macro_tools?color=e3e8f0&logo=docs.rs)](https://docs.rs/macro_tools) [![Open in Gitpod](https://raster.shields.io/static/v1?label=try&message=online&color=eee&logo=gitpod&logoColor=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=module%2Fcore%2Fmacro_tools%2Fexamples%2Fmacro_tools_trivial.rs,RUN_POSTFIX=--example%20macro_tools_trivial/https://github.com/Wandalen/wTools) [![discord](https://img.shields.io/discord/872391416519737405?color=eee&logo=discord&logoColor=eee&label=ask)](https://discord.gg/m3YfbXpUUY) @@ -67,10 +67,10 @@ using reusable components like `AttributePropertyBoolean`. - `AttributeComponent`: A trait that defines how an attribute should be parsed from a `syn::Attribute`. - `AttributePropertyComponent`: A trait that defines a marker for attribute properties. - `Assign`: A trait that simplifies the logic of assigning fields to a struct. Using a -component-based approach requires each field to have a unique type, which aligns with the -strengths of strongly-typed languages. This method ensures that the logic of -assigning values to fields is encapsulated within the fields themselves, promoting modularity -and reusability. + component-based approach requires each field to have a unique type, which aligns with the + strengths of strongly-typed languages. This method ensures that the logic of + assigning values to fields is encapsulated within the fields themselves, promoting modularity + and reusability. The reusable property components from the library come with parameters that distinguish different properties of the same type. This is useful when an attribute has multiple boolean diff --git a/module/core/macro_tools/src/attr.rs b/module/core/macro_tools/src/attr.rs index 15c0d1e69d..e2edde225f 100644 --- a/module/core/macro_tools/src/attr.rs +++ b/module/core/macro_tools/src/attr.rs @@ -5,6 +5,7 @@ /// Define a private namespace for all its items. mod private { + #[ allow( clippy::wildcard_imports ) ] use crate::*; /// Checks if the given iterator of attributes contains an attribute named `debug`. @@ -48,7 +49,8 @@ mod private /// /// assert!( contains_debug, "Expected to find 'debug' attribute" ); /// ``` - /// + /// # Errors + /// qqq: doc pub fn has_debug< 'a >( attrs : impl Iterator< Item = &'a syn::Attribute > ) -> syn::Result< bool > { @@ -56,7 +58,7 @@ mod private { if let Some( ident ) = attr.path().get_ident() { - let ident_string = format!( "{}", ident ); + let ident_string = format!( "{ident}" ); if ident_string == "debug" { return Ok( true ) @@ -67,7 +69,7 @@ mod private return_syn_err!( "Unknown structure attribute:\n{}", qt!{ attr } ); } } - return Ok( false ) + Ok( false ) } /// Checks if the given attribute name is a standard Rust attribute. @@ -110,8 +112,9 @@ mod private /// assert_eq!( macro_tools::attr::is_standard( "my_attribute" ), false ); /// ``` /// - - pub fn is_standard<'a>( attr_name : &'a str ) -> bool + #[ must_use ] + #[ allow( clippy::match_same_arms ) ] + pub fn is_standard( attr_name : &str ) -> bool { match attr_name { @@ -199,6 +202,7 @@ mod private } } + #[ allow( clippy::iter_without_into_iter ) ] impl AttributesInner { /// Iterator @@ -208,6 +212,7 @@ mod private } } + #[ allow( clippy::default_trait_access ) ] impl syn::parse::Parse for AttributesInner { @@ -274,6 +279,7 @@ mod private } } + #[ allow( clippy::iter_without_into_iter ) ] impl AttributesOuter { /// Iterator @@ -283,6 +289,7 @@ mod private } } + #[ allow( clippy::default_trait_access ) ] impl syn::parse::Parse for AttributesOuter { @@ -425,6 +432,9 @@ mod private /// # Returns /// /// A `syn::Result` containing the constructed component if successful, or an error if the parsing fails. + /// + /// # Errors + /// qqq: doc fn from_meta( attr : &syn::Attribute ) -> syn::Result< Self >; // zzz : redo maybe @@ -440,6 +450,7 @@ pub use own::*; #[ allow( unused_imports ) ] pub mod own { + #[ allow( clippy::wildcard_imports ) ] use super::*; #[ doc( inline ) ] pub use orphan::*; @@ -456,6 +467,7 @@ pub mod own #[ allow( unused_imports ) ] pub mod orphan { + #[ allow( clippy::wildcard_imports ) ] use super::*; #[ doc( inline ) ] pub use exposed::*; @@ -465,6 +477,7 @@ pub mod orphan #[ allow( unused_imports ) ] pub mod exposed { + #[ allow( clippy::wildcard_imports ) ] use super::*; pub use super::super::attr; diff --git a/module/core/macro_tools/src/attr_prop.rs b/module/core/macro_tools/src/attr_prop.rs index 8ffa3d9cab..e981e9803a 100644 --- a/module/core/macro_tools/src/attr_prop.rs +++ b/module/core/macro_tools/src/attr_prop.rs @@ -151,6 +151,7 @@ pub use own::*; #[ allow( unused_imports ) ] pub mod own { + #[ allow( clippy::wildcard_imports ) ] use super::*; #[ doc( inline ) ] pub use orphan::*; @@ -164,6 +165,7 @@ pub mod own #[ allow( unused_imports ) ] pub mod orphan { + #[ allow( clippy::wildcard_imports ) ] use super::*; #[ doc( inline ) ] pub use exposed::*; @@ -173,6 +175,7 @@ pub mod orphan #[ allow( unused_imports ) ] pub mod exposed { + #[ allow( clippy::wildcard_imports ) ] use super::*; pub use super::super::attr_prop; diff --git a/module/core/macro_tools/src/attr_prop/boolean.rs b/module/core/macro_tools/src/attr_prop/boolean.rs index fa786b990d..d57917b576 100644 --- a/module/core/macro_tools/src/attr_prop/boolean.rs +++ b/module/core/macro_tools/src/attr_prop/boolean.rs @@ -3,6 +3,8 @@ //! Defaults to `false`. //! +use core::marker::PhantomData; +#[ allow( clippy::wildcard_imports ) ] use crate::*; // use former_types::Assign; @@ -114,6 +116,7 @@ pub struct AttributePropertyBoolean< Marker = AttributePropertyBooleanMarker >( impl< Marker > AttributePropertyBoolean< Marker > { /// Just unwraps and returns the internal data. + #[ must_use ] #[ inline( always ) ] pub fn internal( self ) -> bool { @@ -122,6 +125,7 @@ impl< Marker > AttributePropertyBoolean< Marker > /// Returns a reference to the internal boolean value. #[ inline( always ) ] + #[ must_use ] pub fn ref_internal( &self ) -> &bool { &self.0 @@ -160,9 +164,10 @@ impl< Marker > syn::parse::Parse for AttributePropertyBoolean< Marker > impl< Marker > From< bool > for AttributePropertyBoolean< Marker > { #[ inline( always ) ] + #[ allow( clippy::default_constructed_unit_structs ) ] fn from( src : bool ) -> Self { - Self( src, Default::default() ) + Self( src, PhantomData::default() ) } } diff --git a/module/core/macro_tools/src/attr_prop/boolean_optional.rs b/module/core/macro_tools/src/attr_prop/boolean_optional.rs index e695db40dd..bbc953c63a 100644 --- a/module/core/macro_tools/src/attr_prop/boolean_optional.rs +++ b/module/core/macro_tools/src/attr_prop/boolean_optional.rs @@ -2,7 +2,8 @@ //! A generic optional boolean attribute property: `Option< bool >`. //! Defaults to `false`. //! - +use core::marker::PhantomData; +#[ allow( clippy::wildcard_imports ) ] use crate::*; use components::Assign; @@ -19,6 +20,7 @@ pub struct AttributePropertyOptionalBoolean< Marker = AttributePropertyOptionalB impl< Marker > AttributePropertyOptionalBoolean< Marker > { /// Just unwraps and returns the internal data. + #[ must_use ] #[ inline( always ) ] pub fn internal( self ) -> Option< bool > { @@ -26,6 +28,7 @@ impl< Marker > AttributePropertyOptionalBoolean< Marker > } /// Returns a reference to the internal optional boolean value. + #[ must_use ] #[ inline( always ) ] pub fn ref_internal( &self ) -> Option< &bool > { @@ -42,6 +45,7 @@ where /// Inserts value of another instance into the option if it is None, then returns a mutable reference to the contained value. /// If another instance does is None then do nothing. #[ inline( always ) ] + #[ allow( clippy::single_match ) ] fn assign( &mut self, component : IntoT ) { let component = component.into(); @@ -73,18 +77,20 @@ impl< Marker > syn::parse::Parse for AttributePropertyOptionalBoolean< Marker > impl< Marker > From< bool > for AttributePropertyOptionalBoolean< Marker > { #[ inline( always ) ] + #[ allow( clippy::default_constructed_unit_structs ) ] fn from( src : bool ) -> Self { - Self( Some( src ), Default::default() ) + Self( Some( src ), PhantomData::default() ) } } impl< Marker > From< Option< bool > > for AttributePropertyOptionalBoolean< Marker > { #[ inline( always ) ] + #[ allow( clippy::default_constructed_unit_structs ) ] fn from( src : Option< bool > ) -> Self { - Self( src, Default::default() ) + Self( src, PhantomData::default() ) } } diff --git a/module/core/macro_tools/src/attr_prop/singletone.rs b/module/core/macro_tools/src/attr_prop/singletone.rs index 0e6970bdd0..1ee3d86266 100644 --- a/module/core/macro_tools/src/attr_prop/singletone.rs +++ b/module/core/macro_tools/src/attr_prop/singletone.rs @@ -11,6 +11,8 @@ //! //! This is useful for attributes that need to enable or disable features or flags. +use core::marker::PhantomData; +#[ allow( clippy::wildcard_imports ) ] use crate::*; // use former_types::Assign; @@ -35,6 +37,7 @@ impl< Marker > AttributePropertySingletone< Marker > { /// Unwraps and returns the internal optional boolean value. + #[ must_use ] #[ inline( always ) ] pub fn internal( self ) -> bool { @@ -42,6 +45,7 @@ impl< Marker > AttributePropertySingletone< Marker > } /// Returns a reference to the internal optional boolean value. + #[ must_use ] #[ inline( always ) ] pub fn ref_internal( &self ) -> &bool { @@ -72,9 +76,10 @@ where impl< Marker > From< bool > for AttributePropertySingletone< Marker > { #[ inline( always ) ] + #[ allow( clippy::default_constructed_unit_structs ) ] fn from( src : bool ) -> Self { - Self( src, Default::default() ) + Self( src, PhantomData::default() ) } } diff --git a/module/core/macro_tools/src/attr_prop/singletone_optional.rs b/module/core/macro_tools/src/attr_prop/singletone_optional.rs index 7d500cc94f..0761d65233 100644 --- a/module/core/macro_tools/src/attr_prop/singletone_optional.rs +++ b/module/core/macro_tools/src/attr_prop/singletone_optional.rs @@ -12,7 +12,8 @@ //! ``` //! //! This is useful for attributes that need to enable or disable features or flags. - +use core::marker::PhantomData; +#[ allow( clippy::wildcard_imports ) ] use crate::*; // use former_types::Assign; @@ -39,7 +40,10 @@ impl< Marker > AttributePropertyOptionalSingletone< Marker > { /// Return bool value: on/off, use argument as default if it's `None`. + /// # Panics + /// qqq: doc #[ inline ] + #[ must_use ] pub fn value( self, default : bool ) -> bool { if self.0.is_none() @@ -51,12 +55,14 @@ impl< Marker > AttributePropertyOptionalSingletone< Marker > /// Unwraps and returns the internal optional boolean value. #[ inline( always ) ] + #[ must_use ] pub fn internal( self ) -> Option< bool > { self.0 } /// Returns a reference to the internal optional boolean value. + #[ must_use ] #[ inline( always ) ] pub fn ref_internal( &self ) -> Option< &bool > { @@ -73,6 +79,7 @@ where /// Inserts value of another instance into the option if it is None, then returns a mutable reference to the contained value. /// If another instance does is None then do nothing. #[ inline( always ) ] + #[ allow( clippy::single_match ) ] fn assign( &mut self, component : IntoT ) { let component = component.into(); @@ -94,18 +101,20 @@ where impl< Marker > From< bool > for AttributePropertyOptionalSingletone< Marker > { #[ inline( always ) ] + #[ allow( clippy::default_constructed_unit_structs ) ] fn from( src : bool ) -> Self { - Self( Some( src ), Default::default() ) + Self( Some( src ), PhantomData::default() ) } } impl< Marker > From< Option< bool > > for AttributePropertyOptionalSingletone< Marker > { #[ inline( always ) ] + #[ allow( clippy::default_constructed_unit_structs ) ] fn from( src : Option< bool > ) -> Self { - Self( src, Default::default() ) + Self( src, PhantomData::default() ) } } diff --git a/module/core/macro_tools/src/attr_prop/syn.rs b/module/core/macro_tools/src/attr_prop/syn.rs index 183ead1a3a..4427a83f22 100644 --- a/module/core/macro_tools/src/attr_prop/syn.rs +++ b/module/core/macro_tools/src/attr_prop/syn.rs @@ -2,6 +2,8 @@ //! Property of an attribute which simply wraps one of the standard `syn` types. //! +use core::marker::PhantomData; +#[ allow( clippy::wildcard_imports ) ] use crate::*; // use former_types::Assign; @@ -108,8 +110,9 @@ impl< T, Marker > From< T > for AttributePropertySyn< T, Marker > where T : syn::parse::Parse + quote::ToTokens { #[ inline( always ) ] + #[ allow( clippy::default_constructed_unit_structs ) ] fn from( src : T ) -> Self { - Self( src, Default::default() ) + Self( src, PhantomData::default() ) } } diff --git a/module/core/macro_tools/src/attr_prop/syn_optional.rs b/module/core/macro_tools/src/attr_prop/syn_optional.rs index 4e5bba2783..e95e46b779 100644 --- a/module/core/macro_tools/src/attr_prop/syn_optional.rs +++ b/module/core/macro_tools/src/attr_prop/syn_optional.rs @@ -1,7 +1,8 @@ //! //! Property of an attribute which simply wraps one of the standard `syn` types and keeps it optional. //! - +use core::marker::PhantomData; +#[ allow( clippy::wildcard_imports ) ] use crate::*; // use former_types::Assign; @@ -46,6 +47,7 @@ where { /// Inserts value of another instance into the option if it is None, then returns a mutable reference to the contained value. /// If another instance does is None then do nothing. + #[ allow( clippy::single_match ) ] #[ inline( always ) ] fn assign( &mut self, component : IntoT ) { @@ -70,9 +72,10 @@ impl< T, Marker > Default for AttributePropertyOptionalSyn< T, Marker > where T : syn::parse::Parse + quote::ToTokens, { + #[ allow( clippy::default_constructed_unit_structs ) ] fn default() -> Self { - Self( None, Default::default() ) + Self( None, PhantomData::default() ) } } @@ -123,9 +126,10 @@ impl< T, Marker > From< T > for AttributePropertyOptionalSyn< T, Marker > where T : syn::parse::Parse + quote::ToTokens { #[ inline( always ) ] + #[ allow( clippy::default_constructed_unit_structs ) ] fn from( src : T ) -> Self { - Self( Some( src ), Default::default() ) + Self( Some( src ), PhantomData::default() ) } } @@ -133,9 +137,10 @@ impl< T, Marker > From< Option< T > > for AttributePropertyOptionalSyn< T, Marke where T : syn::parse::Parse + quote::ToTokens { #[ inline( always ) ] + #[ allow( clippy::default_constructed_unit_structs ) ] fn from( src : Option< T > ) -> Self { - Self( src, Default::default() ) + Self( src, PhantomData::default() ) } } diff --git a/module/core/macro_tools/src/components.rs b/module/core/macro_tools/src/components.rs index 4e8b2ccf20..7d744f57c1 100644 --- a/module/core/macro_tools/src/components.rs +++ b/module/core/macro_tools/src/components.rs @@ -15,6 +15,7 @@ pub use own::*; #[ allow( unused_imports ) ] pub mod own { + #[ allow( clippy::wildcard_imports ) ] use super::*; #[ doc( inline ) ] pub use orphan::*; @@ -31,6 +32,7 @@ pub mod own #[ allow( unused_imports ) ] pub mod orphan { + #[ allow( clippy::wildcard_imports ) ] use super::*; #[ doc( inline ) ] pub use exposed::*; @@ -40,6 +42,7 @@ pub mod orphan #[ allow( unused_imports ) ] pub mod exposed { + #[ allow( clippy::wildcard_imports ) ] use super::*; pub use super::super::components; diff --git a/module/core/macro_tools/src/container_kind.rs b/module/core/macro_tools/src/container_kind.rs index 6dc2719925..a2e1de1297 100644 --- a/module/core/macro_tools/src/container_kind.rs +++ b/module/core/macro_tools/src/container_kind.rs @@ -5,6 +5,7 @@ /// Define a private namespace for all its items. mod private { + #[ allow( clippy::wildcard_imports ) ] use crate::*; // use crate::type_rightmost; @@ -39,7 +40,9 @@ mod private /// let kind = container_kind::of_type( &tree_type ); /// assert_eq!( kind, container_kind::ContainerKind::HashMap ); /// ``` - + /// # Panics + /// qqq: doc + #[ must_use ] pub fn of_type( ty : &syn::Type ) -> ContainerKind { @@ -61,7 +64,7 @@ mod private ContainerKind::No } - /// Return kind of container specified by type. Unlike [of_type] it also understand optional types. + /// Return kind of container specified by type. Unlike [`of_type`] it also understand optional types. /// /// Good to verify `Option< alloc::vec::Vec< i32 > >` is optional vector. /// @@ -75,13 +78,16 @@ mod private /// assert_eq!( kind, container_kind::ContainerKind::HashMap ); /// assert_eq!( optional, true ); /// ``` + /// # Panics + /// qqq: doc + #[ must_use ] pub fn of_optional( ty : &syn::Type ) -> ( ContainerKind, bool ) { if typ::type_rightmost( ty ) == Some( "Option".to_string() ) { - let ty2 = typ::type_parameters( ty, 0 ..= 0 ).first().copied(); + let ty2 = typ::type_parameters( ty, &( 0 ..= 0 ) ).first().copied(); // inspect_type::inspect_type_of!( ty2 ); if ty2.is_none() { @@ -104,6 +110,7 @@ pub use own::*; #[ allow( unused_imports ) ] pub mod own { + #[ allow( clippy::wildcard_imports ) ] use super::*; #[ doc( inline ) ] pub use orphan::*; @@ -122,6 +129,7 @@ pub mod own #[ allow( unused_imports ) ] pub mod orphan { + #[ allow( clippy::wildcard_imports ) ] use super::*; #[ doc( inline ) ] pub use exposed::*; @@ -131,6 +139,7 @@ pub mod orphan #[ allow( unused_imports ) ] pub mod exposed { + #[ allow( clippy::wildcard_imports ) ] use super::*; pub use super::super::container_kind; diff --git a/module/core/macro_tools/src/ct.rs b/module/core/macro_tools/src/ct.rs index 1264aea393..4083f7321c 100644 --- a/module/core/macro_tools/src/ct.rs +++ b/module/core/macro_tools/src/ct.rs @@ -19,6 +19,7 @@ pub use own::*; #[ allow( unused_imports ) ] pub mod own { + #[ allow( clippy::wildcard_imports ) ] use super::*; #[ doc( inline ) ] @@ -35,6 +36,7 @@ pub mod own #[ allow( unused_imports ) ] pub mod orphan { + #[ allow( clippy::wildcard_imports ) ] use super::*; #[ doc( inline ) ] pub use exposed::*; @@ -44,6 +46,7 @@ pub mod orphan #[ allow( unused_imports ) ] pub mod exposed { + #[ allow( clippy::wildcard_imports ) ] use super::*; pub use super::super::ct; diff --git a/module/core/macro_tools/src/derive.rs b/module/core/macro_tools/src/derive.rs index dacf84f8e7..7dd8888d61 100644 --- a/module/core/macro_tools/src/derive.rs +++ b/module/core/macro_tools/src/derive.rs @@ -5,6 +5,7 @@ /// Define a private namespace for all its items. mod private { + #[ allow( clippy::wildcard_imports ) ] use crate::*; use syn::punctuated::Punctuated; @@ -24,8 +25,10 @@ mod private /// }; /// let fields = derive.named_fields( &ast ); /// ``` + /// # Errors + /// qqq: doc - pub fn named_fields< 'a >( ast : &'a syn::DeriveInput ) -> crate::Result< &'a Punctuated< syn::Field, syn::token::Comma > > + pub fn named_fields( ast : &syn::DeriveInput ) -> crate::Result< &Punctuated< syn::Field, syn::token::Comma > > { let fields = match ast.data @@ -54,6 +57,7 @@ pub use own::*; #[ allow( unused_imports ) ] pub mod own { + #[ allow( clippy::wildcard_imports ) ] use super::*; #[ doc( inline ) ] pub use orphan::*; @@ -70,6 +74,7 @@ pub mod own #[ allow( unused_imports ) ] pub mod orphan { + #[ allow( clippy::wildcard_imports ) ] use super::*; #[ doc( inline ) ] pub use exposed::*; @@ -79,6 +84,7 @@ pub mod orphan #[ allow( unused_imports ) ] pub mod exposed { + #[ allow( clippy::wildcard_imports ) ] use super::*; pub use super::super::derive; @@ -96,6 +102,7 @@ pub mod exposed #[ allow( unused_imports ) ] pub mod prelude { + #[ allow( clippy::wildcard_imports ) ] use super::*; #[ doc( inline ) ] diff --git a/module/core/macro_tools/src/diag.rs b/module/core/macro_tools/src/diag.rs index 81138cd382..40a3afb1bf 100644 --- a/module/core/macro_tools/src/diag.rs +++ b/module/core/macro_tools/src/diag.rs @@ -5,6 +5,7 @@ /// Define a private namespace for all its items. mod private { + #[ allow( clippy::wildcard_imports ) ] use crate::*; /// Adds indentation and optional prefix/postfix to each line of the given string. @@ -62,17 +63,17 @@ mod private { if b.0 > 0 { - a.push_str( "\n" ); + a.push( '\n' ); } a.push_str( prefix ); - a.push_str( &b.1 ); + a.push_str( b.1 ); a.push_str( postfix ); a }); - if src.ends_with( "\n" ) || src.ends_with( "\n\r" ) || src.ends_with( "\r\n" ) + if src.ends_with( '\n' ) || src.ends_with( "\n\r" ) || src.ends_with( "\r\n" ) { - result.push_str( "\n" ); + result.push( '\n' ); result.push_str( prefix ); result.push_str( postfix ); } @@ -128,11 +129,12 @@ mod private /// }; /// /// // Format the debug report for printing or logging - /// let formatted_report = report_format( "Code Transformation for MyStruct", original_input, generated_code ); + /// let formatted_report = report_format( &"Code Transformation for MyStruct", &original_input, generated_code ); /// println!( "{}", formatted_report ); /// ``` /// + #[ allow( clippy::needless_pass_by_value ) ] pub fn report_format< IntoAbout, IntoInput, IntoOutput > ( about : IntoAbout, input : IntoInput, output : IntoOutput @@ -142,7 +144,7 @@ mod private IntoInput : ToString, IntoOutput : ToString, { - format!( "\n" ) + + "\n".to_string() + &format!( " = context\n\n{}\n\n", indentation( " ", about.to_string(), "" ) ) + &format!( " = original\n\n{}\n\n", indentation( " ", input.to_string(), "" ) ) + &format!( " = generated\n\n{}\n", indentation( " ", output.to_string(), "" ) ) @@ -384,6 +386,7 @@ pub use own::*; #[ allow( unused_imports ) ] pub mod own { + #[ allow( clippy::wildcard_imports ) ] use super::*; #[ doc( inline ) ] @@ -395,6 +398,7 @@ pub mod own #[ allow( unused_imports ) ] pub mod orphan { + #[ allow( clippy::wildcard_imports ) ] use super::*; #[ doc( inline ) ] pub use exposed::*; @@ -412,6 +416,7 @@ pub mod orphan #[ allow( unused_imports ) ] pub mod exposed { + #[ allow( clippy::wildcard_imports ) ] use super::*; pub use super::super::diag; @@ -432,6 +437,7 @@ pub mod exposed #[ allow( unused_imports ) ] pub mod prelude { + #[ allow( clippy::wildcard_imports ) ] use super::*; #[ doc( inline ) ] diff --git a/module/core/macro_tools/src/equation.rs b/module/core/macro_tools/src/equation.rs index 3d89aa4ba4..ae7080efdb 100644 --- a/module/core/macro_tools/src/equation.rs +++ b/module/core/macro_tools/src/equation.rs @@ -5,6 +5,7 @@ /// Define a private namespace for all its items. mod private { + #[ allow( clippy::wildcard_imports ) ] use crate::*; /// Represents an equation parsed from a procedural macro input. @@ -85,7 +86,7 @@ mod private /// /// For attribute like `#[former( default = 31 ) ]` return key `default` and value `31`, - /// as well as syn::Meta as the last element of result tuple. + /// as well as `syn::Meta` as the last element of result tuple. /// /// ### Basic use-case. /// @@ -96,19 +97,20 @@ mod private /// let got = equation::from_meta( &attr ).unwrap(); /// assert_eq!( macro_tools::code_to_str!( got ), "default = 31".to_string() ); /// ``` - + /// # Errors + /// qqq: doc pub fn from_meta( attr : &syn::Attribute ) -> Result< Equation > { let meta = &attr.meta; - return match meta + match meta { syn::Meta::List( ref meta_list ) => { let eq : Equation = syn::parse2( meta_list.tokens.clone() )?; Ok( eq ) } - _ => return Err( syn::Error::new( attr.span(), "Unknown format of attribute, expected syn::Meta::List( meta_list )" ) ), - }; + _ => Err( syn::Error::new( attr.span(), "Unknown format of attribute, expected syn::Meta::List( meta_list )" ) ), + } } } @@ -121,6 +123,7 @@ pub use own::*; #[ allow( unused_imports ) ] pub mod own { + #[ allow( clippy::wildcard_imports ) ] use super::*; #[ doc( inline ) ] pub use orphan::*; @@ -135,6 +138,7 @@ pub mod own #[ allow( unused_imports ) ] pub mod orphan { + #[ allow( clippy::wildcard_imports ) ] use super::*; #[ doc( inline ) ] pub use exposed::*; @@ -144,6 +148,7 @@ pub mod orphan #[ allow( unused_imports ) ] pub mod exposed { + #[ allow( clippy::wildcard_imports ) ] use super::*; pub use super::super::equation; diff --git a/module/core/macro_tools/src/generic_args.rs b/module/core/macro_tools/src/generic_args.rs index 1cd40e8f7a..b07b22c5d3 100644 --- a/module/core/macro_tools/src/generic_args.rs +++ b/module/core/macro_tools/src/generic_args.rs @@ -24,6 +24,7 @@ mod private /// # Returns /// A new instance of `syn::AngleBracketedGenericArguments` representing the generic parameters /// of the original type. + #[ allow( clippy::wrong_self_convention ) ] fn into_generic_args( &self ) -> syn::AngleBracketedGenericArguments; } @@ -98,6 +99,7 @@ mod private /// /// This example demonstrates how lifetimes `'a` and `'b` are placed before other generic parameters /// like `T`, `U`, and `V` in the merged result, adhering to the expected syntax order in Rust generics. + #[ must_use ] pub fn merge ( a : &syn::AngleBracketedGenericArguments, @@ -110,7 +112,7 @@ mod private // Function to categorize and collect arguments into lifetimes and others let mut categorize_and_collect = |args : &syn::punctuated::Punctuated| { - for arg in args.iter() + for arg in args { match arg { @@ -148,6 +150,7 @@ pub use own::*; #[ allow( unused_imports ) ] pub mod own { + #[ allow( clippy::wildcard_imports ) ] use super::*; #[ doc( inline ) ] @@ -163,6 +166,7 @@ pub mod own #[ allow( unused_imports ) ] pub mod orphan { + #[ allow( clippy::wildcard_imports ) ] use super::*; #[ doc( inline ) ] pub use exposed::*; diff --git a/module/core/macro_tools/src/generic_params.rs b/module/core/macro_tools/src/generic_params.rs index fac0859aa1..401fd0d326 100644 --- a/module/core/macro_tools/src/generic_params.rs +++ b/module/core/macro_tools/src/generic_params.rs @@ -5,6 +5,7 @@ /// Define a private namespace for all its items. mod private { + #[ allow( clippy::wildcard_imports ) ] use crate::*; use crate::IterTrait; // use iter_tools::IterTrait; @@ -36,12 +37,15 @@ mod private impl GenericsWithWhere { /// Unwraps the `GenericsWithWhere` to retrieve the inner `syn::Generics`. + #[ must_use ] pub fn unwrap( self ) -> syn::Generics { self.generics } /// Parses a string to a `GenericsWithWhere`, specifically designed to handle generics syntax with where clauses effectively. + /// # Errors + /// qqq: doc pub fn parse_from_str( s : &str ) -> syn::Result< GenericsWithWhere > { syn::parse_str::< GenericsWithWhere >( s ) @@ -109,27 +113,29 @@ mod private /// # Examples /// /// - /// # use syn::{Generics, parse_quote}; + /// # use `syn::{Generics`, `parse_quote`}; /// - /// let mut generics_a : syn::Generics = parse_quote!{ < T : Clone, U : Default > }; - /// generics_a.where_clause = parse_quote!{ where T : Default }; - /// let mut generics_b : syn::Generics = parse_quote!{ < V : core::fmt::Debug > }; - /// generics_b.where_clause = parse_quote!{ where V : Sized }; - /// let got = generic_params::merge( &generics_a, &generics_b ); + /// let mut `generics_a` : `syn::Generics` = `parse_quote`!{ < T : Clone, U : Default > }; + /// `generics_a.where_clause` = `parse_quote`!{ where T : Default }; + /// let mut `generics_b` : `syn::Generics` = `parse_quote`!{ < V : `core::fmt::Debug` > }; + /// `generics_b.where_clause` = `parse_quote`!{ where V : Sized }; + /// let got = `generic_params::merge`( &`generics_a`, &`generics_b` ); /// - /// let mut exp : syn::Generics = parse_quote! + /// let mut exp : `syn::Generics` = `parse_quote`! /// { - /// < T : Clone, U : Default, V : core::fmt::Debug > + /// < T : Clone, U : Default, V : `core::fmt::Debug` > /// }; - /// exp.where_clause = parse_quote! + /// `exp.where_clause` = `parse_quote`! /// { /// where /// T : Default, /// V : Sized /// }; /// - /// assert_eq!( got, exp ); + /// `assert_eq`!( got, exp ); + #[ must_use ] + #[ allow( clippy::default_trait_access ) ] pub fn merge( a : &syn::Generics, b : &syn::Generics ) -> syn::Generics { @@ -205,6 +211,8 @@ mod private /// assert!( simplified_generics.where_clause.is_none() ); // Where clause is removed /// ``` + #[ allow( clippy::default_trait_access ) ] + #[ must_use ] pub fn only_names( generics : &syn::Generics ) -> syn::Generics { // use syn::{ Generics, GenericParam, LifetimeDef, TypeParam, ConstParam }; @@ -282,9 +290,9 @@ mod private /// &syn::Ident::new( "N", proc_macro2::Span::call_site() ) /// ]); /// ``` - - pub fn names< 'a >( generics : &'a syn::Generics ) - -> impl IterTrait< 'a, &'a syn::Ident > + #[ must_use ] + pub fn names( generics : &syn::Generics ) + -> impl IterTrait< '_, &syn::Ident > // -> std::iter::Map // < // syn::punctuated::Iter< 'a, syn::GenericParam >, @@ -388,6 +396,8 @@ mod private /// ``` /// + #[ allow( clippy::type_complexity ) ] + #[ must_use ] pub fn decompose ( generics : &syn::Generics, @@ -512,6 +522,7 @@ pub use own::*; /// Own namespace of the module. pub mod own { + #[ allow( clippy::wildcard_imports ) ] use super::*; #[ doc( inline ) ] @@ -530,6 +541,7 @@ pub mod own #[ allow( unused_imports ) ] pub mod orphan { + #[ allow( clippy::wildcard_imports ) ] use super::*; #[ doc( inline ) ] pub use exposed::*; diff --git a/module/core/macro_tools/src/item.rs b/module/core/macro_tools/src/item.rs index d82f484847..45138346e9 100644 --- a/module/core/macro_tools/src/item.rs +++ b/module/core/macro_tools/src/item.rs @@ -6,6 +6,7 @@ /// Define a private namespace for all its items. mod private { + #[ allow( clippy::wildcard_imports ) ] use crate::*; /// Ensures the last field in a struct has a trailing comma. @@ -56,7 +57,7 @@ mod private /// } /// }.to_string() ); /// ``` - + #[ must_use ] pub fn ensure_comma( input : &syn::ItemStruct ) -> syn::ItemStruct { let mut new_input = input.clone(); // Clone the input to modify it @@ -66,16 +67,16 @@ mod private // Handle named fields syn::Fields::Named( syn::FieldsNamed { named, .. } ) => { - punctuated::ensure_trailing_comma( named ) + punctuated::ensure_trailing_comma( named ); }, // Handle unnamed fields (tuples) syn::Fields::Unnamed( syn::FieldsUnnamed { unnamed, .. } ) => { - punctuated::ensure_trailing_comma( unnamed ) + punctuated::ensure_trailing_comma( unnamed ); }, // Do nothing for unit structs syn::Fields::Unit => {} - } + }; new_input } @@ -90,6 +91,7 @@ pub use own::*; #[ allow( unused_imports ) ] pub mod own { + #[ allow( clippy::wildcard_imports ) ] use super::*; #[ doc( inline ) ] pub use orphan::*; @@ -104,6 +106,7 @@ pub mod own #[ allow( unused_imports ) ] pub mod orphan { + #[ allow( clippy::wildcard_imports ) ] use super::*; #[ doc( inline ) ] pub use exposed::*; diff --git a/module/core/macro_tools/src/item_struct.rs b/module/core/macro_tools/src/item_struct.rs index dd8f31f739..09f8f2c7a5 100644 --- a/module/core/macro_tools/src/item_struct.rs +++ b/module/core/macro_tools/src/item_struct.rs @@ -5,13 +5,15 @@ /// Define a private namespace for all its items. mod private { + #[ allow( clippy::wildcard_imports ) ] use crate::*; // use iter_tools::{ IterTrait, BoxedIter }; /// Extracts the types of each field into a vector. - pub fn field_types< 'a >( t : &'a syn::ItemStruct ) + #[ must_use ] + pub fn field_types( t : &syn::ItemStruct ) -> - impl IterTrait< 'a, &'a syn::Type > + impl IterTrait< '_, &syn::Type > // -> std::iter::Map // < // syn::punctuated::Iter< 'a, syn::Field >, @@ -22,7 +24,13 @@ mod private } /// Retrieves the names of each field, if they exist. - pub fn field_names< 'a >( t : &'a syn::ItemStruct ) -> Option< BoxedIter< 'a, &'a syn::Ident > > + /// # Errors + /// qqq: doc + /// # Panics + /// qqq: error + #[ allow( clippy::match_wildcard_for_single_variants ) ] + #[ must_use ] + pub fn field_names( t : &syn::ItemStruct ) -> Option< BoxedIter< '_, &syn::Ident > > { match &t.fields { @@ -35,6 +43,9 @@ mod private /// Retrieves the type of the first field of the struct. /// /// Returns the type if the struct has at least one field, otherwise returns an error. + /// # Errors + /// qqq + #[ allow( clippy::match_wildcard_for_single_variants ) ] pub fn first_field_type( t : &syn::ItemStruct ) -> Result< syn::Type > { let maybe_field = match t.fields @@ -49,13 +60,16 @@ mod private return Ok( field.ty.clone() ) } - return Err( syn_err!( t.span(), "Expects at least one field" ) ); + Err( syn_err!( t.span(), "Expects at least one field" ) ) } /// Retrieves the name of the first field of the struct, if available. /// /// Returns `Some` with the field identifier for named fields, or `None` for unnamed fields. /// Returns an error if the struct has no fields + /// # Errors + /// qqq: doc + #[ allow( clippy::match_wildcard_for_single_variants ) ] pub fn first_field_name( t : &syn::ItemStruct ) -> Result< Option< syn::Ident > > { let maybe_field = match t.fields @@ -70,7 +84,7 @@ mod private return Ok( field.ident.clone() ) } - return Err( syn_err!( t.span(), "Expects type for fields" ) ); + Err( syn_err!( t.span(), "Expects type for fields" ) ) } @@ -84,6 +98,7 @@ pub use own::*; #[ allow( unused_imports ) ] pub mod own { + #[ allow( clippy::wildcard_imports ) ] use super::*; #[ doc( inline ) ] pub use orphan::*; @@ -101,6 +116,7 @@ pub mod own #[ allow( unused_imports ) ] pub mod orphan { + #[ allow( clippy::wildcard_imports ) ] use super::*; #[ doc( inline ) ] pub use exposed::*; @@ -110,6 +126,7 @@ pub mod orphan #[ allow( unused_imports ) ] pub mod exposed { + #[ allow( clippy::wildcard_imports ) ] use super::*; pub use super::super::item_struct; diff --git a/module/core/macro_tools/src/iter.rs b/module/core/macro_tools/src/iter.rs index 71bf438658..587750de8a 100644 --- a/module/core/macro_tools/src/iter.rs +++ b/module/core/macro_tools/src/iter.rs @@ -15,6 +15,7 @@ pub use own::*; #[ allow( unused_imports ) ] pub mod own { + #[ allow( clippy::wildcard_imports ) ] use super::*; #[ doc( inline ) ] pub use orphan::*; @@ -27,6 +28,7 @@ pub mod own #[ allow( unused_imports ) ] pub mod orphan { + #[ allow( clippy::wildcard_imports ) ] use super::*; #[ doc( inline ) ] pub use exposed::*; @@ -36,6 +38,7 @@ pub mod orphan #[ allow( unused_imports ) ] pub mod exposed { + #[ allow( clippy::wildcard_imports ) ] use super::*; // pub use super::super::iter; diff --git a/module/core/macro_tools/src/kw.rs b/module/core/macro_tools/src/kw.rs index 4a29a6879d..9ae3e2abf2 100644 --- a/module/core/macro_tools/src/kw.rs +++ b/module/core/macro_tools/src/kw.rs @@ -17,6 +17,7 @@ mod private // qqq : cover by test /// Check is string a keyword. + #[ must_use ] pub fn is( src : &str ) -> bool { KEYWORDS.contains( &src ) @@ -32,6 +33,7 @@ pub use own::*; #[ allow( unused_imports ) ] pub mod own { + #[ allow( clippy::wildcard_imports ) ] use super::*; #[ doc( inline ) ] pub use orphan::*; @@ -41,6 +43,7 @@ pub mod own #[ allow( unused_imports ) ] pub mod orphan { + #[ allow( clippy::wildcard_imports ) ] use super::*; #[ doc( inline ) ] pub use exposed::*; @@ -50,6 +53,7 @@ pub mod orphan #[ allow( unused_imports ) ] pub mod exposed { + #[ allow( clippy::wildcard_imports ) ] use super::*; pub use super::super::kw; diff --git a/module/core/macro_tools/src/lib.rs b/module/core/macro_tools/src/lib.rs index 8f20b5d77b..c3a663cf22 100644 --- a/module/core/macro_tools/src/lib.rs +++ b/module/core/macro_tools/src/lib.rs @@ -8,13 +8,14 @@ #[ cfg( feature = "enabled" ) ] mod private { + #[ allow( clippy::wildcard_imports ) ] use crate::*; /// - /// Result with syn::Error. + /// Result with `syn::Error`. /// - pub type Result< T > = std::result::Result< T, syn::Error >; + pub type Result< T > = core::result::Result< T, syn::Error >; } @@ -63,7 +64,7 @@ pub mod typ; #[ cfg( all( feature = "enabled", feature = "typed" ) ) ] pub mod typed; -#[ cfg( all( feature = "enabled" ) ) ] +#[ cfg( feature = "enabled" ) ] pub mod iter; /// @@ -98,6 +99,7 @@ pub mod own mod _all { + #[ allow( clippy::wildcard_imports ) ] use super::super::*; pub use orphan::*; @@ -167,6 +169,7 @@ pub mod orphan mod _all { + #[ allow( clippy::wildcard_imports ) ] use super::super::*; pub use exposed::*; } @@ -185,6 +188,7 @@ pub mod exposed mod _all { + #[ allow( clippy::wildcard_imports ) ] use super::super::*; pub use prelude::*; @@ -249,6 +253,7 @@ pub mod prelude mod _all { + #[ allow( clippy::wildcard_imports ) ] use super::super::*; // pub use prelude::*; diff --git a/module/core/macro_tools/src/name.rs b/module/core/macro_tools/src/name.rs index a5185ea8c4..ae70cba3f8 100644 --- a/module/core/macro_tools/src/name.rs +++ b/module/core/macro_tools/src/name.rs @@ -39,7 +39,7 @@ mod private syn::Item::Union( item ) => item.name(), // syn::Item::Use( item ) => item.name(), // syn::Item::Verbatim( item ) => item.name(), - _ => "".into(), + _ => String::new(), } } } @@ -51,7 +51,7 @@ mod private let first = self.segments.first(); if first.is_none() { - return "".into() + return String::new() } let first = first.unwrap(); first.ident.to_string() @@ -104,7 +104,7 @@ mod private { if self.trait_.is_none() { - return "".into() + return String::new() } let t = self.trait_.as_ref().unwrap(); t.1.name() @@ -117,7 +117,7 @@ mod private { if self.ident.is_none() { - return "".to_string() + return String::new() } let ident = self.ident.as_ref().unwrap(); ident.to_string() @@ -232,6 +232,7 @@ pub use own::*; #[ allow( unused_imports ) ] pub mod own { + #[ allow( clippy::wildcard_imports ) ] use super::*; #[ doc( inline ) ] pub use orphan::*; @@ -241,6 +242,7 @@ pub mod own #[ allow( unused_imports ) ] pub mod orphan { + #[ allow( clippy::wildcard_imports ) ] use super::*; #[ doc( inline ) ] pub use exposed::*; @@ -250,6 +252,7 @@ pub mod orphan #[ allow( unused_imports ) ] pub mod exposed { + #[ allow( clippy::wildcard_imports ) ] use super::*; pub use super::super::name; @@ -263,6 +266,7 @@ pub mod exposed #[ allow( unused_imports ) ] pub mod prelude { + #[ allow( clippy::wildcard_imports ) ] use super::*; #[ doc( inline ) ] #[ allow( unused_imports ) ] diff --git a/module/core/macro_tools/src/phantom.rs b/module/core/macro_tools/src/phantom.rs index 507a7fe0d1..4f52f3d5fa 100644 --- a/module/core/macro_tools/src/phantom.rs +++ b/module/core/macro_tools/src/phantom.rs @@ -7,6 +7,7 @@ /// Define a private namespace for all its items. mod private { + #[ allow( clippy::wildcard_imports ) ] use crate::*; /// Adds a `PhantomData` field to a struct to manage generic parameter usage. @@ -42,7 +43,8 @@ mod private /// // Output will include a _phantom field of type `PhantomData< ( T, U ) >` /// ``` /// - + #[ allow( clippy::default_trait_access, clippy::semicolon_if_nothing_returned ) ] + #[ must_use ] pub fn add_to_item( input : &syn::ItemStruct ) -> syn::ItemStruct { @@ -136,6 +138,8 @@ mod private /// // Output : ::core::marker::PhantomData< ( &'a (), *const T, N ) > /// ``` /// + #[ must_use ] + #[ allow( clippy::default_trait_access ) ] pub fn tuple( input : &syn::punctuated::Punctuated< syn::GenericParam, syn::token::Comma > ) -> syn::Type { use proc_macro2::Span; @@ -198,6 +202,7 @@ pub use own::*; /// Own namespace of the module. pub mod own { + #[ allow( clippy::wildcard_imports ) ] use super::*; #[ doc( inline ) ] @@ -214,6 +219,7 @@ pub mod own #[ allow( unused_imports ) ] pub mod orphan { + #[ allow( clippy::wildcard_imports ) ] use super::*; #[ doc( inline ) ] pub use exposed::*; diff --git a/module/core/macro_tools/src/punctuated.rs b/module/core/macro_tools/src/punctuated.rs index 893e8459fb..a2c3fa0c8a 100644 --- a/module/core/macro_tools/src/punctuated.rs +++ b/module/core/macro_tools/src/punctuated.rs @@ -28,6 +28,7 @@ pub use own::*; /// Own namespace of the module. pub mod own { + #[ allow( clippy::wildcard_imports ) ] use super::*; #[ doc( inline ) ] @@ -43,6 +44,7 @@ pub mod own #[ allow( unused_imports ) ] pub mod orphan { + #[ allow( clippy::wildcard_imports ) ] use super::*; #[ doc( inline ) ] pub use exposed::*; diff --git a/module/core/macro_tools/src/quantifier.rs b/module/core/macro_tools/src/quantifier.rs index a1c3cb7833..030074314a 100644 --- a/module/core/macro_tools/src/quantifier.rs +++ b/module/core/macro_tools/src/quantifier.rs @@ -5,6 +5,7 @@ /// Define a private namespace for all its items. mod private { + #[ allow( clippy::wildcard_imports ) ] use crate::*; /// @@ -105,11 +106,13 @@ mod private T : Element, { /// Constructor. + #[ must_use ] pub fn new() -> Self { Self( Vec::new() ) } /// Constructor. + #[ must_use ] pub fn new_with( src : Vec< T > ) -> Self { Self( src ) @@ -148,6 +151,7 @@ mod private T : quote::ToTokens, { type Item = T; + #[ allow( clippy::std_instead_of_alloc ) ] type IntoIter = std::vec::IntoIter< Self::Item >; fn into_iter( self ) -> Self::IntoIter { @@ -253,6 +257,7 @@ pub use own::*; #[ allow( unused_imports ) ] pub mod own { + #[ allow( clippy::wildcard_imports ) ] use super::*; #[ doc( inline ) ] pub use orphan::*; @@ -262,6 +267,7 @@ pub mod own #[ allow( unused_imports ) ] pub mod orphan { + #[ allow( clippy::wildcard_imports ) ] use super::*; #[ doc( inline ) ] pub use exposed::*; @@ -271,6 +277,7 @@ pub mod orphan #[ allow( unused_imports ) ] pub mod exposed { + #[ allow( clippy::wildcard_imports ) ] use super::*; pub use super::super::quantifier; @@ -291,6 +298,7 @@ pub mod exposed #[ allow( unused_imports ) ] pub mod prelude { + #[ allow( clippy::wildcard_imports ) ] use super::*; #[ doc( inline ) ] pub use private:: diff --git a/module/core/macro_tools/src/struct_like.rs b/module/core/macro_tools/src/struct_like.rs index 5b9652b506..321fbffee3 100644 --- a/module/core/macro_tools/src/struct_like.rs +++ b/module/core/macro_tools/src/struct_like.rs @@ -5,6 +5,7 @@ /// Define a private namespace for all its items. mod private { + #[ allow( clippy::wildcard_imports ) ] use crate::*; /// Enum to encapsulate either a field from a struct or a variant from an enum. @@ -59,6 +60,7 @@ mod private { /// Returns a reference to the attributes of the item. + #[ must_use ] pub fn attrs( &self ) -> &Vec< syn::Attribute > { match self @@ -69,6 +71,7 @@ mod private } /// Returns a reference to the visibility of the item. + #[ must_use ] pub fn vis( &self ) -> Option< &syn::Visibility > { match self @@ -79,6 +82,7 @@ mod private } /// Returns a reference to the mutability of the item. + #[ must_use ] pub fn mutability( &self ) -> Option< &syn::FieldMutability > { match self @@ -89,6 +93,7 @@ mod private } /// Returns a reference to the identifier of the item. + #[ must_use] pub fn ident( &self ) -> Option< &syn::Ident > { match self @@ -99,6 +104,7 @@ mod private } /// Returns an iterator over elements of the item. + #[ must_use ] pub fn typ( &self ) -> Option< &syn::Type > { match self @@ -115,6 +121,7 @@ mod private } /// Returns a reference to the fields of the item. + #[ must_use ] pub fn fields( &self ) -> Option< &syn::Fields > { match self @@ -125,6 +132,7 @@ mod private } /// Returns a reference to the discriminant of the item. + #[ must_use ] pub fn discriminant( &self ) -> Option< &( syn::token::Eq, syn::Expr ) > { match self @@ -202,7 +210,7 @@ mod private // Parse ItemStruct let mut item_struct : ItemStruct = input.parse()?; item_struct.vis = visibility; - item_struct.attrs = attributes.into(); + item_struct.attrs = attributes; if item_struct.fields.is_empty() { Ok( StructLike::Unit( item_struct ) ) @@ -217,7 +225,7 @@ mod private // Parse ItemEnum let mut item_enum : ItemEnum = input.parse()?; item_enum.vis = visibility; - item_enum.attrs = attributes.into(); + item_enum.attrs = attributes; Ok( StructLike::Enum( item_enum ) ) } else @@ -274,14 +282,12 @@ mod private } /// Returns an iterator over elements of the item. + #[ must_use ] pub fn attrs( &self ) -> &Vec< syn::Attribute > { match self { - StructLike::Unit( item ) => - { - &item.attrs - }, + StructLike::Unit( item ) | StructLike::Struct( item ) => { &item.attrs @@ -294,14 +300,12 @@ mod private } /// Returns an iterator over elements of the item. + #[ must_use ] pub fn vis( &self ) -> &syn::Visibility { match self { - StructLike::Unit( item ) => - { - &item.vis - }, + StructLike::Unit( item ) | StructLike::Struct( item ) => { &item.vis @@ -314,14 +318,12 @@ mod private } /// Returns an iterator over elements of the item. + #[ must_use ] pub fn ident( &self ) -> &syn::Ident { match self { - StructLike::Unit( item ) => - { - &item.ident - }, + StructLike::Unit( item ) | StructLike::Struct( item ) => { &item.ident @@ -334,14 +336,12 @@ mod private } /// Returns an iterator over elements of the item. + #[ must_use ] pub fn generics( &self ) -> &syn::Generics { match self { - StructLike::Unit( item ) => - { - &item.generics - }, + StructLike::Unit( item ) | StructLike::Struct( item ) => { &item.generics @@ -355,13 +355,14 @@ mod private /// Returns an iterator over fields of the item. // pub fn fields< 'a >( &'a self ) -> impl IterTrait< 'a, &'a syn::Field > + #[ must_use ] pub fn fields< 'a >( &'a self ) -> BoxedIter< 'a, &'a syn::Field > { let result : BoxedIter< 'a, &'a syn::Field > = match self { StructLike::Unit( _item ) => { - Box::new( std::iter::empty() ) + Box::new( core::iter::empty() ) }, StructLike::Struct( item ) => { @@ -369,22 +370,22 @@ mod private }, StructLike::Enum( _item ) => { - Box::new( std::iter::empty() ) + Box::new( core::iter::empty() ) }, }; result } /// Extracts the name of each field. + /// # Panics + /// qqq: docs // pub fn field_names< 'a >( &'a self ) -> Option< impl IterTrait< 'a, &'a syn::Ident > + '_ > - pub fn field_names< 'a >( &'a self ) -> Option< BoxedIter< 'a, &'a syn::Ident >> + #[ must_use ] + pub fn field_names( &self ) -> Option< BoxedIter< '_, &syn::Ident >> { match self { - StructLike::Unit( item ) => - { - item_struct::field_names( item ) - }, + StructLike::Unit( item ) | StructLike::Struct( item ) => { item_struct::field_names( item ) @@ -398,8 +399,9 @@ mod private } /// Extracts the type of each field. - pub fn field_types<'a>( &'a self ) - -> BoxedIter< 'a, &'a syn::Type > + #[ must_use ] + pub fn field_types( & self ) + -> BoxedIter< '_, & syn::Type > // -> std::iter::Map // < // std::boxed::Box< dyn _IterTrait< '_, &syn::Field > + 'a >, @@ -411,8 +413,9 @@ mod private /// Extracts the name of each field. // pub fn field_attrs< 'a >( &'a self ) -> impl IterTrait< 'a, &'a Vec< syn::Attribute > > - pub fn field_attrs<'a>( &'a self ) - -> BoxedIter< 'a, &'a Vec< syn::Attribute > > + #[ must_use ] + pub fn field_attrs( & self ) + -> BoxedIter< '_, &Vec< syn::Attribute > > // -> std::iter::Map // < // std::boxed::Box< dyn _IterTrait< '_, &syn::Field > + 'a >, @@ -423,6 +426,7 @@ mod private } /// Extract the first field. + #[ must_use ] pub fn first_field( &self ) -> Option< &syn::Field > { self.fields().next() @@ -443,6 +447,7 @@ pub use own::*; #[ allow( unused_imports ) ] pub mod own { + #[ allow( clippy::wildcard_imports ) ] use super::*; #[ doc( inline ) ] pub use orphan::*; @@ -458,6 +463,7 @@ pub mod own #[ allow( unused_imports ) ] pub mod orphan { + #[ allow( clippy::wildcard_imports ) ] use super::*; #[ doc( inline ) ] pub use exposed::*; @@ -467,6 +473,7 @@ pub mod orphan #[ allow( unused_imports ) ] pub mod exposed { + #[ allow( clippy::wildcard_imports ) ] use super::*; pub use super::super::struct_like; diff --git a/module/core/macro_tools/src/tokens.rs b/module/core/macro_tools/src/tokens.rs index 4785c8dfc8..cfb52da63f 100644 --- a/module/core/macro_tools/src/tokens.rs +++ b/module/core/macro_tools/src/tokens.rs @@ -5,6 +5,7 @@ /// Define a private namespace for all its items. mod private { + #[ allow( clippy::wildcard_imports ) ] use crate::*; use core::fmt; @@ -32,6 +33,7 @@ mod private impl Tokens { /// Constructor from `proc_macro2::TokenStream`. + #[ must_use ] pub fn new( inner : proc_macro2::TokenStream ) -> Self { Tokens { inner } @@ -59,7 +61,7 @@ mod private { fn fmt( &self, f : &mut fmt::Formatter< '_ > ) -> fmt::Result { - write!( f, "{}", self.inner.to_string() ) + write!( f, "{}", self.inner ) } } @@ -67,7 +69,7 @@ mod private { fn fmt( &self, f : &mut core::fmt::Formatter< '_ > ) -> core::fmt::Result { - write!( f, "{}", self.inner.to_string() ) + write!( f, "{}", self.inner ) } } @@ -81,6 +83,7 @@ pub use own::*; #[ allow( unused_imports ) ] pub mod own { + #[ allow( clippy::wildcard_imports ) ] use super::*; #[ doc( inline ) ] pub use orphan::*; @@ -90,6 +93,7 @@ pub mod own #[ allow( unused_imports ) ] pub mod orphan { + #[ allow( clippy::wildcard_imports ) ] use super::*; #[ doc( inline ) ] pub use exposed::*; @@ -99,6 +103,7 @@ pub mod orphan #[ allow( unused_imports ) ] pub mod exposed { + #[ allow( clippy::wildcard_imports ) ] use super::*; pub use super::super::tokens; diff --git a/module/core/macro_tools/src/typ.rs b/module/core/macro_tools/src/typ.rs index 1c1f0e39e5..3a2e710808 100644 --- a/module/core/macro_tools/src/typ.rs +++ b/module/core/macro_tools/src/typ.rs @@ -5,6 +5,7 @@ /// Define a private namespace for all its items. mod private { + #[ allow( clippy::wildcard_imports ) ] use crate::*; use interval_adapter::BoundExt; @@ -22,7 +23,9 @@ mod private /// let got = typ::type_rightmost( &tree_type ); /// assert_eq!( got, Some( "Option".to_string() ) ); /// ``` - + /// # Panics + /// qqq: doc + #[ must_use ] pub fn type_rightmost( ty : &syn::Type ) -> Option< String > { if let syn::Type::Path( path ) = ty @@ -53,8 +56,10 @@ mod private /// // < i16 /// // < i32 /// ``` - - pub fn type_parameters( ty : &syn::Type, range : impl NonIterableInterval ) -> Vec< &syn::Type > + /// # Panics + /// qqq: doc + #[ allow( clippy::cast_possible_wrap ) ] + pub fn type_parameters< 'a >( ty : &'a syn::Type, range : &'a impl NonIterableInterval ) -> Vec< &'a syn::Type > { if let syn::Type::Path( syn::TypePath{ path : syn::Path { ref segments, .. }, .. } ) = ty { @@ -104,7 +109,7 @@ mod private /// assert!( macro_tools::typ::is_optional( &parsed_type ) ); /// ``` /// - + #[ must_use ] pub fn is_optional( ty : &syn::Type ) -> bool { typ::type_rightmost( ty ) == Some( "Option".to_string() ) @@ -124,10 +129,11 @@ mod private /// let first_param = macro_tools::typ::parameter_first( &parsed_type ).expect( "Should have at least one parameter" ); /// // Option< i32 > /// ``` - + /// # Errors + /// qqq: docs pub fn parameter_first( ty : &syn::Type ) -> Result< &syn::Type > { - typ::type_parameters( ty, 0 ..= 0 ) + typ::type_parameters( ty, &( 0 ..= 0 ) ) .first() .copied() .ok_or_else( || syn_err!( ty, "Expects at least one parameter here:\n {}", qt!{ #ty } ) ) @@ -143,6 +149,7 @@ pub use own::*; #[ allow( unused_imports ) ] pub mod own { + #[ allow( clippy::wildcard_imports ) ] use super::*; #[ doc( inline ) ] pub use orphan::*; @@ -160,6 +167,7 @@ pub mod own #[ allow( unused_imports ) ] pub mod orphan { + #[ allow( clippy::wildcard_imports ) ] use super::*; #[ doc( inline ) ] pub use exposed::*; @@ -169,6 +177,7 @@ pub mod orphan #[ allow( unused_imports ) ] pub mod exposed { + #[ allow( clippy::wildcard_imports ) ] use super::*; pub use super::super::typ; diff --git a/module/core/macro_tools/src/typed.rs b/module/core/macro_tools/src/typed.rs index 5e75476fb1..c5d2d05c3c 100644 --- a/module/core/macro_tools/src/typed.rs +++ b/module/core/macro_tools/src/typed.rs @@ -17,6 +17,7 @@ pub use own::*; #[ allow( unused_imports ) ] pub mod own { + #[ allow( clippy::wildcard_imports ) ] use super::*; #[ doc( inline ) ] @@ -35,6 +36,7 @@ pub mod own #[ allow( unused_imports ) ] pub mod orphan { + #[ allow( clippy::wildcard_imports ) ] use super::*; #[ doc( inline ) ] pub use exposed::*; @@ -44,6 +46,7 @@ pub mod orphan #[ allow( unused_imports ) ] pub mod exposed { + #[ allow( clippy::wildcard_imports ) ] use super::*; pub use super::super::typed; diff --git a/module/core/mod_interface/Readme.md b/module/core/mod_interface/Readme.md index 0bb7e9b255..53e887fdd3 100644 --- a/module/core/mod_interface/Readme.md +++ b/module/core/mod_interface/Readme.md @@ -1,6 +1,6 @@ -# Module :: mod_interface +# Module :: `mod_interface` [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) [![rust-status](https://github.com/Wandalen/wTools/actions/workflows/module_mod_interface_push.yml/badge.svg)](https://github.com/Wandalen/wTools/actions/workflows/module_mod_interface_push.yml) [![docs.rs](https://img.shields.io/docsrs/mod_interface?color=e3e8f0&logo=docs.rs)](https://docs.rs/mod_interface) [![discord](https://img.shields.io/discord/872391416519737405?color=eee&logo=discord&logoColor=eee&label=ask)](https://discord.gg/m3YfbXpUUY) @@ -92,7 +92,8 @@ crate::mod_interface! use super::child; } -fn main() + +// fn main() { assert!( child::prelude_thing(), "prelude thing of child is there" ); @@ -195,7 +196,7 @@ pub mod own { use super::*; pub use orphan::*; - pub use child::orphan::*; + pub use super::child::orphan::*; pub use super::child; } @@ -226,7 +227,7 @@ pub mod prelude // -fn main() +// fn main() { assert!( child::prelude_thing(), "prelude thing of child is there" ); @@ -263,41 +264,6 @@ fn main() -As you can see: - -- The content of the `prelude` chapter is automatically propagated into the `exposed` chapter of the same layer. -- The content of the `exposed` chapter is automatically propagated into the `orphan` chapter of the same layer. -- The content of the `orphan` chapter is automatically propagated into the `own` chapter of the same layer. -- The content of the `own` chapter is not automatically propagated anywhere. - -### `layer ` vs `use ` - -The `use ;` syntax is used to import a layer from anywhere within the project or even from external crates. This provides flexibility in how layers are organized, as the layer being used does not need to be a direct submodule of the current module. It allows you to bring any accessible layer into the current scope without enforcing a specific file structure. The visibility of the imported layer remains as it is defined in its original location, and this syntax does not inherently change that visibility. - -In contrast, the `layer ` syntax is used to establish a hierarchical relationship where the current module uses a child layer. This requires the child layer to be a direct submodule, meaning it must be physically present in the file structure as a submodule. The `layer ` syntax implies `pub mod layer1`, making the child layer publicly accessible as a submodule. This enforces a specific organizational structure, where the child layer is part of the current module's hierarchy, and its contents are directly accessible according to the defined propagation strategies. - -Thus, `layer ` acts as a shortcut, combining the definition of a reference to a module file and using it, while `use ` uses a module that is already defined somewhere, not necessarily in the same crate. - -### `reuse ` vs `use ` - -The `reuse ` syntax treats the child layer as an integral part of the parent layer, so the normal rules of propagation do not apply to the content of the child layer. Specifically, the `own` chapter of the child layer is imported into the `own` chapter of the parent layer, and the `orphan` chapter of the child layer is imported into the `orphan` chapter of the parent layer. - -In contrast, `use ` follows the standard propagation rules: - -- `child::own` is not propagated. -- `child::orphan` is imported into `parent::own`. -- `child::exposed` is imported into `parent::exposed`. -- `child::prelude` is imported into `parent::prelude`. - -For `reuse `, the propagation is as follows: - -- `child::own` is imported into `parent::own`. -- `child::orphan` is imported into `parent::orphan`. -- `child::exposed` is imported into `parent::exposed`. -- `child::prelude` is imported into `parent::prelude`. - -`reusing` does not impact parent of parent or child of child. - ### Debugging To debug module interface use directive `#![ debug ]` in macro `mod_interface`. Let's update the main file of the example : diff --git a/module/core/mod_interface/src/lib.rs b/module/core/mod_interface/src/lib.rs index 726b166188..07820b8983 100644 --- a/module/core/mod_interface/src/lib.rs +++ b/module/core/mod_interface/src/lib.rs @@ -22,6 +22,7 @@ pub use own::*; #[ allow( unused_imports ) ] pub mod own { + #[ allow( clippy::wildcard_imports ) ] use super::*; #[ doc( inline ) ] pub use orphan::*; @@ -37,6 +38,7 @@ pub mod own #[ allow( unused_imports ) ] pub mod orphan { + #[ allow( clippy::wildcard_imports ) ] use super::*; #[ doc( inline ) ] pub use exposed::*; @@ -47,6 +49,7 @@ pub mod orphan #[ allow( unused_imports ) ] pub mod exposed { + #[ allow( clippy::wildcard_imports ) ] use super::*; #[ doc( inline ) ] pub use prelude::*; diff --git a/module/core/mod_interface_meta/Readme.md b/module/core/mod_interface_meta/Readme.md index d9b2a9bd8b..d953c21eca 100644 --- a/module/core/mod_interface_meta/Readme.md +++ b/module/core/mod_interface_meta/Readme.md @@ -1,11 +1,11 @@ -# Module :: mod_interface_meta +# Module :: `mod_interface_meta` [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) [![rust-status](https://github.com/Wandalen/wTools/actions/workflows/module_mod_interface_meta_push.yml/badge.svg)](https://github.com/Wandalen/wTools/actions/workflows/module_mod_interface_meta_push.yml) [![docs.rs](https://img.shields.io/docsrs/mod_interface_meta?color=e3e8f0&logo=docs.rs)](https://docs.rs/mod_interface_meta) [![discord](https://img.shields.io/discord/872391416519737405?color=eee&logo=discord&logoColor=eee&label=ask)](https://discord.gg/m3YfbXpUUY) Protocol of modularity unifying interface of a module and introducing layers. -Not intended to be used without runtime. This module and runtime is aggregate in module::mod_interface is [here](https://github.com/Wandalen/wTools/tree/master/module/core/mod_interface). -module and runtime is aggregate in module::mod_interface is [here](https://github.com/Wandalen/wTools/tree/master/module/core/mod_interface). +Not intended to be used without runtime. This module and runtime is aggregate in `module::mod_interface` is [here](https://github.com/Wandalen/wTools/tree/master/module/core/mod_interface). +module and runtime is aggregate in `module::mod_interface` is [here](https://github.com/Wandalen/wTools/tree/master/module/core/mod_interface). diff --git a/module/core/mod_interface_meta/src/impls.rs b/module/core/mod_interface_meta/src/impls.rs index 81c6cec066..cdac53e906 100644 --- a/module/core/mod_interface_meta/src/impls.rs +++ b/module/core/mod_interface_meta/src/impls.rs @@ -1,7 +1,9 @@ /// Define a private namespace for all its items. mod private { + #[ allow( clippy::wildcard_imports ) ] use crate::*; + #[ allow( clippy::wildcard_imports ) ] use macro_tools::exposed::*; use std::collections::HashMap; @@ -409,9 +411,10 @@ mod private /// /// Protocol of modularity unifying interface of a module and introducing layers. /// - #[ allow ( dead_code ) ] + #[ allow ( dead_code, clippy::too_many_lines ) ] pub fn mod_interface( input : proc_macro::TokenStream ) -> syn::Result< proc_macro2::TokenStream > { + #[ allow( clippy::enum_glob_use ) ] use ElementType::*; let original_input = input.clone(); @@ -583,7 +586,7 @@ mod private if has_debug { - let about = format!( "derive : mod_interface" ); + let about = "derive : mod_interface"; diag::report_print( about, &original_input, &result ); } @@ -601,6 +604,7 @@ mod private #[ allow( unused_imports ) ] pub mod own { + #[ allow( clippy::wildcard_imports ) ] use super::*; pub use orphan::*; } @@ -611,6 +615,7 @@ pub use own::*; #[ allow( unused_imports ) ] pub mod orphan { + #[ allow( clippy::wildcard_imports ) ] use super::*; pub use exposed::*; } @@ -619,6 +624,7 @@ pub mod orphan #[ allow( unused_imports ) ] pub mod exposed { + #[ allow( clippy::wildcard_imports ) ] use super::*; pub use prelude::*; pub use private:: @@ -630,6 +636,7 @@ pub mod exposed #[ allow( unused_imports ) ] pub mod prelude { + #[ allow( clippy::wildcard_imports ) ] use super::*; pub use private:: { diff --git a/module/core/mod_interface_meta/src/lib.rs b/module/core/mod_interface_meta/src/lib.rs index 70dc68878e..f077e63508 100644 --- a/module/core/mod_interface_meta/src/lib.rs +++ b/module/core/mod_interface_meta/src/lib.rs @@ -93,10 +93,13 @@ mod impls; #[ allow( unused_imports ) ] use impls::exposed::*; mod record; +#[ allow( clippy::wildcard_imports ) ] use record::exposed::*; mod visibility; +#[ allow( clippy::wildcard_imports ) ] use visibility::exposed::*; mod use_tree; +#[ allow( clippy::wildcard_imports ) ] use use_tree::exposed::*; /// diff --git a/module/core/mod_interface_meta/src/record.rs b/module/core/mod_interface_meta/src/record.rs index aeb6a696eb..24b773be10 100644 --- a/module/core/mod_interface_meta/src/record.rs +++ b/module/core/mod_interface_meta/src/record.rs @@ -1,7 +1,9 @@ /// Define a private namespace for all its items. mod private { + #[ allow( clippy::wildcard_imports ) ] use crate::*; + #[ allow( clippy::wildcard_imports ) ] use macro_tools::exposed::*; /// @@ -69,6 +71,7 @@ mod private { fn to_tokens( &self, tokens : &mut proc_macro2::TokenStream ) { + #[ allow( clippy::enum_glob_use ) ] use ElementType::*; match self { @@ -128,7 +131,7 @@ mod private { let ident = input.parse()?; elements = syn::punctuated::Punctuated::new(); - elements.push( Pair::new( Default::default(), ident ) ); + elements.push( Pair::new( AttributesOuter::default(), ident ) ); } }, } @@ -201,8 +204,7 @@ mod private // code_print!( attr.path() ); // code_print!( attr.meta ); - let good = true - && code_to_str!( attr.path() ) == "debug" + let good = code_to_str!( attr.path() ) == "debug" // && code_to_str!( attr.meta ).is_empty() ; @@ -268,6 +270,7 @@ pub use own::*; #[ allow( unused_imports ) ] pub mod own { + #[ allow( clippy::wildcard_imports ) ] use super::*; pub use orphan::*; } @@ -276,6 +279,7 @@ pub mod own #[ allow( unused_imports ) ] pub mod orphan { + #[ allow( clippy::wildcard_imports ) ] use super::*; pub use exposed::*; } @@ -284,6 +288,7 @@ pub mod orphan #[ allow( unused_imports ) ] pub mod exposed { + #[ allow( clippy::wildcard_imports ) ] use super::*; pub use prelude::*; pub use private:: @@ -299,6 +304,7 @@ pub mod exposed #[ allow( unused_imports ) ] pub mod prelude { + #[ allow( clippy::wildcard_imports ) ] use super::*; pub use private:: { diff --git a/module/core/mod_interface_meta/src/use_tree.rs b/module/core/mod_interface_meta/src/use_tree.rs index 3f51fccc22..48e1e5bb81 100644 --- a/module/core/mod_interface_meta/src/use_tree.rs +++ b/module/core/mod_interface_meta/src/use_tree.rs @@ -26,6 +26,7 @@ mod private /// Add `super::private::` to path unless it starts from `::` or `super` or `crate`. pub fn private_prefix_is_needed( &self ) -> bool { + #[ allow( clippy::wildcard_imports, clippy::enum_glob_use ) ] use syn::UseTree::*; // println!( "private_prefix_is_needed : {:?}", self ); @@ -46,6 +47,7 @@ mod private /// Get pure path, cutting off `as module2` from `use module1 as module2`. pub fn pure_path( &self ) -> syn::Result< syn::punctuated::Punctuated< syn::Ident, Token![::] > > { + #[ allow( clippy::wildcard_imports, clippy::enum_glob_use ) ] use syn::UseTree::*; // let leading_colon = None; @@ -92,11 +94,11 @@ mod private pub fn pure_without_super_path( &self ) -> syn::Result< syn::punctuated::Punctuated< syn::Ident, Token![::] > > { let path = self.pure_path()?; - if path.len() < 1 + if path.is_empty() { return Ok( path ); } - if path[ 0 ].to_string() == "super" + if path[ 0 ] == "super" { // let mut path2 = syn::punctuated::Punctuated::< syn::Ident, Token![::] >::new(); let path2 : syn::punctuated::Punctuated< syn::Ident, Token![::] > = path.into_iter().skip(1).collect(); @@ -149,6 +151,7 @@ mod private { fn parse( input : ParseStream< '_ > ) -> syn::Result< Self > { + #[ allow( clippy::wildcard_imports, clippy::enum_glob_use ) ] use syn::UseTree::*; let leading_colon = input.parse()?; let tree = input.parse()?; @@ -216,6 +219,7 @@ pub use own::*; #[ allow( unused_imports ) ] pub mod own { + #[ allow( clippy::wildcard_imports ) ] use super::*; pub use orphan::*; } @@ -224,6 +228,7 @@ pub mod own #[ allow( unused_imports ) ] pub mod orphan { + #[ allow( clippy::wildcard_imports ) ] use super::*; pub use exposed::*; } @@ -232,6 +237,7 @@ pub mod orphan #[ allow( unused_imports ) ] pub mod exposed { + #[ allow( clippy::wildcard_imports ) ] use super::*; pub use prelude::*; diff --git a/module/core/mod_interface_meta/src/visibility.rs b/module/core/mod_interface_meta/src/visibility.rs index 4e229ed21c..747b2a2da5 100644 --- a/module/core/mod_interface_meta/src/visibility.rs +++ b/module/core/mod_interface_meta/src/visibility.rs @@ -13,6 +13,7 @@ mod private pub mod kw { + #[ allow( clippy::wildcard_imports ) ] use super::*; // syn::custom_keyword!( private ); syn::custom_keyword!( own ); @@ -425,7 +426,7 @@ mod private Visibility::Orphan( e ) => e.restriction(), Visibility::Exposed( e ) => e.restriction(), Visibility::Prelude( e ) => e.restriction(), - Visibility::Public( _ ) => None, + Visibility::Public( _ ) | // Visibility::Restricted( e ) => e.restriction(), Visibility::Inherited => None, } @@ -485,12 +486,12 @@ mod private } } - #[ allow( clippy::derive_hash_xor_eq ) ] + #[ allow( clippy::derived_hash_with_manual_eq ) ] impl Hash for Visibility { fn hash< H : Hasher >( &self, state : &mut H ) { - self.kind().hash( state ) + self.kind().hash( state ); } } @@ -520,6 +521,7 @@ pub use own::*; #[ allow( unused_imports ) ] pub mod own { + #[ allow( clippy::wildcard_imports ) ] use super::*; pub use orphan::*; } @@ -528,6 +530,7 @@ pub mod own #[ allow( unused_imports ) ] pub mod orphan { + #[ allow( clippy::wildcard_imports ) ] use super::*; pub use exposed::*; } @@ -536,6 +539,7 @@ pub mod orphan #[ allow( unused_imports ) ] pub mod exposed { + #[ allow( clippy::wildcard_imports ) ] use super::*; pub use prelude::*; diff --git a/module/core/process_tools/Readme.md b/module/core/process_tools/Readme.md index 97f7c673ea..86fb5c3d6d 100644 --- a/module/core/process_tools/Readme.md +++ b/module/core/process_tools/Readme.md @@ -1,6 +1,6 @@ -# Module :: process_tools +# Module :: `process_tools` [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) [![rust-status](https://github.com/Wandalen/wTools/actions/workflows/module_process_tools_push.yml/badge.svg)](https://github.com/Wandalen/wTools/actions/workflows/module_process_tools_push.yml) [![docs.rs](https://img.shields.io/docsrs/process_tools?color=e3e8f0&logo=docs.rs)](https://docs.rs/process_tools) [![discord](https://img.shields.io/discord/872391416519737405?color=eee&logo=discord&logoColor=eee&label=ask)](https://discord.gg/m3YfbXpUUY) diff --git a/module/core/process_tools/src/environment.rs b/module/core/process_tools/src/environment.rs index 31d20aa9f0..485b2b6092 100644 --- a/module/core/process_tools/src/environment.rs +++ b/module/core/process_tools/src/environment.rs @@ -6,7 +6,7 @@ mod private /// /// This function looks for environment variables that are commonly set by CI/CD systems to determine if it's running /// within such an environment. It supports detection for a variety of popular CI/CD platforms including GitHub Actions, - /// GitLab CI, Travis CI, CircleCI, and Jenkins. + /// GitLab CI, Travis CI, `CircleCI`, and Jenkins. /// /// # Returns /// - `true` if an environment variable indicating a CI/CD environment is found. @@ -29,10 +29,11 @@ mod private /// ``` #[ cfg( feature = "process_environment_is_cicd" ) ] + #[ must_use ] pub fn is_cicd() -> bool { use std::env; - let ci_vars = vec! + let ci_vars = [ "CI", // Common in many CI systems "GITHUB_ACTIONS", // GitHub Actions diff --git a/module/core/process_tools/src/process.rs b/module/core/process_tools/src/process.rs index d58e95455a..d0637d805a 100644 --- a/module/core/process_tools/src/process.rs +++ b/module/core/process_tools/src/process.rs @@ -1,4 +1,5 @@ /// Define a private namespace for all its items. +#[ allow( clippy::std_instead_of_alloc, clippy::std_instead_of_core ) ] mod private { // use crate::*; @@ -73,17 +74,20 @@ mod private /// Executes an external process in a specified directory without using a shell. /// /// # Arguments: - /// - `bin_path`: Path to the executable bin_path. - /// - `args`: Command-line arguments for the bin_path. - /// - `current_path`: Directory current_path to run the bin_path in. + /// - `bin_path`: Path to the executable `bin_path`. + /// - `args`: Command-line arguments for the `bin_path`. + /// - `current_path`: Directory `current_path` to run the `bin_path` in. /// /// # Returns: /// A `Result` containing `Report` on success, detailing execution output, /// or an error message on failure. /// - /// # Errors: + /// # Errors /// Returns an error if the process fails to spawn, complete, or if output /// cannot be decoded as UTF-8. + /// + /// # Panics + /// qqq: doc // // qqq : for Petro : use typed error // qqq : for Petro : write example @@ -131,7 +135,7 @@ mod private .context( "failed to spawn process" ) .map_err( | e | { - report.error = Err( e.into() ); + report.error = Err( e ); Err::< (), () >( () ) }); @@ -141,16 +145,14 @@ mod private } let child = child.unwrap(); - let output = child + child .wait_with_output() .context( "failed to wait on child" ) .map_err( | e | { - report.error = Err( e.into() ); + report.error = Err( e ); Err::< (), () >( () ) - }); - - output + }) }; if report.error.is_err() @@ -163,7 +165,7 @@ mod private .context( "Found invalid UTF-8" ) .map_err( | e | { - report.error = Err( e.into() ); + report.error = Err( e ); Err::< (), () >( () ) }); @@ -179,7 +181,7 @@ mod private .context( "Found invalid UTF-8" ) .map_err( | e | { - report.error = Err( e.into() ); + report.error = Err( e ); Err::< (), () >( () ) }); @@ -290,10 +292,10 @@ mod private { Report { - command : Default::default(), + command : String::default(), current_path : PathBuf::new(), - out : Default::default(), - err : Default::default(), + out : String::default(), + err : String::default(), error : Ok( () ), } } diff --git a/module/core/pth/Readme.md b/module/core/pth/Readme.md index a6f4c2f04d..9479a502d6 100644 --- a/module/core/pth/Readme.md +++ b/module/core/pth/Readme.md @@ -11,7 +11,7 @@ All functions in the crate don't touch file system, but only process paths. ### Type `AbsolutePath` -The AbsolutePath type ensures that paths are absolute, which helps reduce issues and maintenance costs associated with relative paths. Relative paths can be problematic as they introduce additional variables and complexities, making code analysis, integration, refactoring, and testing more difficult. By using absolute paths, software architecture can be improved, similar to how avoiding global variables can enhance code quality. It is recommended to use relative paths only at the outskirts of an application. +The `AbsolutePath` type ensures that paths are absolute, which helps reduce issues and maintenance costs associated with relative paths. Relative paths can be problematic as they introduce additional variables and complexities, making code analysis, integration, refactoring, and testing more difficult. By using absolute paths, software architecture can be improved, similar to how avoiding global variables can enhance code quality. It is recommended to use relative paths only at the outskirts of an application. ### Trait `AsPath` @@ -19,11 +19,11 @@ This trait is used to avoid redundant allocation of memory by providing a refere ### Trait `TryIntoPath` -This trait is used to convert any path-like type into an owned PathBuf. Unlike `TryIntoCowPath`, it always returns an owned PathBuf, so there is no need to differentiate between borrowed and owned paths at runtime. Unlike `AsPath`, it is implemented for a wider range of path-like types, similar to `TryIntoCowPath`. +This trait is used to convert any path-like type into an owned `PathBuf`. Unlike `TryIntoCowPath`, it always returns an owned `PathBuf`, so there is no need to differentiate between borrowed and owned paths at runtime. Unlike `AsPath`, it is implemented for a wider range of path-like types, similar to `TryIntoCowPath`. ### Trait `TryIntoCowPath` -This trait is designed to avoid redundant memory allocation. Unlike TryIntoPath, it does not allocate memory on the heap if it’s not necessary. Unlike `AsPath`, it is implemented for a wider number of path-like types, similar to TryIntoPath. The drawback is the necessity to differentiate borrowed and owned paths at runtime. +This trait is designed to avoid redundant memory allocation. Unlike `TryIntoPath`, it does not allocate memory on the heap if it’s not necessary. Unlike `AsPath`, it is implemented for a wider number of path-like types, similar to `TryIntoPath`. The drawback is the necessity to differentiate borrowed and owned paths at runtime. -# Module :: strs_tools +# Module :: `strs_tools` [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) [![rust-status](https://github.com/Wandalen/wTools/actions/workflows/module_strs_tools_push.yml/badge.svg)](https://github.com/Wandalen/wTools/actions/workflows/module_strs_tools_push.yml) [![docs.rs](https://img.shields.io/docsrs/strs_tools?color=e3e8f0&logo=docs.rs)](https://docs.rs/strs_tools) [![Open in Gitpod](https://raster.shields.io/static/v1?label=try&message=online&color=eee&logo=gitpod&logoColor=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=module%2Fcore%2Fstrs_tools%2Fexamples%2Fstrs_tools_trivial.rs,RUN_POSTFIX=--example%20strs_tools_trivial/https://github.com/Wandalen/wTools) [![discord](https://img.shields.io/discord/872391416519737405?color=eee&logo=discord&logoColor=eee&label=ask)](https://discord.gg/m3YfbXpUUY) diff --git a/module/core/strs_tools/src/lib.rs b/module/core/strs_tools/src/lib.rs index 72ff01c34c..b3858c911f 100644 --- a/module/core/strs_tools/src/lib.rs +++ b/module/core/strs_tools/src/lib.rs @@ -18,6 +18,7 @@ pub use own::*; #[ allow( unused_imports ) ] pub mod own { + #[ allow( clippy::wildcard_imports ) ] use super::*; pub use orphan::*; pub use super::string::orphan::*; @@ -28,6 +29,7 @@ pub mod own #[ allow( unused_imports ) ] pub mod orphan { + #[ allow( clippy::wildcard_imports ) ] use super::*; pub use exposed::*; } diff --git a/module/core/strs_tools/src/string/indentation.rs b/module/core/strs_tools/src/string/indentation.rs index 59c29951eb..8e71ccbcfb 100644 --- a/module/core/strs_tools/src/string/indentation.rs +++ b/module/core/strs_tools/src/string/indentation.rs @@ -57,17 +57,17 @@ mod private { if b.0 > 0 { - a.push_str( "\n" ); + a.push( '\n' ); } a.push_str( prefix ); - a.push_str( &b.1 ); + a.push_str( b.1 ); a.push_str( postfix ); a }); - if src.ends_with( "\n" ) || src.ends_with( "\n\r" ) || src.ends_with( "\r\n" ) + if src.ends_with( '\n' ) || src.ends_with( "\n\r" ) || src.ends_with( "\r\n" ) { - result.push_str( "\n" ); + result.push( '\n' ); result.push_str( prefix ); result.push_str( postfix ); } @@ -85,6 +85,7 @@ pub use own::*; #[ allow( unused_imports ) ] pub mod own { + #[ allow( clippy::wildcard_imports ) ] use super::*; pub use orphan::*; pub use private:: @@ -96,6 +97,7 @@ pub mod own #[ allow( unused_imports ) ] pub mod orphan { + #[ allow( clippy::wildcard_imports ) ] use super::*; pub use exposed::*; pub use private:: @@ -107,6 +109,7 @@ pub mod orphan #[ allow( unused_imports ) ] pub mod exposed { + #[ allow( clippy::wildcard_imports ) ] use super::*; pub use super::own as indentation; diff --git a/module/core/strs_tools/src/string/isolate.rs b/module/core/strs_tools/src/string/isolate.rs index 50ba7fe294..34e9af9449 100644 --- a/module/core/strs_tools/src/string/isolate.rs +++ b/module/core/strs_tools/src/string/isolate.rs @@ -26,7 +26,7 @@ mod private } /// - /// Adapter for IsolateOptions. + /// Adapter for `IsolateOptions`. /// pub trait IsolateOptionsAdapter< 'a > @@ -144,6 +144,7 @@ mod private /// It produces former. To convert former into options and run algorithm of splitting call `perform()`. /// + #[ must_use ] pub fn isolate<'a>() -> IsolateOptionsFormer<'a> { IsolateOptions::former() @@ -155,6 +156,7 @@ mod private /// It produces former. To convert former into options and run algorithm of splitting call `perform()`. /// + #[ must_use ] pub fn isolate_left<'a>() -> IsolateOptionsFormer<'a> { IsolateOptions::former() @@ -167,6 +169,7 @@ mod private /// It produces former. To convert former into options and run algorithm of splitting call `perform()`. /// + #[ must_use ] pub fn isolate_right<'a>() -> IsolateOptionsFormer<'a> { IsolateOptions::former() @@ -194,6 +197,7 @@ pub use own::*; #[ allow( unused_imports ) ] pub mod orphan { + #[ allow( clippy::wildcard_imports ) ] use super::*; pub use exposed::*; } diff --git a/module/core/strs_tools/src/string/mod.rs b/module/core/strs_tools/src/string/mod.rs index 46552b8124..d473f9eeef 100644 --- a/module/core/strs_tools/src/string/mod.rs +++ b/module/core/strs_tools/src/string/mod.rs @@ -33,7 +33,9 @@ pub use own::*; #[ allow( unused_imports ) ] pub mod own { + #[ allow( clippy::wildcard_imports ) ] use super::*; + #[ allow( clippy::wildcard_imports ) ] pub use orphan::*; #[ cfg( all( feature = "string_indentation", not( feature = "no_std" ) ) ) ] pub use super::indentation::orphan::*; @@ -52,6 +54,7 @@ pub mod own #[ allow( unused_imports ) ] pub mod orphan { + #[ allow( clippy::wildcard_imports ) ] use super::*; pub use exposed::*; } diff --git a/module/core/strs_tools/src/string/number.rs b/module/core/strs_tools/src/string/number.rs index 1a0df1c3ad..fac6b4664d 100644 --- a/module/core/strs_tools/src/string/number.rs +++ b/module/core/strs_tools/src/string/number.rs @@ -11,14 +11,15 @@ pub use own::*; #[ allow( unused_imports ) ] pub mod own { + #[ allow( clippy::wildcard_imports ) ] use super::*; pub use orphan::*; pub use private:: { }; - #[ cfg( all( feature = "string_parse_number" ) ) ] + #[ cfg( feature = "string_parse_number" ) ] #[ doc( inline ) ] - #[ allow( unused_imports ) ] + #[ allow( unused_imports, clippy::wildcard_imports ) ] pub use lexical::*; } @@ -26,6 +27,7 @@ pub mod own #[ allow( unused_imports ) ] pub mod orphan { + #[ allow( clippy::wildcard_imports ) ] use super::*; pub use exposed::*; pub use private:: @@ -37,6 +39,7 @@ pub mod orphan #[ allow( unused_imports ) ] pub mod exposed { + #[ allow( clippy::wildcard_imports ) ] use super::*; pub use super::own as number; diff --git a/module/core/strs_tools/src/string/parse_request.rs b/module/core/strs_tools/src/string/parse_request.rs index 44e215d15d..e926b4a4cf 100644 --- a/module/core/strs_tools/src/string/parse_request.rs +++ b/module/core/strs_tools/src/string/parse_request.rs @@ -1,7 +1,9 @@ /// Define a private namespace for all its items. mod private { + #[ allow( clippy::wildcard_imports ) ] use crate::*; + #[ allow( clippy::wildcard_imports ) ] use string:: { split::*, @@ -48,6 +50,7 @@ mod private } } + #[ allow( clippy::from_over_into ) ] impl< T > Into > for OpType< T > { fn into( self ) -> Vec< T > @@ -62,8 +65,11 @@ mod private impl OpType< T > { - /// Append item of OpType to current value. If current type is `Primitive`, then it will be converted to + /// Append item of `OpType` to current value. If current type is `Primitive`, then it will be converted to /// `Vector`. + /// # Panics + /// qqq: doc + #[ must_use ] pub fn append( mut self, item : OpType< T > ) -> OpType< T > { let mut mut_item = item; @@ -156,6 +162,7 @@ mod private /// Options for parser. /// + #[ allow( clippy::struct_excessive_bools ) ] #[ derive( Debug, former::Former ) ] #[ perform( fn parse( mut self ) -> Request< 'a > ) ] pub struct ParseOptions< 'a > @@ -179,7 +186,7 @@ mod private } /// - /// Adapter for ParseOptions. + /// Adapter for `ParseOptions`. /// pub trait ParseOptionsAdapter< 'a > @@ -245,6 +252,7 @@ mod private self.subject_win_paths_maybe } + #[ allow( clippy::assigning_clones, clippy::too_many_lines, clippy::collapsible_if ) ] fn parse( mut self ) -> Request< 'a > where Self : Sized, @@ -474,6 +482,7 @@ mod private /// It produces former. To convert former into options and run algorithm of splitting call `perform()`. /// + #[ must_use ] pub fn request_parse<'a>() -> ParseOptionsFormer<'a> { ParseOptions::former() @@ -488,6 +497,7 @@ pub use own::*; #[ allow( unused_imports ) ] pub mod own { + #[ allow( clippy::wildcard_imports ) ] use super::*; pub use orphan::*; pub use private:: @@ -504,6 +514,7 @@ pub mod own #[ allow( unused_imports ) ] pub mod orphan { + #[ allow( clippy::wildcard_imports ) ] use super::*; pub use exposed::*; } @@ -512,6 +523,7 @@ pub mod orphan #[ allow( unused_imports ) ] pub mod exposed { + #[ allow( clippy::wildcard_imports ) ] use super::*; pub use super::own as parse_request; @@ -526,6 +538,7 @@ pub mod exposed #[ allow( unused_imports ) ] pub mod prelude { + #[ allow( clippy::wildcard_imports ) ] use super::*; pub use private::ParseOptionsAdapter; } diff --git a/module/core/strs_tools/src/string/split.rs b/module/core/strs_tools/src/string/split.rs index 5c9eac10cd..6744e6a020 100644 --- a/module/core/strs_tools/src/string/split.rs +++ b/module/core/strs_tools/src/string/split.rs @@ -72,7 +72,7 @@ mod private { if let Some( x ) = src.find( pat ) { - r.push( ( x, x + pat.len() ) ) + r.push( ( x, x + pat.len() ) ); } } @@ -116,7 +116,7 @@ mod private impl< 'a, D : Searcher + Clone > SplitFastIterator< 'a, D > { - #[ allow( dead_code ) ] + #[ allow( dead_code, clippy::needless_pass_by_value ) ] fn new( o : impl SplitOptionsAdapter< 'a, D > ) -> Self { Self @@ -154,12 +154,10 @@ mod private { return None; } - else - { - self.counter -= 1; - self.stop_empty = true; - return Some( Split { string : "", typ : SplitType::Delimeted } ); - } + + self.counter -= 1; + self.stop_empty = true; + return Some( Split { string : "", typ : SplitType::Delimeted } ); } if start == 0 && end != 0 @@ -170,7 +168,7 @@ mod private let mut next = &self.iterable[ ..start ]; if start == end && self.counter >= 3 { - next = &self.iterable[ ..start + 1 ]; + next = &self.iterable[ ..=start ]; start += 1; } @@ -229,6 +227,7 @@ mod private /// #[ derive( Debug ) ] + #[ allow( clippy::struct_excessive_bools ) ] pub struct SplitIterator< 'a > { iterator : SplitFastIterator< 'a, Vec< &'a str > >, @@ -247,6 +246,7 @@ mod private impl< 'a > SplitIterator< 'a > { + #[ allow( clippy::needless_pass_by_value ) ] fn new( o : impl SplitOptionsAdapter< 'a, Vec< &'a str > > ) -> Self { let iterator; @@ -338,10 +338,7 @@ mod private { return self.next(); } - else - { - return Some( split ); - } + return Some( split ); }, None => { @@ -405,6 +402,7 @@ mod private /// #[ derive( Debug ) ] + #[ allow( clippy::struct_excessive_bools ) ] pub struct SplitOptions< 'a, D > where D : Searcher + Default + Clone, @@ -422,7 +420,8 @@ mod private impl< 'a > SplitOptions< 'a, Vec< &'a str > > { - /// Produces SplitIterator. + /// Produces `SplitIterator`. + #[ must_use ] pub fn split( self ) -> SplitIterator< 'a > where Self : Sized, @@ -435,7 +434,7 @@ mod private where D : Searcher + Default + Clone { - /// Produces SplitFastIterator. + /// Produces `SplitFastIterator`. pub fn split_fast( self ) -> SplitFastIterator< 'a, D > where Self : Sized, @@ -561,9 +560,10 @@ mod private } /// - /// Former for SplitOptions. + /// Former for `SplitOptions`. /// + #[ allow( clippy::struct_excessive_bools ) ] #[ derive( Debug ) ] pub struct SplitOptionsFormer< 'a > { @@ -637,6 +637,7 @@ mod private /// .perform(); /// ``` + #[ must_use ] pub fn split< 'a >() -> SplitOptionsFormer< 'a > { SplitOptionsFormer::new( < &str >::default() ) @@ -651,6 +652,7 @@ pub use own::*; #[ allow( unused_imports ) ] pub mod own { + #[ allow( clippy::wildcard_imports ) ] use super::*; pub use orphan::*; pub use private:: @@ -668,6 +670,7 @@ pub mod own #[ allow( unused_imports ) ] pub mod orphan { + #[ allow( clippy::wildcard_imports ) ] use super::*; pub use exposed::*; } @@ -676,6 +679,7 @@ pub mod orphan #[ allow( unused_imports ) ] pub mod exposed { + #[ allow( clippy::wildcard_imports ) ] use super::*; pub use super::own as split; @@ -690,6 +694,7 @@ pub mod exposed #[ allow( unused_imports ) ] pub mod prelude { + #[ allow( clippy::wildcard_imports ) ] use super::*; pub use private::SplitOptionsAdapter; } diff --git a/module/move/crates_tools/Readme.md b/module/move/crates_tools/Readme.md index dabc50fb6c..f407d2599e 100644 --- a/module/move/crates_tools/Readme.md +++ b/module/move/crates_tools/Readme.md @@ -1,6 +1,6 @@ -# Module :: crates_tools +# Module :: `crates_tools` [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) [![rust-status](https://github.com/Wandalen/wTools/actions/workflows/module_crates_tools_push.yml/badge.svg)](https://github.com/Wandalen/wTools/actions/workflows/module_crates_tools_push.yml) [![docs.rs](https://img.shields.io/docsrs/crates_tools?color=e3e8f0&logo=docs.rs)](https://docs.rs/crates_tools) [![Open in Gitpod](https://raster.shields.io/static/v1?label=try&message=online&color=eee&logo=gitpod&logoColor=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=module%2Fmove%2Fcrates_tools%2Fexamples%2Fcrates_tools_trivial.rs,RUN_POSTFIX=--example%20crates_tools_trivial/https://github.com/Wandalen/wTools) [![discord](https://img.shields.io/discord/872391416519737405?color=eee&logo=discord&logoColor=eee&label=ask)](https://discord.gg/m3YfbXpUUY) @@ -25,7 +25,6 @@ Some possible use cases are: ```rust use crates_tools::*; -fn main() { #[ cfg( feature = "enabled" ) ] { diff --git a/module/move/crates_tools/src/lib.rs b/module/move/crates_tools/src/lib.rs index f43c975b7f..1d60bb2135 100644 --- a/module/move/crates_tools/src/lib.rs +++ b/module/move/crates_tools/src/lib.rs @@ -8,19 +8,21 @@ mod private { use std::collections::HashMap; - use std::fmt::Formatter; + use core::fmt::Formatter; use std::io::Read; use std::path::{ Path, PathBuf }; - use std::time::Duration; - use ureq::{ Agent, AgentBuilder }; + use core::time::Duration; + use ureq::AgentBuilder; /// Represents a `.crate` archive, which is a collection of files and their contents. #[ derive( Default, Clone, PartialEq ) ] pub struct CrateArchive( HashMap< PathBuf, Vec< u8 > > ); - impl std::fmt::Debug for CrateArchive + impl core::fmt::Debug for CrateArchive { - fn fmt( &self, f : &mut Formatter< '_ > ) -> std::fmt::Result + #[ allow( clippy::implicit_return, clippy::min_ident_chars ) ] + #[ inline] + fn fmt( &self, f : &mut Formatter< '_ > ) -> core::fmt::Result { f.debug_struct( "CrateArchive" ).field( "files", &self.0.keys() ).finish() } @@ -29,24 +31,33 @@ mod private impl CrateArchive { /// Reads and decode a `.crate` archive from a given path. + /// # Errors + /// qqq: doc + #[ allow( clippy::question_mark_used, clippy::implicit_return ) ] + #[ inline ] pub fn read< P >( path : P ) -> std::io::Result< Self > where P : AsRef< Path >, { let mut file = std::fs::File::open( path )?; let mut buf = vec![]; + #[ allow( clippy::verbose_file_reads ) ] file.read_to_end( &mut buf )?; Self::decode( buf ) } #[ cfg( feature = "network" ) ] + #[ allow( clippy::question_mark_used, clippy::implicit_return, clippy::result_large_err ) ] /// Downloads and decodes a `.crate` archive from a given url. + /// # Errors + /// qqq: docs + #[ inline ] pub fn download< Url >( url : Url ) -> Result< Self, ureq::Error > where Url : AsRef< str >, { - let agent: Agent = AgentBuilder::new() + let agent = AgentBuilder::new() .timeout_read( Duration::from_secs( 5 ) ) .timeout_write( Duration::from_secs( 5 ) ) .build(); @@ -63,16 +74,24 @@ mod private /// Requires the full version of the package, in the format of `"x.y.z"` /// /// Returns error if the package with specified name and version - not exists. + /// # Errors + /// qqq: doc #[ cfg( feature = "network" ) ] + #[ allow( clippy::implicit_return, clippy::result_large_err ) ] + #[ inline ] pub fn download_crates_io< N, V >( name : N, version : V ) -> Result< Self, ureq::Error > where - N : std::fmt::Display, - V : std::fmt::Display, + N : core::fmt::Display, + V : core::fmt::Display, { Self::download( format!( "https://static.crates.io/crates/{name}/{name}-{version}.crate" ) ) } /// Decodes a bytes that represents a `.crate` file. + /// # Errors + /// qqq: doc + #[ allow( clippy::question_mark_used, unknown_lints, clippy::implicit_return ) ] + #[ inline ] pub fn decode< B >( bytes : B ) -> std::io::Result< Self > where B : AsRef<[ u8 ]>, @@ -81,43 +100,44 @@ mod private use flate2::bufread::GzDecoder; use tar::Archive; - let bytes = bytes.as_ref(); - if bytes.is_empty() + let bytes_slice = bytes.as_ref(); + if bytes_slice.is_empty() { return Ok( Self::default() ) } - let gz = GzDecoder::new( bytes ); + let gz = GzDecoder::new( bytes_slice ); let mut archive = Archive::new( gz ); let mut output = HashMap::new(); for file in archive.entries()? { - let mut file = file?; + let mut archive_file = file?; let mut contents = vec![]; - file.read_to_end( &mut contents )?; + archive_file.read_to_end( &mut contents )?; - output.insert( file.path()?.to_path_buf(), contents ); + output.insert( archive_file.path()?.to_path_buf(), contents ); } Ok( Self( output ) ) } - } - impl CrateArchive - { /// Returns a list of files from the `.crate` file. + #[ allow( clippy::implicit_return ) ] + #[ inline ] pub fn list( &self ) -> Vec< &Path > { self.0.keys().map( PathBuf::as_path ).collect() } /// Returns content of file by specified path from the `.crate` file in bytes representation. + #[ allow( clippy::implicit_return ) ] + #[ inline ] pub fn content_bytes< P >( &self, path : P ) -> Option< &[ u8 ] > - where - P : AsRef< Path >, + where + P : AsRef< Path >, { self.0.get( path.as_ref() ).map( Vec::as_ref ) } @@ -126,7 +146,7 @@ mod private #[ cfg( feature = "enabled" ) ] #[ doc( inline ) ] -#[ allow( unused_imports ) ] +#[ allow( unused_imports, clippy::pub_use ) ] pub use own::*; /// Own namespace of the module. @@ -134,9 +154,9 @@ pub use own::*; #[ allow( unused_imports ) ] pub mod own { - use super::*; + use super::orphan; #[ doc( inline ) ] - #[ allow( unused_imports ) ] + #[ allow( unused_imports, clippy::pub_use ) ] pub use orphan::*; } @@ -145,9 +165,9 @@ pub mod own #[ allow( unused_imports ) ] pub mod orphan { - use super::*; + use super::exposed; #[ doc( inline ) ] - #[ allow( unused_imports ) ] + #[ allow( unused_imports, clippy::pub_use ) ] pub use exposed::*; } @@ -156,9 +176,9 @@ pub mod orphan #[ allow( unused_imports ) ] pub mod exposed { - use super::*; + use super::prelude; #[ doc( inline ) ] - #[ allow( unused_imports ) ] + #[ allow( unused_imports, clippy::pub_use ) ] pub use prelude::*; } @@ -167,8 +187,8 @@ pub mod exposed #[ allow( unused_imports ) ] pub mod prelude { - use super::*; + use super::private; #[ doc( inline ) ] - #[ allow( unused_imports ) ] + #[ allow( unused_imports, clippy::pub_use ) ] pub use private::CrateArchive; } diff --git a/module/move/optimization_tools/Cargo.toml b/module/move/optimization_tools/Cargo.toml index af2f73c222..4a276984c9 100644 --- a/module/move/optimization_tools/Cargo.toml +++ b/module/move/optimization_tools/Cargo.toml @@ -17,8 +17,8 @@ categories = [ "algorithms", "development-tools" ] keywords = [ "fundamental", "general-purpose" ] # xxx : qqq : switch that on -# [lints] -# workspace = true +#[lints] +#workspace = true [package.metadata.docs.rs] features = [ "full" ] @@ -60,7 +60,7 @@ plotters = { version = "0.3.5", default-features = false, features = [ "bitmap_backend", ] } plotters-backend = { version = "0.3.5", optional = true } -piston_window = { version = "0.120.0", optional = true } +piston_window = { version = "0.132.0", optional = true } exmex = { version = "0.18.0", features = [ "partial" ], optional = true } rayon = "1.8.0" thiserror = "1.0.56" diff --git a/module/move/unitore/Cargo.toml b/module/move/unitore/Cargo.toml index fa560e6cae..08313bc8e0 100644 --- a/module/move/unitore/Cargo.toml +++ b/module/move/unitore/Cargo.toml @@ -43,7 +43,7 @@ toml = "0.8.10" serde = "1.0.196" url = { version = "2.0", features = ["serde"] } humantime-serde = "1.1.1" -gluesql = "0.15.0" +gluesql = "0.16.2" async-trait = "0.1.41" wca = { workspace = true } mockall = "0.12.1" diff --git a/module/move/wca/src/ca/aggregator.rs b/module/move/wca/src/ca/aggregator.rs index c8cd532342..fb3725ba16 100644 --- a/module/move/wca/src/ca/aggregator.rs +++ b/module/move/wca/src/ca/aggregator.rs @@ -1,5 +1,7 @@ +#[ allow( clippy::std_instead_of_alloc, clippy::std_instead_of_core ) ] mod private { + #[ allow( clippy::wildcard_imports ) ] use crate::*; use ca:: { @@ -81,6 +83,7 @@ mod private // xxx : aaa : aaa2 : for Bohdan : one level is obviously redundant // Program< Namespace< ExecutableCommand_ > > -> Program< ExecutableCommand_ > // aaa : done. The concept of `Namespace` has been removed + #[ allow( clippy::type_complexity ) ] struct CommandsAggregatorCallback( Box< dyn Fn( &str, &Program< VerifiedCommand > ) > ); impl fmt::Debug for CommandsAggregatorCallback @@ -94,7 +97,7 @@ mod private /// The `CommandsAggregator` struct is responsible for aggregating all commands that the user defines, /// and for parsing and executing them. It is the main entry point of the library. /// - /// CommandsAggregator component brings everything together. This component is responsible for configuring the `Parser`, `Grammar`, and `Executor` components based on the user’s needs. It also manages the entire pipeline of processing, from parsing the raw text input to executing the final command(parse -> validate -> execute). + /// `CommandsAggregator` component brings everything together. This component is responsible for configuring the `Parser`, `Grammar`, and `Executor` components based on the user’s needs. It also manages the entire pipeline of processing, from parsing the raw text input to executing the final command(parse -> validate -> execute). /// /// # Example: /// @@ -145,8 +148,8 @@ mod private let dictionary = ca.dictionary.get_or_insert_with( Dictionary::default ); dictionary.order = ca.order.unwrap_or_default(); - let help_generator = std::mem::take( &mut ca.help_generator ).unwrap_or_default(); - let help_variants = std::mem::take( &mut ca.help_variants ).unwrap_or_else( || HashSet::from([ HelpVariants::All ]) ); + let help_generator = core::mem::take( &mut ca.help_generator ).unwrap_or_default(); + let help_variants = core::mem::take( &mut ca.help_variants ).unwrap_or_else( || HashSet::from([ HelpVariants::All ]) ); if help_variants.contains( &HelpVariants::All ) { @@ -171,6 +174,8 @@ mod private /// # Arguments /// /// * `name` - The name of the command. + /// # Panics + /// qqq: doc pub fn command< IntoName >( self, name : IntoName ) -> CommandAsSubformer< Self, impl CommandAsSubformerEnd< Self > > where IntoName : Into< String >, @@ -204,6 +209,7 @@ mod private /// /// The modified instance of `Self`. // `'static` means that the value must be owned or live at least as a `Context' + #[ must_use ] pub fn with_context< T >( mut self, value : T ) -> Self where T : Sync + Send + 'static, @@ -231,6 +237,7 @@ mod private /// ca.perform( ".help" )?; /// # Ok( () ) } /// ``` + #[ must_use ] pub fn help< HelpFunction >( mut self, func : HelpFunction ) -> Self where HelpFunction : Fn( &Dictionary, HelpGeneratorOptions< '_ > ) -> String + 'static @@ -256,6 +263,7 @@ mod private /// ca.perform( ".help" )?; /// # Ok( () ) } /// ``` + #[ must_use ] pub fn callback< Callback >( mut self, callback : Callback ) -> Self where Callback : Fn( &str, &Program< VerifiedCommand > ) + 'static, @@ -270,18 +278,20 @@ mod private /// Parse, converts and executes a program /// /// Takes a string with program and executes it + /// # Errors + /// qqq: doc pub fn perform< S >( &self, program : S ) -> Result< (), Error > where S : IntoInput { let Input( ref program ) = program.into_input(); - let raw_program = self.parser.parse( program ).map_err( | e | Error::Validation( ValidationError::Parser { input : format!( "{:?}", program ), error : e } ) )?; + let raw_program = self.parser.parse( program ).map_err( | e | Error::Validation( ValidationError::Parser { input : format!( "{program:?}" ), error : e } ) )?; let grammar_program = self.verifier.to_program( &self.dictionary, raw_program ).map_err( | e | Error::Validation( ValidationError::Verifier( e ) ) )?; if let Some( callback ) = &self.callback_fn { - callback.0( &program.join( " " ), &grammar_program ) + callback.0( &program.join( " " ), &grammar_program ); } self.executor.program( &self.dictionary, grammar_program ).map_err( | e | Error::Execution( e.into() ) ) diff --git a/module/move/wca/src/ca/executor/context.rs b/module/move/wca/src/ca/executor/context.rs index 716bbafda6..a9611b618e 100644 --- a/module/move/wca/src/ca/executor/context.rs +++ b/module/move/wca/src/ca/executor/context.rs @@ -1,3 +1,4 @@ +#[ allow( clippy::std_instead_of_alloc, clippy::std_instead_of_core ) ] mod private { use std::sync::Arc; @@ -37,7 +38,7 @@ mod private #[ derive( Debug, Clone ) ] pub struct Context { - inner : Arc< dyn std::any::Any + Send + Sync >, + inner : Arc< dyn core::any::Any + Send + Sync >, } impl Default for Context @@ -80,6 +81,7 @@ mod private /// An `Option` containing a reference-counted smart pointer (`Arc`) to the object of type `T` if it exists in the context. /// `None` is returned if the object does not exist or if it cannot be downcasted to type `T`. // `'static` means that the object must be owned or live at least as a `Context' + #[ must_use ] pub fn get< T : Send + Sync + 'static >( &self ) -> Option< Arc< T > > { self.inner.clone().downcast::< T >().ok() diff --git a/module/move/wca/src/ca/executor/executor.rs b/module/move/wca/src/ca/executor/executor.rs index 9d662801a8..d6a7a3bdf1 100644 --- a/module/move/wca/src/ca/executor/executor.rs +++ b/module/move/wca/src/ca/executor/executor.rs @@ -1,5 +1,6 @@ mod private { + #[ allow( clippy::wildcard_imports ) ] use crate::*; use ca::help::{ HelpGeneratorOptions, generate_help_content, LevelOfDetail }; @@ -45,7 +46,8 @@ mod private /// # Returns /// /// A `Result` with `Ok( () )` if the execution was successful, or an `Err` containing an error message if an error occurred. - /// + /// # Errors + /// qqq: doc // aaa : use typed error // aaa : done pub fn program( &self, dictionary : &Dictionary, program : Program< VerifiedCommand > ) @@ -71,6 +73,10 @@ mod private /// # Returns /// /// Returns a Result indicating success or failure. If successful, returns `Ok(())`, otherwise returns an error. + /// # Errors + /// qqq: doc + /// # Panics + /// qqq: doc // aaa : use typed error // aaa : done pub fn command( &self, dictionary : &Dictionary, command : VerifiedCommand ) @@ -116,6 +122,7 @@ mod private // aaa : use typed error // aaa : done + #[ allow( clippy::needless_pass_by_value ) ] fn _exec_internal_command( dictionary : &Dictionary, command : VerifiedCommand ) -> Result< (), InternalCommandError > { diff --git a/module/move/wca/src/ca/executor/routine.rs b/module/move/wca/src/ca/executor/routine.rs index ad9a455052..f40594af22 100644 --- a/module/move/wca/src/ca/executor/routine.rs +++ b/module/move/wca/src/ca/executor/routine.rs @@ -1,5 +1,7 @@ +#[ allow( clippy::std_instead_of_alloc, clippy::std_instead_of_core ) ] mod private { + #[ allow( clippy::wildcard_imports ) ] use crate::*; // aaa : group @@ -61,6 +63,7 @@ mod private /// let first_arg : &str = args[ 0 ].clone().into(); /// assert_eq!( "Hello, World!", first_arg ); /// ``` + #[ must_use ] pub fn get_owned< T : From< Value > >( &self, index : usize ) -> Option< T > { self.0.get( index ).map( | arg | arg.to_owned().into() ) @@ -180,9 +183,9 @@ mod private pub struct Handler< I, O >( Box< dyn Fn( I ) -> O > ); - impl< I, O > std::fmt::Debug for Handler< I, O > + impl< I, O > core::fmt::Debug for Handler< I, O > { - fn fmt( &self, f : &mut Formatter< '_ > ) -> std::fmt::Result + fn fmt( &self, f : &mut Formatter< '_ > ) -> core::fmt::Result { f.debug_struct( "Handler" ).finish_non_exhaustive() } @@ -261,9 +264,9 @@ mod private WithContext( Rc< RoutineWithContextFn > ), } - impl std::fmt::Debug for Routine + impl core::fmt::Debug for Routine { - fn fmt( &self, f : &mut Formatter< '_ > ) -> std::fmt::Result + fn fmt( &self, f : &mut Formatter< '_ > ) -> core::fmt::Result { match self { @@ -316,7 +319,7 @@ mod private { // We can't compare closures. Because every closure has a separate type, even if they're identical. // Therefore, we check that the two Rc's point to the same closure (allocation). - #[ allow( clippy::vtable_address_comparisons ) ] + #[ allow( ambiguous_wide_pointer_comparisons ) ] match ( self, other ) { ( Routine::WithContext( this ), Routine::WithContext( other ) ) => Rc::ptr_eq( this, other ), @@ -335,9 +338,9 @@ mod private // xxx // aaa : This is an untyped error because we want to provide a common interface for all commands, while also allowing users to propagate their own specific custom errors. - impl IntoResult for std::convert::Infallible { fn into_result( self ) -> error::untyped::Result< () > { Ok( () ) } } + impl IntoResult for core::convert::Infallible { fn into_result( self ) -> error::untyped::Result< () > { Ok( () ) } } impl IntoResult for () { fn into_result( self ) -> error::untyped::Result< () > { Ok( () ) } } - impl< E : std::fmt::Debug + std::fmt::Display + 'static > IntoResult + impl< E : core::fmt::Debug + std::fmt::Display + 'static > IntoResult for error::untyped::Result< (), E > { fn into_result( self ) -> error::untyped::Result< () > diff --git a/module/move/wca/src/ca/formatter.rs b/module/move/wca/src/ca/formatter.rs index 30e6787d6d..1c606532d7 100644 --- a/module/move/wca/src/ca/formatter.rs +++ b/module/move/wca/src/ca/formatter.rs @@ -1,6 +1,7 @@ mod private { + #[ allow( clippy::wildcard_imports ) ] use crate::*; use iter_tools::Itertools; use ca::aggregator::Order; @@ -26,13 +27,14 @@ mod private /// The `md_generator` function takes a reference to a `Dictionary` and an `Order` to produce /// a help document in Markdown format. This function is useful for generating structured, /// readable help documentation suitable for Markdown-compatible platforms. + #[ must_use ] pub fn md_generator( grammar : &Dictionary, order: Order ) -> String { let text = grammar.commands() .into_iter() .map( |( name, cmd )| { - let subjects = cmd.subjects.iter().fold( String::new(), | _, _ | format!( " `[argument]`" ) ); + let subjects = cmd.subjects.iter().fold( String::new(), | _, _ | " `[argument]`".to_string() ); let properties = if cmd.properties.is_empty() { " " } else { " `[properties]` " }; format! ( @@ -48,17 +50,17 @@ mod private format!( "{acc}\n- {cmd}" ) }); - let list_of_commands = format!( "## Commands\n\n{}", text ); + let list_of_commands = format!( "## Commands\n\n{text}" ); let about_each_command = grammar.commands() .into_iter() .map( |( name, cmd )| { - let subjects = cmd.subjects.iter().fold( String::new(), | _, _ | format!( " `[Subject]`" ) ); + let subjects = cmd.subjects.iter().fold( String::new(), | _, _ | " `[Subject]`".to_string() ); let properties = if cmd.properties.is_empty() { " " } else { " `[properties]` " }; let hint = if cmd.hint.is_empty() { &cmd.long_hint } else { &cmd.hint }; - let heading = format!( "## .{}{subjects}{properties}\n__{}__\n", name, hint ); + let heading = format!( "## .{name}{subjects}{properties}\n__{hint}__\n" ); let hint = if cmd.long_hint.is_empty() { &cmd.hint } else { &cmd.long_hint }; let full_subjects = cmd @@ -86,8 +88,8 @@ mod private format! ( "{heading}\n{}{}\n\n{hint}\n", - if cmd.subjects.is_empty() { "".to_string() } else { format!( "\n\nSubjects:{}", &full_subjects ) }, - if cmd.properties.is_empty() { "".to_string() } else { format!( "\n\nProperties:{}",&full_properties ) }, + if cmd.subjects.is_empty() { String::new() } else { format!( "\n\nSubjects:{}", &full_subjects ) }, + if cmd.properties.is_empty() { String::new() } else { format!( "\n\nProperties:{}",&full_properties ) }, ) }) diff --git a/module/move/wca/src/ca/grammar/command.rs b/module/move/wca/src/ca/grammar/command.rs index 0c17e726b1..e1bc974d63 100644 --- a/module/move/wca/src/ca/grammar/command.rs +++ b/module/move/wca/src/ca/grammar/command.rs @@ -1,5 +1,7 @@ +#[ allow( clippy::std_instead_of_alloc, clippy::std_instead_of_core ) ] mod private { + #[ allow( clippy::wildcard_imports ) ] use crate::*; use std::collections::HashMap; @@ -120,11 +122,11 @@ mod private { Order::Nature => { - self.properties.iter().map( | ( key, value ) | ( key, value ) ).collect() + self.properties.iter().collect() } Order::Lexicography => { - self.properties.iter().map( | ( key, value ) | ( key, value ) ).sorted_by_key( | ( k, _ ) | *k ).collect() + self.properties.iter().sorted_by_key( | ( k, _ ) | *k ).collect() } } } @@ -135,6 +137,7 @@ mod private Definition : former::FormerDefinition< Storage = < Command as former::EntityToStorage >::Storage >, { /// Setter for separate properties aliases. + #[ must_use ] pub fn property_alias< S : Into< String > >( mut self, key : S, alias : S ) -> Self { let key = key.into(); @@ -177,6 +180,7 @@ mod private /// # Returns /// /// Returns the `CommandFormer` instance with the new command routine set. + #[ must_use ] pub fn routine< I, R, F : Into< Handler< I, R > > >( mut self, f : F ) -> Self where Routine: From< Handler< I, R > >, @@ -209,6 +213,8 @@ mod private /// # Arguments /// /// * `name` - The name of the property. It should implement the `Into< String >` trait. + /// # Panics + /// qqq: doc pub fn property< IntoName >( self, name : IntoName ) -> PropertyDescriptionAsSubformer< Self, impl PropertyDescriptionAsSubformerEnd< Self > > where IntoName : Into< String >, diff --git a/module/move/wca/src/ca/grammar/dictionary.rs b/module/move/wca/src/ca/grammar/dictionary.rs index 8dc784f3db..3e8e0389a5 100644 --- a/module/move/wca/src/ca/grammar/dictionary.rs +++ b/module/move/wca/src/ca/grammar/dictionary.rs @@ -1,5 +1,7 @@ +#[ allow( clippy::std_instead_of_alloc, clippy::std_instead_of_core ) ] mod private { + #[ allow( clippy::wildcard_imports ) ] use crate::*; use former::Former; use indexmap::IndexMap; @@ -87,17 +89,18 @@ mod private } /// asd + #[ must_use ] pub fn commands( &self ) -> Vec< ( &String, &Command ) > { match self.order { Order::Nature => { - self.commands.iter().map( | ( key, value ) | ( key, value ) ).collect() + self.commands.iter().collect() } Order::Lexicography => { - self.commands.iter().map( | ( key, value ) | ( key, value ) ).sorted_by_key( | ( key, _ ) | *key ).collect() + self.commands.iter().sorted_by_key( | ( key, _ ) | *key ).collect() } } } diff --git a/module/move/wca/src/ca/grammar/types.rs b/module/move/wca/src/ca/grammar/types.rs index 99526b35dd..6bea357228 100644 --- a/module/move/wca/src/ca/grammar/types.rs +++ b/module/move/wca/src/ca/grammar/types.rs @@ -1,5 +1,7 @@ +#[ allow( clippy::std_instead_of_alloc, clippy::std_instead_of_core ) ] mod private { + #[ allow( clippy::wildcard_imports ) ] use crate::*; use std::fmt:: { @@ -44,6 +46,8 @@ mod private pub trait TryCast< T > { /// return casted value + /// # Errors + /// qqq: doc fn try_cast( &self, value : String ) -> error::untyped::Result< T >; } @@ -116,7 +120,7 @@ mod private } Value::List( list ) => { - let list = list.iter().map( | element | element.to_string() ).join( "," ); + let list = list.iter().map( std::string::ToString::to_string ).join( "," ); write!( f, "{list}" )?; } } @@ -135,7 +139,7 @@ mod private { match value { - #[ allow( clippy::redundant_closure_call ) ] // ok because of it improve understanding what is `value` at macro call + #[ allow( clippy::redundant_closure_call, clippy::cast_possible_truncation, clippy::cast_sign_loss ) ] // ok because of it improve understanding what is `value` at macro call $value_kind( value ) => ( $cast )( value ), _ => panic!( "Unknown cast variant. Got `{value:?}` and try to cast to `{}`", stringify!( $kind ) ) } @@ -170,8 +174,8 @@ mod private { match value { - Value::List( value ) => value.into_iter().map( | x | x.into() ).collect(), - _ => panic!( "Unknown cast variant. Got `{value:?}` and try to cast to `Vec<{}>`", std::any::type_name::< T >() ) + Value::List( value ) => value.into_iter().map( std::convert::Into::into ).collect(), + _ => panic!( "Unknown cast variant. Got `{value:?}` and try to cast to `Vec<{}>`", core::any::type_name::< T >() ) } } } diff --git a/module/move/wca/src/ca/help.rs b/module/move/wca/src/ca/help.rs index 6a40e28e1c..b48a8ed93c 100644 --- a/module/move/wca/src/ca/help.rs +++ b/module/move/wca/src/ca/help.rs @@ -1,5 +1,7 @@ +#[ allow( clippy::std_instead_of_alloc, clippy::std_instead_of_core ) ] mod private { + #[ allow( clippy::wildcard_imports ) ] use crate::*; use ca:: { @@ -77,6 +79,10 @@ mod private /// This function takes a `Dictionary` of terms or commands and a `HelpGeneratorOptions` /// struct to customize the help output, generating a user-friendly help message /// or guide in `String` format. + /// # Panics + /// qqq: doc + #[ must_use ] + #[ allow( clippy::match_same_arms ) ] pub fn generate_help_content( dictionary : &Dictionary, o : HelpGeneratorOptions< '_ > ) -> String { struct Row @@ -101,15 +107,15 @@ mod private }; let subjects = match o.subject_detailing { - LevelOfDetail::None => "".into(), - _ if command.subjects.is_empty() => "".into(), + LevelOfDetail::None => String::new(), + _ if command.subjects.is_empty() => String::new(), LevelOfDetail::Simple => "< subjects >".into(), LevelOfDetail::Detailed => command.subjects.iter().map( | v | format!( "< {}{:?} >", if v.optional { "?" } else { "" }, v.kind ) ).collect::< Vec< _ > >().join( " " ), }; let properties = match o.property_detailing { - LevelOfDetail::None => "".into(), - _ if command.subjects.is_empty() => "".into(), + LevelOfDetail::None => String::new(), + _ if command.subjects.is_empty() => String::new(), LevelOfDetail::Simple => "< properties >".into(), LevelOfDetail::Detailed => command.properties( dictionary.order ).iter().map( |( n, v )| format!( "< {}:{}{:?} >", if v.optional { "?" } else { "" }, n, v.kind ) ).collect::< Vec< _ > >().join( " " ), }; @@ -122,10 +128,10 @@ mod private format! ( "{}{}", - if command.subjects.is_empty() { "".to_string() } else { format!( "\nSubjects:\n\t{}", &full_subjects ) }, - if command.properties.is_empty() { "".to_string() } else { format!( "\nProperties:\n\t{}",&full_properties ) } + if command.subjects.is_empty() { String::new() } else { format!( "\nSubjects:\n\t{}", &full_subjects ) }, + if command.properties.is_empty() { String::new() } else { format!( "\nProperties:\n\t{}",&full_properties ) } ) - } else { "".into() }; + } else { String::new() }; Row { @@ -178,6 +184,7 @@ mod private impl HelpVariants { /// Generates help commands + #[ allow( clippy::match_wildcard_for_single_variants ) ] pub fn generate( &self, helper : &HelpGeneratorFn, dictionary : &mut Dictionary, order : Order ) { match self @@ -196,6 +203,7 @@ mod private } // .help + #[ allow( clippy::unused_self ) ] fn general_help( &self, helper : &HelpGeneratorFn, dictionary : &mut Dictionary, order : Order ) { let phrase = "help".to_string(); @@ -260,6 +268,7 @@ mod private } // .help command_name + #[ allow( clippy::unused_self ) ] fn subject_command_help( &self, helper : &HelpGeneratorFn, dictionary : &mut Dictionary ) { let phrase = "help".to_string(); @@ -410,15 +419,16 @@ mod private impl HelpGeneratorFn { /// Executes the function to generate help content + #[ must_use ] pub fn exec( &self, dictionary : &Dictionary, args : HelpGeneratorOptions< '_ > ) -> String { self.0( dictionary, args ) } } - impl std::fmt::Debug for HelpGeneratorFn + impl core::fmt::Debug for HelpGeneratorFn { - fn fmt( &self, f : &mut std::fmt::Formatter< '_ > ) -> std::fmt::Result + fn fmt( &self, f : &mut core::fmt::Formatter< '_ > ) -> core::fmt::Result { f.write_str( "HelpGenerator" ) } diff --git a/module/move/wca/src/ca/input.rs b/module/move/wca/src/ca/input.rs index 46f70221b8..34d57ba2c9 100644 --- a/module/move/wca/src/ca/input.rs +++ b/module/move/wca/src/ca/input.rs @@ -3,10 +3,11 @@ mod private use std::io::{ self, Write }; /// Ask use input from standard input. + #[ must_use ] pub fn ask( request : &str ) -> String { let mut response = String::new(); - print!( "{} : ", request ); + print!( "{request} : " ); io::stdout().flush().ok(); io::stdin().read_line( &mut response ).ok(); response.trim().to_string() diff --git a/module/move/wca/src/ca/parser/command.rs b/module/move/wca/src/ca/parser/command.rs index 84cfbefc2b..9d75b11655 100644 --- a/module/move/wca/src/ca/parser/command.rs +++ b/module/move/wca/src/ca/parser/command.rs @@ -39,7 +39,7 @@ mod private /// }; /// ``` /// - /// In the above example, a `ParsedCommand` instance is created with the name "command", a single subject "subject_value", and one property "prop_name" with a raw value of "raw_prop_value". + /// In the above example, a `ParsedCommand` instance is created with the name "command", a single subject "`subject_value`", and one property "`prop_name`" with a raw value of "`raw_prop_value`". /// #[ derive( Default, Debug, Clone, PartialEq, Eq ) ] pub struct ParsedCommand diff --git a/module/move/wca/src/ca/parser/parser.rs b/module/move/wca/src/ca/parser/parser.rs index 0fec9fb6f4..4bee7321d0 100644 --- a/module/move/wca/src/ca/parser/parser.rs +++ b/module/move/wca/src/ca/parser/parser.rs @@ -1,5 +1,6 @@ mod private { + #[ allow( clippy::wildcard_imports ) ] use crate::*; use std::collections::HashMap; @@ -32,6 +33,8 @@ mod private /// # Returns /// /// Returns a `Result` with a `Program` containing the parsed commands if successful, or an error if parsing fails. + /// # Errors + /// qqq: doc // aaa : use typed error // aaa : done. pub fn parse< As, A >( &self, args : As ) -> Result< Program< ParsedCommand >, ParserError > @@ -57,7 +60,7 @@ mod private { if let Some( name ) = input.strip_prefix( '.' ) { - name.is_empty() || name.starts_with( '?' ) || name.chars().next().is_some_and( | c | c.is_alphanumeric() ) + name.is_empty() || name.starts_with( '?' ) || name.chars().next().is_some_and( char::is_alphanumeric ) } else { @@ -90,7 +93,7 @@ mod private i += relative_pos; - return Ok( + Ok( ( ParsedCommand { diff --git a/module/move/wca/src/ca/tool/table.rs b/module/move/wca/src/ca/tool/table.rs index 4315ee5c8e..b3bce748d5 100644 --- a/module/move/wca/src/ca/tool/table.rs +++ b/module/move/wca/src/ca/tool/table.rs @@ -1,5 +1,6 @@ mod private { + #[ allow( clippy::wildcard_imports ) ] use crate::*; // use wtools::error::{ Result, err }; @@ -69,7 +70,7 @@ mod private fn max_column_lengths( table : &Table ) -> Vec< usize > { - let num_columns = table.0.get( 0 ).map_or( 0, | row | row.0.len() ); + let num_columns = table.0.first().map_or( 0, | row | row.0.len() ); ( 0 .. num_columns ) .map( | column_index | { @@ -94,6 +95,8 @@ mod private /// # Returns /// /// * `error::untyped::Result` - A `error::untyped::Result` containing the formatted table as a `String`, or an `Error` if the table is invalid. + /// # Errors + /// qqq: doc // aaa : use typed error // aaa : done pub fn format_table< IntoTable >( table : IntoTable ) -> Result< String, FormatTableError > diff --git a/module/move/wca/src/ca/verifier/command.rs b/module/move/wca/src/ca/verifier/command.rs index c945bbb997..f52d54c897 100644 --- a/module/move/wca/src/ca/verifier/command.rs +++ b/module/move/wca/src/ca/verifier/command.rs @@ -1,5 +1,6 @@ mod private { + #[ allow( clippy::wildcard_imports ) ] use crate::*; use executor::{ Args, Props }; @@ -23,7 +24,7 @@ mod private /// }; /// ``` /// - /// In the above example, a `VerifiedCommand` instance is created with the name "command", a single subject "subject_value", and one property "prop_name" with a typed values. + /// In the above example, a `VerifiedCommand` instance is created with the name "command", a single subject "`subject_value`", and one property "`prop_name`" with a typed values. /// #[ derive( Debug, Clone ) ] pub struct VerifiedCommand diff --git a/module/move/wca/src/ca/verifier/verifier.rs b/module/move/wca/src/ca/verifier/verifier.rs index c0636a594b..db3de37923 100644 --- a/module/move/wca/src/ca/verifier/verifier.rs +++ b/module/move/wca/src/ca/verifier/verifier.rs @@ -1,5 +1,6 @@ mod private { + #[ allow( clippy::wildcard_imports ) ] use crate::*; use help::{ HelpGeneratorOptions, LevelOfDetail, generate_help_content }; @@ -78,6 +79,8 @@ mod private /// Converts raw program to grammatically correct /// /// Converts all namespaces into it with `to_namespace` method. + /// # Errors + /// qqq: doc pub fn to_program ( &self, @@ -183,8 +186,9 @@ mod private Ok( subjects ) } - // aaa : use typed error - // aaa : done. + // aaa : use typed error + // aaa : done. + #[ allow( clippy::manual_map ) ] fn extract_properties( command: &Command, raw_command : HashMap< String, String > ) -> Result< HashMap< String, Value >, PropertyError > @@ -224,7 +228,7 @@ mod private used_keys.flat_map( | key | { - reverse_aliases.get( key ).into_iter().flatten().map( | k | *k ).chain( Some( key ) ) + reverse_aliases.get( key ).into_iter().flatten().copied().chain( Some( key ) ) }) .collect() } @@ -232,6 +236,10 @@ mod private /// Converts raw command to grammatically correct /// /// Make sure that this command is described in the grammar and matches it(command itself and all it options too). + /// # Errors + /// qqq: doc + /// # Panics + /// qqq: doc // aaa : use typed error // aaa : done. pub fn to_command( &self, dictionary : &Dictionary, raw_command : ParsedCommand ) @@ -277,7 +285,7 @@ mod private Ok( VerifiedCommand { - phrase : cmd.phrase.to_owned(), + phrase : cmd.phrase.clone(), internal_command : false, args : Args( subjects ), props : Props( properties ), diff --git a/module/move/willbe/Cargo.toml b/module/move/willbe/Cargo.toml index 4f16918129..e647b11d88 100644 --- a/module/move/willbe/Cargo.toml +++ b/module/move/willbe/Cargo.toml @@ -61,12 +61,12 @@ flate2 = "~1.0" globwalk = "~0.8" toml_edit = "~0.14" petgraph = "~0.6" -ptree = "~0.4" +#ptree = "~0.4" rayon = "1.8.0" semver = "~1.0.0" similar = "~2.4" regex = "1.10.2" -sha-1 = "~0.10" +#sha-1 = "~0.10" tar = "~0.4" handlebars = "4.5.0" ureq = "~2.9" diff --git a/module/move/willbe/Readme.md b/module/move/willbe/Readme.md index b387b877c6..c7d2a441b9 100644 --- a/module/move/willbe/Readme.md +++ b/module/move/willbe/Readme.md @@ -1,6 +1,6 @@ -# Module:: willbe +# `Module`:: willbe [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) [![rust-status](https://github.com/Wandalen/wTools/actions/workflows/module_willbe_push.yml/badge.svg)](https://github.com/Wandalen/wTools/actions/workflows/module_willbe_push.yml) [![docs.rs](https://img.shields.io/docsrs/willbe?color=e3e8f0&logo=docs.rs)](https://docs.rs/willbe) [![discord](https://img.shields.io/discord/872391416519737405?color=eee&logo=discord&logoColor=eee&label=ask)](https://discord.gg/m3YfbXpUUY) diff --git a/module/move/willbe/src/action/cicd_renew.rs b/module/move/willbe/src/action/cicd_renew.rs index 5814e2802c..ca46b77549 100644 --- a/module/move/willbe/src/action/cicd_renew.rs +++ b/module/move/willbe/src/action/cicd_renew.rs @@ -1,5 +1,6 @@ mod private { + #[ allow( clippy::wildcard_imports ) ] use crate::*; use std:: @@ -42,6 +43,12 @@ mod private // qqq : for Petro : should return Report and typed error in Result /// Generate workflows for modules in .github/workflows directory. + /// # Errors + /// qqq: doc + /// + /// # Panics + /// qqq: doc + #[ allow( clippy::too_many_lines, clippy::result_large_err ) ] pub fn action( base_path : &Path ) -> Result< (), CiCdGenerateError > { let workspace_cache = Workspace::try_from( CrateDir::try_from( base_path )? )?; @@ -131,13 +138,13 @@ mod private data.insert( "name", name.as_str() ); data.insert( "username_and_repository", username_and_repository.0.as_str() ); data.insert( "branch", "alpha" ); - let manifest_file = manifest_file.to_string_lossy().replace( "\\", "/" ); + let manifest_file = manifest_file.to_string_lossy().replace( '\\', "/" ); let manifest_file = manifest_file.trim_start_matches( '/' ); data.insert( "manifest_path", manifest_file ); let content = handlebars.render( "module_push", &data )?; file_write( &workflow_file_name, &content )?; - println!( "file_write : {:?}", &workflow_file_name ) + println!( "file_write : {:?}", &workflow_file_name ); } dbg!( &workflow_root ); @@ -306,7 +313,7 @@ mod private Ok( () ) } - /// Prepare params for render appropriative_branch_for template. + /// Prepare params for render `appropriative_branch_for` template. fn map_prepare_for_appropriative_branch< 'a > ( branches : &'a str, @@ -333,7 +340,7 @@ mod private { match std::fs::create_dir_all( folder ) { - Ok( _ ) => {}, + Ok( () ) => {}, Err( e ) if e.kind() == std::io::ErrorKind::AlreadyExists => {}, Err( e ) => return Err( e.into() ), } @@ -372,7 +379,7 @@ mod private .map( String::from ); if let Some( url ) = url { - return url::repo_url_extract( &url ) + url::repo_url_extract( &url ) .and_then( | url | url::git_info_extract( &url ).ok() ) .map( UsernameAndRepository ) .ok_or_else( || error::untyped::format_err!( "Fail to parse repository url from workspace Cargo.toml")) @@ -389,7 +396,7 @@ mod private break; } } - return url + url .and_then( | url | url::repo_url_extract( &url ) ) .and_then( | url | url::git_info_extract( &url ).ok() ) .map( UsernameAndRepository ) diff --git a/module/move/willbe/src/action/deploy_renew.rs b/module/move/willbe/src/action/deploy_renew.rs index 0f1c965332..c0681cc6c7 100644 --- a/module/move/willbe/src/action/deploy_renew.rs +++ b/module/move/willbe/src/action/deploy_renew.rs @@ -1,8 +1,10 @@ mod private { + #[ allow( clippy::wildcard_imports ) ] use crate::*; use std::path::Path; use error::{ untyped::Context }; + #[ allow( clippy::wildcard_imports ) ] use tool::template::*; /// Template for creating deploy files. @@ -17,6 +19,8 @@ mod private /// Creates am instance of `[TemplateHolder]` for deployment template. /// /// Used for properly initializing a template + #[ must_use ] + #[ allow( clippy::should_implement_trait ) ] pub fn default() -> TemplateHolder { let parameters = TemplateParameters::former() @@ -30,7 +34,7 @@ mod private { files : get_deploy_template_files(), parameters, - values : Default::default(), + values : TemplateValues::default(), parameter_storage : "./.deploy_template.toml".as_ref(), template_name : "deploy", } @@ -79,12 +83,13 @@ mod private fn dir_name_to_formatted( dir_name : &str, separator : &str ) -> String { dir_name - .replace( ' ', separator ) - .replace( '_', separator ) + .replace( [ ' ', '_' ], separator ) .to_lowercase() } /// Creates deploy template + /// # Errors + /// qqq: doc pub fn deploy_renew ( path : &Path, @@ -93,7 +98,7 @@ mod private -> error::untyped::Result< () > // qqq : typed error { - if let None = template.load_existing_params( path ) + if template.load_existing_params( path ).is_none() { let current_dir = std::env::current_dir()?; // qqq : for Petro : use file_name diff --git a/module/move/willbe/src/action/features.rs b/module/move/willbe/src/action/features.rs index ca7312d289..2d46a8a882 100644 --- a/module/move/willbe/src/action/features.rs +++ b/module/move/willbe/src/action/features.rs @@ -1,5 +1,7 @@ +#[ allow( clippy::std_instead_of_alloc, clippy::std_instead_of_core ) ] mod private { + #[ allow( clippy::wildcard_imports ) ] use crate::*; use std:: @@ -39,12 +41,13 @@ mod private impl fmt::Display for FeaturesReport { + #[ allow( clippy::match_bool ) ] fn fmt( &self, f : &mut fmt::Formatter< '_ >) -> Result< (), fmt::Error > { self.inner.iter().try_for_each ( | ( package, features ) | { - writeln!(f, "Package {}:", package)?; + writeln!(f, "Package {package}:")?; features.iter().try_for_each ( | ( feature, dependencies ) | { @@ -67,6 +70,8 @@ mod private } /// List features + /// # Errors + /// qqq: doc pub fn features( FeaturesOptions { crate_dir, with_features_deps } : FeaturesOptions ) -> error::untyped::Result< FeaturesReport > // qqq : typed error diff --git a/module/move/willbe/src/action/list.rs b/module/move/willbe/src/action/list.rs index c747532f6d..5bd66e940c 100644 --- a/module/move/willbe/src/action/list.rs +++ b/module/move/willbe/src/action/list.rs @@ -1,6 +1,8 @@ /// Define a private namespace for all its items. +#[ allow( clippy::std_instead_of_alloc, clippy::std_instead_of_core ) ] mod private { + #[ allow( clippy::wildcard_imports ) ] use crate::*; use std::{ fmt, str }; @@ -285,7 +287,7 @@ mod private ( f, "{}", - v.iter().map( | l | l.to_string() ).collect::< Vec< _ > >().join( "\n" ) + v.iter().map( std::string::ToString::to_string ).collect::< Vec< _ > >().join( "\n" ) ), Self::List( v ) => @@ -321,6 +323,7 @@ mod private pub path : Option< ManifestFile >, } + #[ allow( clippy::trivially_copy_pass_by_ref, clippy::needless_lifetimes ) ] fn process_package_dependency< 'a > ( workspace : &Workspace, @@ -347,7 +350,7 @@ mod private name : dependency.name(), // unwrap should be safe because of `semver::VersionReq` version : dependency.req(), - path : dependency.crate_dir().map( | p | p.manifest_file() ), + path : dependency.crate_dir().map( CrateDir::manifest_file ), }; // format!( "{}+{}+{}", dependency.name(), dependency.req(), dependency.crate_dir().unwrap().manifest_file() ); // let dep_id = format!( "{}+{}+{}", dependency.name(), dependency.req(), dependency.path().as_ref().map( | p | p.join( "Cargo.toml" ) ).unwrap_or_default() ); @@ -402,7 +405,7 @@ mod private name : dep.name(), // unwrap should be safe because of `semver::VersionReq` version : dep.req(), - path : dep.crate_dir().map( | p | p.manifest_file() ), + path : dep.crate_dir().map( CrateDir::manifest_file ), }; // if this is a cycle (we have visited this node before) if visited.contains( &dep_id ) @@ -542,7 +545,7 @@ mod private .collect(); for package in packages { - tree_package_report( package.manifest_file().unwrap(), &mut report, &mut visited )? + tree_package_report( package.manifest_file().unwrap(), &mut report, &mut visited )?; } let ListReport::Tree( tree ) = report else { unreachable!() }; let printer = merge_build_dependencies( tree ); @@ -626,12 +629,12 @@ mod private { if args.info.contains( &PackageAdditionalInfo::Version ) { - name.push_str( " " ); + name.push( ' ' ); name.push_str( &p.version().to_string() ); } if args.info.contains( &PackageAdditionalInfo::Path ) { - name.push_str( " " ); + name.push( ' ' ); name.push_str( &p.manifest_file()?.to_string() ); // aaa : is it safe to use unwrap here? // aaa : should be safe, but now returns an error } @@ -679,12 +682,12 @@ mod private { if args.info.contains( &PackageAdditionalInfo::Version ) { - name.push_str( " " ); + name.push( ' ' ); name.push_str( &p.version().to_string() ); } if args.info.contains( &PackageAdditionalInfo::Path ) { - name.push_str( " " ); + name.push( ' ' ); name.push_str( &p.manifest_file().unwrap().to_string() ); } } @@ -757,7 +760,7 @@ mod private } let printer : Vec< TreePrinter > = report .iter() - .map( | rep | TreePrinter::new( rep ) ) + .map( TreePrinter::new ) .collect(); printer } @@ -789,15 +792,15 @@ mod private fn rearrange_duplicates( mut report : Vec< tool::ListNodeReport > ) -> Vec< tool::TreePrinter > { let mut required_normal : collection::HashMap< usize, Vec< tool::ListNodeReport > > = collection::HashMap::new(); - for i in 0 .. report.len() + for (i, report) in report.iter_mut().enumerate() { let ( required, exist ) : ( Vec< _ >, Vec< _ > ) = std::mem::take ( - &mut report[ i ].normal_dependencies + &mut report.normal_dependencies ) .into_iter() .partition( | d | d.duplicate ); - report[ i ].normal_dependencies = exist; + report.normal_dependencies = exist; required_normal.insert( i, required ); } @@ -809,7 +812,7 @@ mod private let printer : Vec< TreePrinter > = report .iter() - .map( | rep | TreePrinter::new( rep ) ) + .map( TreePrinter::new ) .collect(); printer diff --git a/module/move/willbe/src/action/main_header.rs b/module/move/willbe/src/action/main_header.rs index 05ca9ec085..41241c2ffb 100644 --- a/module/move/willbe/src/action/main_header.rs +++ b/module/move/willbe/src/action/main_header.rs @@ -1,5 +1,7 @@ +#[ allow( clippy::std_instead_of_alloc, clippy::std_instead_of_core ) ] mod private { + #[ allow( clippy::wildcard_imports ) ] use crate::*; use std::fmt::{ Display, Formatter }; use std::fs:: @@ -16,6 +18,7 @@ mod private use std::path::PathBuf; use regex::Regex; use entity::{ PathError, WorkspaceInitError }; + #[ allow( unused_imports ) ] use error:: { // err, @@ -48,6 +51,7 @@ mod private impl Display for MainHeaderRenewReport { + #[ allow( clippy::collapsible_else_if ) ] fn fmt( &self, f : &mut Formatter< '_ > ) -> std::fmt::Result { if self.success @@ -140,6 +144,7 @@ mod private } /// Convert `Self`to header. + #[ allow( clippy::uninlined_format_args, clippy::wrong_self_convention ) ] fn to_header( self ) -> Result< String, MainHeaderRenewError > { let discord = self.discord_url @@ -193,6 +198,13 @@ mod private /// [![docs.rs](https://raster.shields.io/static/v1?label=docs&message=online&color=eee&logo=docsdotrs&logoColor=eee)](https://docs.rs/wtools) /// /// ``` + /// + /// # Errors + /// qqq: doc + /// + /// # Panics + /// qqq: doc + #[ allow( clippy::uninlined_format_args ) ] pub fn action( crate_dir : CrateDir ) // -> Result< MainHeaderRenewReport, ( MainHeaderRenewReport, MainHeaderRenewError ) > -> ResultWithReport< MainHeaderRenewReport, MainHeaderRenewError > diff --git a/module/move/willbe/src/action/publish.rs b/module/move/willbe/src/action/publish.rs index 6b8f4dc657..ed6a6c90f2 100644 --- a/module/move/willbe/src/action/publish.rs +++ b/module/move/willbe/src/action/publish.rs @@ -1,6 +1,8 @@ /// Define a private namespace for all its items. +#[ allow( clippy::std_instead_of_alloc, clippy::std_instead_of_core ) ] mod private { + #[ allow( clippy::wildcard_imports ) ] use crate::*; use std::{ env, fmt, fs }; @@ -34,10 +36,10 @@ mod private writeln!( f, "Actions :" )?; for ( path, report ) in &self.packages { - let report = report.to_string().replace("\n", "\n "); + let report = report.to_string().replace('\n', "\n "); let path = if let Some( wrd ) = &self.workspace_root_dir { - path.as_ref().strip_prefix( &wrd.as_ref() ).unwrap() + path.as_ref().strip_prefix( wrd.as_ref() ).unwrap() } else { @@ -158,7 +160,7 @@ mod private let workspace_root_dir : AbsolutePath = workspace .workspace_root() - .try_into()?; + .into(); let packages = workspace.packages(); let packages_to_publish : Vec< String > = packages diff --git a/module/move/willbe/src/action/publish_diff.rs b/module/move/willbe/src/action/publish_diff.rs index a6b76e6528..69b98be647 100644 --- a/module/move/willbe/src/action/publish_diff.rs +++ b/module/move/willbe/src/action/publish_diff.rs @@ -1,6 +1,8 @@ /// Define a private namespace for all its items. +#[ allow( clippy::std_instead_of_alloc, clippy::std_instead_of_core ) ] mod private { + #[ allow( clippy::wildcard_imports ) ] use crate::*; use path::PathBuf; @@ -43,6 +45,7 @@ mod private let root_name = tree.name.clone(); let root_version = tree.version.as_ref().unwrap().clone(); + #[ allow( clippy::items_after_statements, clippy::option_map_unit_fn ) ] fn modify( diffs : &HashMap< AbsolutePath, DiffReport >, tree : &mut ListNodeReport ) { let path = tree.crate_dir.take().unwrap(); @@ -75,7 +78,7 @@ mod private for dep in &mut tree.normal_dependencies { - modify( diffs, dep ) + modify( diffs, dep ); } } modify( &self.diffs, &mut tree ); @@ -83,7 +86,7 @@ mod private let root = AbsolutePath::from( root_path ); let diff = self.diffs.get( &root ).unwrap(); let printer = TreePrinter::new( &tree ); - writeln!( f, "Tree:\n{}", printer )?; + writeln!( f, "Tree:\n{printer}" )?; if diff.has_changes() { writeln!( f, "Changes detected in `{root_name} {root_version}`:" )?; @@ -92,7 +95,7 @@ mod private { writeln!( f, "No changes found in `{root_name} {root_version}`. Files:" )?; } - write!( f, "{}", diff )?; + write!( f, "{diff}" )?; Ok( () ) } @@ -157,7 +160,7 @@ mod private if let Some( out_path ) = &o.keep_archive { - _ = std::fs::create_dir_all( &out_path ); + _ = std::fs::create_dir_all( out_path ); for path in r.list() { let local_path = out_path.join( path ); @@ -173,7 +176,7 @@ mod private let report = tasks[ current_idx ].info.normal_dependencies.clone(); let printer : Vec< TreePrinter > = report .iter() - .map( | rep | TreePrinter::new( rep ) ) + .map( TreePrinter::new ) .collect(); tasks.extend( printer ); diff --git a/module/move/willbe/src/action/readme_health_table_renew.rs b/module/move/willbe/src/action/readme_health_table_renew.rs index 438c44dcd8..dff994b1d9 100644 --- a/module/move/willbe/src/action/readme_health_table_renew.rs +++ b/module/move/willbe/src/action/readme_health_table_renew.rs @@ -1,5 +1,7 @@ +#[ allow( clippy::std_instead_of_alloc, clippy::std_instead_of_core ) ] mod private { + #[ allow( clippy::wildcard_imports ) ] use crate::*; use std:: @@ -37,14 +39,14 @@ mod private ( regex::bytes::Regex::new ( - r#""# + r"" ).unwrap() ).ok(); CLOSE_TAG.set ( regex::bytes::Regex::new ( - r#""# + r"" ).unwrap() ).ok(); } @@ -131,6 +133,7 @@ mod private /// Structure that holds the parameters for generating a table. #[ derive( Debug ) ] + #[ allow( clippy::struct_excessive_bools ) ] struct TableOptions { // Relative path from workspace root to directory with modules @@ -151,23 +154,19 @@ mod private { let include_branches = value .get( "with_branches" ) - .map( | v | bool::from( v ) ) - .unwrap_or( true ); + .map_or( true, bool::from ); let include_stability = value .get( "with_stability" ) - .map( | v | bool::from( v ) ) - .unwrap_or( true ); + .map_or( true, bool::from ); let include_docs = value .get( "with_docs" ) - .map( | v | bool::from( v ) ) - .unwrap_or( true ); + .map_or( true, bool::from ); let include = value .get( "with_gitpod" ) - .map( | v | bool::from( v ) ) - .unwrap_or( true ); + .map_or( true, bool::from ); let b_p = value.get( "1" ); let base_path = if let Some( query::Value::String( path ) ) = value.get( "path" ).or( b_p ) @@ -200,51 +199,49 @@ mod private { return Err( HealthTableRenewError::Common( error::untyped::Error::msg( "Cannot find Cargo.toml" ))) } - else + + let mut contents = String::new(); + File::open( cargo_toml_path )?.read_to_string( &mut contents )?; + let doc = contents.parse::< Document >()?; + + let core_url = + doc + .get( "workspace" ) + .and_then( | workspace | workspace.get( "metadata" ) ) + .and_then( | metadata | metadata.get( "repo_url" ) ) + .and_then( | url | url.as_str() ) + .map( String::from ); + + let branches = + doc + .get( "workspace" ) + .and_then( | workspace | workspace.get( "metadata" ) ) + .and_then( | metadata | metadata.get( "branches" ) ) + .and_then( | branches | branches.as_array()) + .map + ( + | array | + array + .iter() + .filter_map( | value | value.as_str() ) + .map( String::from ) + .collect::< Vec< String > >() + ); + let mut user_and_repo = String::new(); + if let Some( core_url ) = &core_url { - let mut contents = String::new(); - File::open( cargo_toml_path )?.read_to_string( &mut contents )?; - let doc = contents.parse::< Document >()?; - - let core_url = - doc - .get( "workspace" ) - .and_then( | workspace | workspace.get( "metadata" ) ) - .and_then( | metadata | metadata.get( "repo_url" ) ) - .and_then( | url | url.as_str() ) - .map( String::from ); - - let branches = - doc - .get( "workspace" ) - .and_then( | workspace | workspace.get( "metadata" ) ) - .and_then( | metadata | metadata.get( "branches" ) ) - .and_then( | branches | branches.as_array()) - .map - ( - | array | - array - .iter() - .filter_map( | value | value.as_str() ) - .map( String::from ) - .collect::< Vec< String > >() - ); - let mut user_and_repo = "".to_string(); - if let Some( core_url ) = &core_url + user_and_repo = url::git_info_extract( core_url )?; + } + Ok + ( + Self { - user_and_repo = url::git_info_extract( core_url )?; + core_url : core_url.unwrap_or_default(), + user_and_repo, + branches, + workspace_root : path.to_path_buf() } - Ok - ( - Self - { - core_url : core_url.unwrap_or_default(), - user_and_repo, - branches, - workspace_root : path.to_path_buf() - } - ) - } + ) } } @@ -259,6 +256,12 @@ mod private /// will mean that at this place the table with modules located in the directory module/core will be generated. /// The tags do not disappear after generation. /// Anything between the opening and closing tag will be destroyed. + /// + /// # Errors + /// qqq: doc + /// + /// # Panics + /// qqq: doc // aaa : for Petro : typed errors // aaa : done pub fn readme_health_table_renew( path : &Path ) -> Result< (), HealthTableRenewError > @@ -284,8 +287,8 @@ mod private let mut tags_closures = vec![]; let mut tables = vec![]; - let open_caps = TAG_TEMPLATE.get().unwrap().captures_iter( &*contents ); - let close_caps = CLOSE_TAG.get().unwrap().captures_iter( &*contents ); + let open_caps = TAG_TEMPLATE.get().unwrap().captures_iter( &contents ); + let close_caps = CLOSE_TAG.get().unwrap().captures_iter( &contents ); // iterate by regex matches and generate table content for each dir which taken from open-tag for ( open_captures, close_captures ) in open_caps.zip( close_caps ) { @@ -324,6 +327,7 @@ mod private } /// Writes tables into a file at specified positions. + #[ allow( clippy::needless_pass_by_value ) ] fn tables_write_into_file ( tags_closures : Vec< ( usize, usize ) >, @@ -341,11 +345,11 @@ mod private ) in tags_closures.iter().zip( tables.iter() ) { - range_to_target_copy( &*contents, &mut buffer, start, *end_of_start_tag )?; + range_to_target_copy( &contents, &mut buffer, start, *end_of_start_tag )?; range_to_target_copy( con.as_bytes(), &mut buffer, 0,con.len() - 1 )?; start = *start_of_end_tag; } - range_to_target_copy( &*contents,&mut buffer,start,contents.len() - 1 )?; + range_to_target_copy( &contents,&mut buffer,start,contents.len() - 1 )?; file.set_len( 0 )?; file.seek( SeekFrom::Start( 0 ) )?; file.write_all( &buffer )?; @@ -353,7 +357,7 @@ mod private } /// Generate table from `table_parameters`. - /// Generate header, iterate over all modules in package (from table_parameters) and append row. + /// Generate header, iterate over all modules in package (from `table_parameters`) and append row. fn package_readme_health_table_generate ( workspace : &Workspace, @@ -369,7 +373,7 @@ mod private workspace .packages() )?; - let mut table = table_header_generate( parameters, &table_parameters ); + let mut table = table_header_generate( parameters, table_parameters ); for package_name in directory_names { let stability = if table_parameters.include_stability @@ -388,7 +392,7 @@ mod private { None }; - if parameters.core_url == "" + if parameters.core_url.is_empty() { let module_path = workspace .workspace_root() @@ -420,7 +424,7 @@ ensure that at least one remotest is present in git. ", &package_name, stability.as_ref(), parameters, - &table_parameters + table_parameters ) ); } @@ -429,6 +433,7 @@ ensure that at least one remotest is present in git. ", /// Return topologically sorted modules name, from packages list, in specified directory. // fn directory_names( path : PathBuf, packages : &[ WorkspacePackageRef< '_ > ] ) -> Result< Vec< String > > + #[ allow( clippy::type_complexity, clippy::unnecessary_wraps ) ] fn directory_names< 'a > ( path : PathBuf, @@ -478,7 +483,7 @@ ensure that at least one remotest is present in git. ", let module_graph = graph::construct( &module_packages_map ); let names : Vec< String > = graph::topological_sort_with_grouping( module_graph ) .into_iter() - .map + .flat_map ( | mut group | { @@ -486,7 +491,6 @@ ensure that at least one remotest is present in git. ", group } ) - .flatten() .map( | n | n.to_string() ) .collect(); @@ -511,13 +515,13 @@ ensure that at least one remotest is present in git. ", ); if table_parameters.include_stability { - let mut stability = stability_generate( &stability.as_ref().unwrap() ); + let mut stability = stability_generate( stability.as_ref().unwrap() ); stability.push_str( " |" ); rou.push_str( &stability ); } if parameters.branches.is_some() && table_parameters.include_branches { - rou.push_str( &branch_cells_generate( ¶meters, &module_name ) ); + rou.push_str( &branch_cells_generate( parameters, module_name ) ); } if table_parameters.include_docs { @@ -532,12 +536,12 @@ ensure that at least one remotest is present in git. ", } if table_parameters.include { - let path = Path::new( table_parameters.base_path.as_str() ).join( &module_name ); + let path = Path::new( table_parameters.base_path.as_str() ).join( module_name ); let p = Path::new( ¶meters.workspace_root ).join( &path ); // let path = table_parameters.base_path. - let example = if let Some( name ) = find_example_file( p.as_path(), &module_name ) + let example = if let Some( name ) = find_example_file( p.as_path(), module_name ) { - let path = path.to_string_lossy().replace( '\\', "/" ).replace( "/", "%2F" ); + let path = path.to_string_lossy().replace( '\\', "/" ).replace( '/', "%2F" ); let tmp = name.to_string_lossy().replace( '\\', "/" ); let file_name = tmp.split( '/' ).last().unwrap(); let name = file_name.strip_suffix( ".rs" ).unwrap(); @@ -552,14 +556,15 @@ ensure that at least one remotest is present in git. ", } else { - "".into() + String::new() }; - rou.push_str( &format!( " {} |", example ) ); + rou.push_str( &format!( " {example} |" ) ); } format!( "{rou}\n" ) } /// todo + #[ must_use ] pub fn find_example_file( base_path : &Path, module_name : &str ) -> Option< PathBuf > { let examples_dir = base_path.join("examples" ); @@ -568,19 +573,18 @@ ensure that at least one remotest is present in git. ", { if let Ok( entries ) = std::fs::read_dir( &examples_dir ) { - for entry in entries + for entry in entries.flatten() { - if let Ok( entry ) = entry + + let file_name = entry.file_name(); + if let Some( file_name_str ) = file_name.to_str() { - let file_name = entry.file_name(); - if let Some( file_name_str ) = file_name.to_str() + if file_name_str == format!( "{module_name}_trivial.rs" ) { - if file_name_str == format!( "{module_name}_trivial.rs" ) - { - return Some( entry.path() ) - } + return Some( entry.path() ) } } + } } } @@ -588,19 +592,20 @@ ensure that at least one remotest is present in git. ", // If module_trivial.rs doesn't exist, return any other file in the examples directory if let Ok( entries ) = std::fs::read_dir( &examples_dir ) { - for entry in entries + for entry in entries.flatten() { - if let Ok( entry ) = entry + + let file_name = entry.file_name(); + if let Some( file_name_str ) = file_name.to_str() { - let file_name = entry.file_name(); - if let Some( file_name_str ) = file_name.to_str() + if std::path::Path::new( file_name_str ) + .extension() + .map_or( false, | ext | ext.eq_ignore_ascii_case( "rs" ) ) { - if file_name_str.ends_with( ".rs" ) - { - return Some( entry.path() ) - } + return Some( entry.path() ) } } + } } @@ -608,6 +613,7 @@ ensure that at least one remotest is present in git. ", } /// Generate stability cell based on stability + #[ must_use ] pub fn stability_generate( stability : &Stability ) -> String { match stability @@ -642,7 +648,7 @@ ensure that at least one remotest is present in git. ", { for branch in branches { - header.push_str( format!( " {} |", branch ).as_str() ); + header.push_str( format!( " {branch} |" ).as_str() ); separator.push_str( "--------|" ); } } @@ -660,7 +666,7 @@ ensure that at least one remotest is present in git. ", separator.push_str( ":------:|" ); } - format!( "{}\n{}\n", header, separator ) + format!( "{header}\n{separator}\n" ) } /// Generate cells for each branch @@ -703,10 +709,7 @@ ensure that at least one remotest is present in git. ", target.extend_from_slice( &source[ from..= to ] ); return Ok( () ) } - else - { - Err( HealthTableRenewError::Common( error::untyped::Error::msg( "Incorrect indexes" ))) - } + Err( HealthTableRenewError::Common( error::untyped::Error::msg( "Incorrect indexes" ))) } } diff --git a/module/move/willbe/src/action/readme_modules_headers_renew.rs b/module/move/willbe/src/action/readme_modules_headers_renew.rs index aeba473c74..33b12bc29b 100644 --- a/module/move/willbe/src/action/readme_modules_headers_renew.rs +++ b/module/move/willbe/src/action/readme_modules_headers_renew.rs @@ -1,5 +1,7 @@ +#[ allow( clippy::std_instead_of_alloc, clippy::std_instead_of_core ) ] mod private { + #[ allow( clippy::wildcard_imports ) ] use crate::*; use std:: { @@ -130,9 +132,10 @@ mod private { /// Create `ModuleHeader` instance from the folder where Cargo.toml is stored. - fn from_cargo_toml< 'a > + #[ allow( clippy::needless_pass_by_value ) ] + fn from_cargo_toml ( - package : Package< 'a >, + package : Package< '_ >, default_discord_url : &Option< String >, ) -> Result< Self, ModulesHeadersRenewError > @@ -159,6 +162,7 @@ mod private } /// Convert `ModuleHeader`to header. + #[ allow( clippy::uninlined_format_args, clippy::wrong_self_convention ) ] fn to_header( self, workspace_path : &str ) -> Result< String, ModulesHeadersRenewError > { let discord = self.discord_url.map( | discord_url | @@ -181,7 +185,7 @@ mod private { let relative_path = pth::path::path_relative ( - workspace_path.try_into().unwrap(), + workspace_path.into(), name ) .to_string_lossy() @@ -190,7 +194,7 @@ mod private let relative_path = relative_path.replace( "\\", "/" ); // aaa : for Petro : use path_toools // aaa : used - let p = relative_path.replace( "/","%2F" ); + let p = relative_path.replace( '/',"%2F" ); format! ( " [![Open in Gitpod](https://raster.shields.io/static/v1?label=try&message=online&color=eee&logo=gitpod&logoColor=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE={},RUN_POSTFIX=--example%20{}/https://github.com/{})", @@ -201,7 +205,7 @@ mod private } else { - "".into() + String::new() }; Ok( format! ( @@ -239,6 +243,12 @@ mod private /// [![experimental](https://raster.shields.io/static/v1?label=&message=experimental&color=orange)](https://github.com/emersion/stability-badges#experimental) | [![rust-status](https://github.com/Username/test/actions/workflows/ModuleChainOfPackagesAPush.yml/badge.svg)](https://github.com/Username/test/actions/workflows/ModuleChainOfPackagesAPush.yml)[![docs.rs](https://img.shields.io/docsrs/_chain_of_packages_a?color=e3e8f0&logo=docs.rs)](https://docs.rs/_chain_of_packages_a)[![Open in Gitpod](https://raster.shields.io/static/v1?label=try&message=online&color=eee&logo=gitpod&logoColor=eee)](https://gitpod.io/#RUN_PATH=.,SAMPLE_FILE=sample%2Frust%2F_chain_of_packages_a_trivial%2Fsrc%2Fmain.rs,RUN_POSTFIX=--example%20_chain_of_packages_a_trivial/https://github.com/Username/test) /// /// ``` + /// + /// # Errors + /// qqq: doc + /// + /// # Panics + /// qqq: doc pub fn readme_modules_headers_renew( crate_dir : CrateDir ) -> ResultWithReport< ModulesHeadersRenewReport, ModulesHeadersRenewError > // -> Result< ModulesHeadersRenewReport, ( ModulesHeadersRenewReport, ModulesHeadersRenewError ) > @@ -253,7 +263,7 @@ mod private let paths : Vec< AbsolutePath > = workspace .packages() - .filter_map( | p | p.manifest_file().ok().and_then( | a | Some( a.inner() ) ) ) + .filter_map( | p | p.manifest_file().ok().map( crate::entity::files::ManifestFile::inner ) ) .collect(); report.found_files = paths @@ -285,7 +295,7 @@ mod private ) .err_with_report( &report )?; - let header = ModuleHeader::from_cargo_toml( pakage.into(), &discord_url ) + let header = ModuleHeader::from_cargo_toml( pakage, &discord_url ) .err_with_report( &report )?; let mut file = OpenOptions::new() @@ -324,6 +334,7 @@ mod private Ok( report ) } + #[ allow( clippy::uninlined_format_args ) ] fn header_content_generate< 'a > ( content : &'a str, @@ -340,7 +351,7 @@ mod private .unwrap() .replace ( - &content, + content, &format! ( "\n{}\n", diff --git a/module/move/willbe/src/action/test.rs b/module/move/willbe/src/action/test.rs index 0d22053121..2e23df5108 100644 --- a/module/move/willbe/src/action/test.rs +++ b/module/move/willbe/src/action/test.rs @@ -1,6 +1,7 @@ /// Define a private namespace for all its items. mod private { + #[ allow( clippy::wildcard_imports ) ] use crate::*; use entity::test::{ TestPlan, TestOptions, TestsReport, tests_run }; @@ -31,6 +32,7 @@ mod private /// - The `exclude_features` field is a vector of strings representing the names of features to exclude when running tests. /// - The `include_features` field is a vector of strings representing the names of features to include when running tests. #[ derive( Debug, Former ) ] + #[ allow( clippy::struct_excessive_bools ) ] pub struct TestsCommandOptions { dir : AbsolutePath, @@ -63,8 +65,14 @@ mod private /// It is possible to enable and disable various features of the crate. /// The function also has the ability to run tests in parallel using `Rayon` crate. /// The result of the tests is written to the structure `TestsReport` and returned as a result of the function execution. + /// # Errors + /// qqq: doc + /// + /// # Panics + /// qqq: doc // zzz : it probably should not be here // xxx : use newtype + #[ allow( clippy::too_many_lines ) ] pub fn test( o : TestsCommandOptions, dry : bool ) -> ResultWithReport< TestsReport, Error > // qqq : for Petro : typed error @@ -123,6 +131,7 @@ Try to install it with `rustup install {}` command(-s)", data_type::Either::Right( manifest ) => CrateDir::from( manifest ) }; + #[ allow( clippy::useless_conversion ) ] let workspace = Workspace ::try_from( CrateDir::try_from( path.clone() ).err_with_report( &report )? ) .err_with_report( &report )? diff --git a/module/move/willbe/src/action/workspace_renew.rs b/module/move/willbe/src/action/workspace_renew.rs index 05936758d9..ee27334708 100644 --- a/module/move/willbe/src/action/workspace_renew.rs +++ b/module/move/willbe/src/action/workspace_renew.rs @@ -1,5 +1,7 @@ +#[ allow( clippy::std_instead_of_alloc, clippy::std_instead_of_core ) ] mod private { + #[ allow( clippy::wildcard_imports ) ] use crate::*; use std::fs; use std::path::Path; @@ -24,6 +26,7 @@ mod private impl WorkspaceTemplate { /// Returns template parameters + #[ must_use ] pub fn get_parameters( &self ) -> &TemplateParameters { &self.parameters @@ -41,9 +44,9 @@ mod private .form(); Self { - files : Default::default(), + files : WorkspaceTemplateFiles::default(), parameters, - values : Default::default(), + values : TemplateValues::default(), } } } @@ -134,6 +137,10 @@ mod private // qqq : for Petro : should return report // qqq : for Petro : should have typed error /// Creates workspace template + /// # Errors + /// qqq: doc + /// # Panics + /// qqq: doc pub fn action ( path : &Path, @@ -162,7 +169,7 @@ mod private "branches", wca::Value::String ( - branches.into_iter().map( | b | format!( r#""{}""#, b ) ).join( ", " ) + branches.into_iter().map( | b | format!( r#""{b}""# ) ).join( ", " ) ) ); template.files.create_all( path, &template.values )?; diff --git a/module/move/willbe/src/bin/cargo-will.rs b/module/move/willbe/src/bin/cargo-will.rs index 53aa39e51e..00c223060d 100644 --- a/module/move/willbe/src/bin/cargo-will.rs +++ b/module/move/willbe/src/bin/cargo-will.rs @@ -3,11 +3,11 @@ #![ doc( html_root_url = "https://docs.rs/willbe/" ) ] #![ doc = include_str!( concat!( env!( "CARGO_MANIFEST_DIR" ), "/", "Readme.md" ) ) ] -#[ allow( unused_imports ) ] +#[ allow( unused_imports, clippy::wildcard_imports ) ] use::willbe::*; fn main() -> Result< (), error::untyped::Error > { let args = std::env::args().skip( 1 ).collect(); - Ok( willbe::run( args )? ) + willbe::run( args ) } diff --git a/module/move/willbe/src/bin/will.rs b/module/move/willbe/src/bin/will.rs index cbaad31299..b4c1df035a 100644 --- a/module/move/willbe/src/bin/will.rs +++ b/module/move/willbe/src/bin/will.rs @@ -6,12 +6,12 @@ #![ doc( html_root_url = "https://docs.rs/willbe/" ) ] #![ doc = include_str!( concat!( env!( "CARGO_MANIFEST_DIR" ), "/", "Readme.md" ) ) ] -#[ allow( unused_imports ) ] +#[ allow( unused_imports, clippy::wildcard_imports ) ] use::willbe::*; fn main() -> Result< (), error::untyped::Error > { - Ok( willbe::run( std::env::args().collect() )? ) + willbe::run( std::env::args().collect() ) } // cargo_subcommand_metadata::description!( "xxx" ); diff --git a/module/move/willbe/src/bin/willbe.rs b/module/move/willbe/src/bin/willbe.rs index 5943573a67..1ad0cfeab7 100644 --- a/module/move/willbe/src/bin/willbe.rs +++ b/module/move/willbe/src/bin/willbe.rs @@ -3,10 +3,10 @@ #![ doc( html_root_url = "https://docs.rs/willbe/" ) ] #![ doc = include_str!( concat!( env!( "CARGO_MANIFEST_DIR" ), "/", "Readme.md" ) ) ] -#[ allow( unused_imports ) ] +#[ allow( unused_imports, clippy::wildcard_imports ) ] use::willbe::*; fn main() -> Result< (), error::untyped::Error > { - Ok( willbe::run( std::env::args().collect() )? ) + willbe::run( std::env::args().collect() ) } diff --git a/module/move/willbe/src/command/cicd_renew.rs b/module/move/willbe/src/command/cicd_renew.rs index c82b45b8da..07f7f53d24 100644 --- a/module/move/willbe/src/command/cicd_renew.rs +++ b/module/move/willbe/src/command/cicd_renew.rs @@ -1,5 +1,6 @@ mod private { + #[ allow( clippy::wildcard_imports ) ] use crate::*; use error::{ untyped::Context }; @@ -7,6 +8,8 @@ mod private /// /// Generate table. /// + /// # Errors + /// qqq: doc // qqq : typed error pub fn cicd_renew() -> error::untyped::Result< () > { diff --git a/module/move/willbe/src/command/deploy_renew.rs b/module/move/willbe/src/command/deploy_renew.rs index 7e1e68e476..36a90cdfe0 100644 --- a/module/move/willbe/src/command/deploy_renew.rs +++ b/module/move/willbe/src/command/deploy_renew.rs @@ -1,16 +1,21 @@ mod private { + #[ allow( clippy::wildcard_imports ) ] use crate::*; use wca::VerifiedCommand; use error::{ untyped::Context }; + #[ allow( clippy::wildcard_imports ) ] use action::deploy_renew::*; /// /// Create new deploy. /// + /// # Errors + /// qqq: doc // xxx : qqq : typed error + #[ allow( clippy::needless_pass_by_value ) ] pub fn deploy_renew( o : VerifiedCommand ) -> error::untyped::Result< () > { let current_dir = std::env::current_dir()?; diff --git a/module/move/willbe/src/command/features.rs b/module/move/willbe/src/command/features.rs index d57a8a7dc0..9fa2ec7596 100644 --- a/module/move/willbe/src/command/features.rs +++ b/module/move/willbe/src/command/features.rs @@ -1,5 +1,6 @@ mod private { + #[ allow( clippy::wildcard_imports ) ] use crate::*; use action::features::FeaturesOptions; @@ -13,7 +14,10 @@ mod private /// /// List features of a package. /// + /// # Errors + /// qqq: doc + #[ allow( clippy::needless_pass_by_value ) ] pub fn features( o : VerifiedCommand ) -> error::untyped::Result< () > // qqq : use typed error { let path : PathBuf = o.args.get_owned( 0 ).unwrap_or_else( || "./".into() ); diff --git a/module/move/willbe/src/command/list.rs b/module/move/willbe/src/command/list.rs index 7687b6db1d..d474c313b1 100644 --- a/module/move/willbe/src/command/list.rs +++ b/module/move/willbe/src/command/list.rs @@ -1,6 +1,8 @@ -/// Define a private namespace for all its items. +/// Internal namespace. +#[ allow( clippy::std_instead_of_alloc, clippy::std_instead_of_core ) ] mod private { + #[ allow( clippy::wildcard_imports ) ] use crate::*; use std:: @@ -20,6 +22,7 @@ mod private use former::Former; #[ derive( Former ) ] + #[ allow( clippy::struct_excessive_bools ) ] struct ListProperties { #[ former( default = ListFormat::Tree ) ] @@ -46,6 +49,8 @@ mod private /// /// List workspace packages. /// + /// # Errors + /// qqq: doc // qqq : typed error pub fn list( o : VerifiedCommand ) -> error::untyped::Result< () > diff --git a/module/move/willbe/src/command/main_header.rs b/module/move/willbe/src/command/main_header.rs index b29229e9bd..2e850208bc 100644 --- a/module/move/willbe/src/command/main_header.rs +++ b/module/move/willbe/src/command/main_header.rs @@ -1,10 +1,14 @@ mod private { + #[ allow( clippy::wildcard_imports ) ] use crate::*; // use action; use error::untyped::{ Error }; /// Generates header to main Readme.md file. + /// + /// # Errors + /// qqq: doc // qqq : typed error pub fn readme_header_renew() -> error::untyped::Result< () > { diff --git a/module/move/willbe/src/command/mod.rs b/module/move/willbe/src/command/mod.rs index d54c8f2df5..5550a9233b 100644 --- a/module/move/willbe/src/command/mod.rs +++ b/module/move/willbe/src/command/mod.rs @@ -1,6 +1,7 @@ /// Define a private namespace for all its items. mod private { + #[ allow( clippy::wildcard_imports ) ] use crate::*; use wca::{ Type, CommandsAggregator, CommandsAggregatorFormer }; @@ -8,6 +9,7 @@ mod private /// Form CA commands grammar. /// + #[ allow( clippy::too_many_lines ) ] pub fn ca() -> CommandsAggregatorFormer { CommandsAggregator::former() diff --git a/module/move/willbe/src/command/publish.rs b/module/move/willbe/src/command/publish.rs index d02fa67bb5..5b3afd8930 100644 --- a/module/move/willbe/src/command/publish.rs +++ b/module/move/willbe/src/command/publish.rs @@ -1,6 +1,8 @@ /// Define a private namespace for all its items. +#[ allow( clippy::std_instead_of_alloc, clippy::std_instead_of_core ) ] mod private { + #[ allow( clippy::wildcard_imports ) ] use crate::*; use colored::Colorize; @@ -11,6 +13,7 @@ mod private use channel::Channel; #[ derive( Former ) ] + #[ allow( clippy::struct_excessive_bools ) ] struct PublishProperties { #[ former( default = Channel::Stable ) ] @@ -28,6 +31,8 @@ mod private /// /// Publish package. /// + /// # Errors + /// qqq: doc pub fn publish( o : VerifiedCommand ) -> error::untyped::Result< () > // qqq : use typed error { @@ -39,14 +44,11 @@ mod private .get_owned( 0 ) .unwrap_or( std::path::PathBuf::from( "" ) ).display() ); - let prop_line = format! - ( - "{}", - o - .props - .iter() - .map( | p | format!( "{}:{}", p.0, p.1.to_string() ) ) - .collect::< Vec< _ > >().join(" ") ); + let prop_line = o + .props + .iter() + .map( | p | format!( "{}:{}", p.0, p.1 ) ) + .collect::< Vec< _ > >().join(" "); let patterns : Vec< _ > = o .args @@ -83,9 +85,9 @@ mod private if dry && !report.packages.is_empty() { - let args = if args_line.is_empty() { String::new() } else { format!(" {}", args_line) }; - let prop = if prop_line.is_empty() { String::new() } else { format!(" {}", prop_line) }; - let line = format!("will .publish{}{} dry:0", args, prop ); + let args = if args_line.is_empty() { String::new() } else { format!(" {args_line}" ) }; + let prop = if prop_line.is_empty() { String::new() } else { format!(" {prop_line}" ) }; + let line = format!("will .publish{args}{prop} dry:0" ); println!("To apply plan, call the command `{}`", line.blue() ); // aaa : for Petro : for Bohdan : bad. should be exact command with exact parameters // aaa : it`s already works diff --git a/module/move/willbe/src/command/publish_diff.rs b/module/move/willbe/src/command/publish_diff.rs index 6408de6de5..a35b453b2e 100644 --- a/module/move/willbe/src/command/publish_diff.rs +++ b/module/move/willbe/src/command/publish_diff.rs @@ -1,5 +1,6 @@ mod private { + #[ allow( clippy::wildcard_imports ) ] use crate::*; use std::fs; @@ -31,6 +32,9 @@ mod private /// # Errors /// /// Returns an error if there is an issue with the command. + /// + /// # Panics + /// qqq: doc pub fn publish_diff( o : VerifiedCommand ) -> error::untyped::Result< () > // qqq : use typed error { diff --git a/module/move/willbe/src/command/readme_headers_renew.rs b/module/move/willbe/src/command/readme_headers_renew.rs index 3f7af310a7..86f46c5588 100644 --- a/module/move/willbe/src/command/readme_headers_renew.rs +++ b/module/move/willbe/src/command/readme_headers_renew.rs @@ -1,5 +1,7 @@ +#[ allow( clippy::std_instead_of_alloc, clippy::std_instead_of_core ) ] mod private { + #[ allow( clippy::wildcard_imports ) ] use crate::*; // use action; // use error::{ err }; @@ -65,6 +67,8 @@ mod private } /// Aggregates two commands: `generate_modules_headers` & `generate_main_header` + /// # Errors + /// qqq: doc pub fn readme_headers_renew() -> error::untyped::Result< () > // qqq : use typed error { let mut report = ReadmeHeadersRenewReport::default(); diff --git a/module/move/willbe/src/command/readme_health_table_renew.rs b/module/move/willbe/src/command/readme_health_table_renew.rs index c91b5b6357..c569a0a1b8 100644 --- a/module/move/willbe/src/command/readme_health_table_renew.rs +++ b/module/move/willbe/src/command/readme_health_table_renew.rs @@ -1,5 +1,6 @@ mod private { + #[ allow( clippy::wildcard_imports ) ] use crate::*; use error::{ untyped::Context }; @@ -7,6 +8,8 @@ mod private /// /// Generate table. /// + /// # Errors + /// qqq: doc // qqq : typed error pub fn readme_health_table_renew() -> error::untyped::Result< () > { diff --git a/module/move/willbe/src/command/readme_modules_headers_renew.rs b/module/move/willbe/src/command/readme_modules_headers_renew.rs index 391205210e..bfd5ed7db1 100644 --- a/module/move/willbe/src/command/readme_modules_headers_renew.rs +++ b/module/move/willbe/src/command/readme_modules_headers_renew.rs @@ -1,10 +1,14 @@ mod private { + #[ allow( clippy::wildcard_imports ) ] use crate::*; // use path::AbsolutePath; // use error::{ untyped::Error }; /// Generate headers for workspace members + /// + /// # Errors + /// qqq: doc // qqq : typed error pub fn readme_modules_headers_renew() -> error::untyped::Result< () > { diff --git a/module/move/willbe/src/command/test.rs b/module/move/willbe/src/command/test.rs index 36fac78a27..118f44f8d6 100644 --- a/module/move/willbe/src/command/test.rs +++ b/module/move/willbe/src/command/test.rs @@ -1,6 +1,7 @@ /// Define a private namespace for all its items. mod private { + #[ allow( clippy::wildcard_imports ) ] use crate::*; use collection::HashSet; @@ -17,6 +18,7 @@ mod private use optimization::Optimization; #[ derive( Former, Debug ) ] + #[ allow( clippy::struct_excessive_bools ) ] struct TestsProperties { #[ former( default = true ) ] @@ -48,6 +50,8 @@ mod private } /// run tests in specified crate + /// # Errors + /// qqq: doc // qqq : don't use 1-prameter Result pub fn test( o : VerifiedCommand ) -> error::untyped::Result< () > // qqq : use typed error { @@ -60,15 +64,11 @@ mod private .unwrap_or( std::path::PathBuf::from( "" ) ) .display() ); - let prop_line = format! - ( - "{}", - o - .props - .iter() - .map( | p | format!( "{}:{}", p.0, p.1.to_string() ) ) - .collect::< Vec< _ > >().join(" ") - ); + let prop_line = o + .props + .iter() + .map( | p | format!( "{}:{}", p.0, p.1 ) ) + .collect::< Vec< _ > >().join(" "); let path : PathBuf = o.args.get_owned( 0 ).unwrap_or_else( || "./".into() ); let path = AbsolutePath::try_from( fs::canonicalize( path )? )?; @@ -127,9 +127,9 @@ Set at least one of them to true." ); { if dry { - let args = if args_line.is_empty() { String::new() } else { format!(" {}", args_line) }; - let prop = if prop_line.is_empty() { String::new() } else { format!(" {}", prop_line) }; - let line = format!("will .publish{}{} dry:0", args, prop); + let args = if args_line.is_empty() { String::new() } else { format!(" {args_line}" ) }; + let prop = if prop_line.is_empty() { String::new() } else { format!(" {prop_line}" ) }; + let line = format!( "will .publish{args}{prop} dry:0" ); println!("To apply plan, call the command `{}`", line.blue()); } else diff --git a/module/move/willbe/src/command/workspace_renew.rs b/module/move/willbe/src/command/workspace_renew.rs index 018046b146..a77254accd 100644 --- a/module/move/willbe/src/command/workspace_renew.rs +++ b/module/move/willbe/src/command/workspace_renew.rs @@ -1,5 +1,7 @@ +#[ allow( clippy::std_instead_of_alloc, clippy::std_instead_of_core ) ] mod private { + #[ allow( clippy::wildcard_imports ) ] use crate::*; use former::Former; @@ -17,6 +19,8 @@ mod private /// /// Create new workspace. /// + /// # Errors + /// qqq: doc // qqq : typed error pub fn workspace_renew( o : VerifiedCommand ) -> error::untyped::Result< () > // qqq : use typed error diff --git a/module/move/willbe/src/entity/channel.rs b/module/move/willbe/src/entity/channel.rs index 65fe6b7716..92aa9f95d7 100644 --- a/module/move/willbe/src/entity/channel.rs +++ b/module/move/willbe/src/entity/channel.rs @@ -1,5 +1,7 @@ +#[ allow( clippy::std_instead_of_alloc, clippy::std_instead_of_core ) ] mod private { + #[ allow( clippy::wildcard_imports ) ] use crate::*; use std:: { @@ -9,6 +11,7 @@ mod private use path::Path; use collection::HashSet; use error::untyped::{ Error }; + #[ allow( clippy::wildcard_imports ) ] use process_tools::process::*; /// The `Channel` enum represents different release channels for rust. @@ -51,6 +54,9 @@ mod private /// Retrieves a list of available channels. /// /// This function takes a path and returns a `Result` with a vector of strings representing the available channels. + /// + /// # Errors + /// qqq: doc // qqq : typed error pub fn available_channels< P >( path : P ) -> error::untyped::Result< HashSet< Channel > > where diff --git a/module/move/willbe/src/entity/code.rs b/module/move/willbe/src/entity/code.rs index 5c8418bad8..0faf0cdf27 100644 --- a/module/move/willbe/src/entity/code.rs +++ b/module/move/willbe/src/entity/code.rs @@ -1,5 +1,7 @@ +#[ allow( clippy::std_instead_of_alloc, clippy::std_instead_of_core ) ] mod private { + #[ allow( clippy::wildcard_imports ) ] use crate::*; use std:: @@ -17,7 +19,9 @@ mod private pub trait AsCode { /// Converts the object to its code representation. - fn as_code< 'a >( &'a self ) -> std::io::Result< Cow< 'a, str > >; + /// # Errors + /// qqq: doc + fn as_code( &self ) -> std::io::Result< Cow< '_, str > >; } /// A trait for retrieving an iterator over items of a source file. diff --git a/module/move/willbe/src/entity/dependency.rs b/module/move/willbe/src/entity/dependency.rs index 5d09c1cea0..128a946061 100644 --- a/module/move/willbe/src/entity/dependency.rs +++ b/module/move/willbe/src/entity/dependency.rs @@ -1,6 +1,7 @@ mod private { + #[ allow( clippy::wildcard_imports ) ] use crate::*; // use crates_tools::CrateArchive; @@ -25,6 +26,7 @@ mod private /// The file system path for a local path dependency. /// Only produced on cargo 1.51+ + #[ must_use ] pub fn crate_dir( &self ) -> Option< CrateDir > { match &self.inner.path @@ -35,12 +37,14 @@ mod private } /// Name as given in the Cargo.toml. + #[ must_use ] pub fn name( &self ) -> String { self.inner.name.clone() } /// The kind of dependency this is. + #[ must_use ] pub fn kind( &self ) -> DependencyKind { match self.inner.kind @@ -53,6 +57,7 @@ mod private } /// Required version + #[ must_use ] pub fn req( &self ) -> semver::VersionReq { self.inner.req.clone() @@ -114,7 +119,7 @@ mod private { Self { - name : value.name().into(), + name : value.name(), crate_dir : value.crate_dir(), // path : value.path().clone().map( | path | AbsolutePath::try_from( path ).unwrap() ), } @@ -161,10 +166,16 @@ mod private // qqq : for Bohdan : poor description /// Recursive implementation of the `list` function - pub fn _list< 'a > + /// # Errors + /// qqq: doc + /// + /// # Panics + /// qqq: doc + #[ allow( clippy::needless_pass_by_value, clippy::implicit_hasher ) ] + pub fn _list ( workspace : &Workspace, // aaa : for Bohdan : no mut // aaa : no mut - package : &Package< 'a >, + package : &Package< '_ >, graph : &mut collection::HashMap< CrateId, collection::HashSet< CrateId > >, opts : DependenciesOptions ) @@ -183,7 +194,7 @@ mod private let manifest_file = &package.manifest_file(); let package = workspace - .package_find_by_manifest( &manifest_file ) + .package_find_by_manifest( manifest_file ) .ok_or( format_err!( "Package not found in the workspace with path : `{}`", manifest_file.as_ref().display() ) )?; let deps : collection::HashSet< _ > = package @@ -229,11 +240,14 @@ mod private /// # Returns /// /// If the operation is successful, returns a vector of `PathBuf` objects, where each `PathBuf` represents the path to a local dependency of the specified package. + /// # Errors + /// qqq: doc // qqq : typed error? - pub fn list< 'a > + #[ allow( clippy::needless_pass_by_value ) ] + pub fn list ( workspace : &mut Workspace, - package : &Package< 'a >, + package : &Package< '_ >, opts : DependenciesOptions ) // qqq : use typed error diff --git a/module/move/willbe/src/entity/diff.rs b/module/move/willbe/src/entity/diff.rs index 08b0638b77..9f6f4b7709 100644 --- a/module/move/willbe/src/entity/diff.rs +++ b/module/move/willbe/src/entity/diff.rs @@ -1,5 +1,7 @@ +#[ allow( clippy::std_instead_of_alloc, clippy::std_instead_of_core ) ] mod private { + #[ allow( clippy::wildcard_imports ) ] use crate::*; use std:: @@ -73,6 +75,9 @@ mod private /// # Returns /// /// Returns a new instance of the struct with the excluded items removed from the internal report. + /// # Panics + /// qqq: doc + #[ must_use ] pub fn exclude< Is, I >( mut self, items : Is ) -> Self where Is : Into< HashSet< I > >, @@ -89,11 +94,12 @@ mod private Self( map ) } - /// Checks if there are any changes in the DiffItems. + /// Checks if there are any changes in the `DiffItems`. /// /// # Returns - /// * `true` if there are changes in any of the DiffItems. - /// * `false` if all DiffItems are the same. + /// * `true` if there are changes in any of the `DiffItems`. + /// * `false` if all `DiffItems` are the same. + #[ must_use ] pub fn has_changes( &self ) -> bool { !self.0.iter().all( |( _, item )| matches!( item, DiffItem::File( Diff::Same( () ) ) )) @@ -112,9 +118,9 @@ mod private { match item { - Diff::Same( _ ) => writeln!( f, " {}", path.display() )?, - Diff::Add( _ ) => writeln!( f, "+ {} NEW", path.to_string_lossy().green() )?, - Diff::Rem( _ ) => writeln!( f, "- {} REMOVED", path.to_string_lossy().red() )?, + Diff::Same( () ) => writeln!( f, " {}", path.display() )?, + Diff::Add( () ) => writeln!( f, "+ {} NEW", path.to_string_lossy().green() )?, + Diff::Rem( () ) => writeln!( f, "- {} REMOVED", path.to_string_lossy().red() )?, }; } DiffItem::Content( items ) => @@ -127,7 +133,7 @@ mod private { match item { - Diff::Same( t ) => write!( f, "| {}", t )?, + Diff::Same( t ) => write!( f, "| {t}" )?, Diff::Add( t ) => write!( f, "| + {}", t.green() )?, Diff::Rem( t ) => write!( f, "| - {}", t.red() )?, }; @@ -156,6 +162,9 @@ mod private /// # Returns /// /// A `DiffReport` struct, representing the unique and shared attributes of the two crate archives. + /// # Panics + /// qqq: doc + #[ must_use ] pub fn crate_diff( left : &CrateArchive, right : &CrateArchive ) -> DiffReport { let mut report = DiffReport::default(); diff --git a/module/move/willbe/src/entity/features.rs b/module/move/willbe/src/entity/features.rs index 300fa7ca2f..8212fdef42 100644 --- a/module/move/willbe/src/entity/features.rs +++ b/module/move/willbe/src/entity/features.rs @@ -1,5 +1,7 @@ +#[ allow( clippy::std_instead_of_alloc, clippy::std_instead_of_core ) ] mod private { + #[ allow( clippy::wildcard_imports ) ] use crate::*; use collection::{ BTreeSet, HashSet }; use error::untyped::{ bail }; // xxx @@ -38,7 +40,10 @@ mod private /// let feature_combinations = features_powerset( &package, power, &exclude_features, &include_features, enabled_features, false, false ); /// // Use `feature_combinations` as needed. /// ``` - + /// + /// # Errors + /// qqq: doc + #[ allow( clippy::too_many_arguments ) ] pub fn features_powerset ( package : WorkspacePackageRef< '_ >, @@ -96,6 +101,7 @@ mod private } /// Calculate estimate for `features_powerset.length` + #[ must_use ] pub fn estimate_with ( n : usize, diff --git a/module/move/willbe/src/entity/files.rs b/module/move/willbe/src/entity/files.rs index d8941dfa27..b6cc1ac89a 100644 --- a/module/move/willbe/src/entity/files.rs +++ b/module/move/willbe/src/entity/files.rs @@ -1,6 +1,8 @@ /// Define a private namespace for all its items. +#[ allow( clippy::std_instead_of_alloc, clippy::std_instead_of_core ) ] mod private { + #[ allow( clippy::wildcard_imports ) ] use crate::*; use std:: diff --git a/module/move/willbe/src/entity/files/crate_dir.rs b/module/move/willbe/src/entity/files/crate_dir.rs index 7ea3424e56..7bbf133bfc 100644 --- a/module/move/willbe/src/entity/files/crate_dir.rs +++ b/module/move/willbe/src/entity/files/crate_dir.rs @@ -1,3 +1,7 @@ +#![ allow( clippy::std_instead_of_alloc, clippy::std_instead_of_core ) ] + + +#[ allow( clippy::wildcard_imports ) ] use crate::*; use entity:: @@ -34,6 +38,7 @@ impl CrateDir /// Returns inner type which is an absolute path. #[ inline( always ) ] + #[ must_use ] pub fn absolute_path( self ) -> AbsolutePath { self.0 @@ -41,6 +46,7 @@ impl CrateDir /// Returns path to manifest aka cargo file. #[ inline( always ) ] + #[ must_use ] pub fn manifest_file( self ) -> ManifestFile { self.into() diff --git a/module/move/willbe/src/entity/files/either.rs b/module/move/willbe/src/entity/files/either.rs index ef151d616c..0caa927f4f 100644 --- a/module/move/willbe/src/entity/files/either.rs +++ b/module/move/willbe/src/entity/files/either.rs @@ -1,3 +1,4 @@ +#[ allow( clippy::wildcard_imports ) ] use crate::*; use core:: { @@ -22,7 +23,8 @@ pub struct EitherDirOrFile( data_type::Either< CrateDir, ManifestFile > ); impl EitherDirOrFile { - /// Returns inner type which is an data_type::Either< CrateDir, ManifestFile >. + /// Returns inner type which is an `data_type::Either`< `CrateDir`, `ManifestFile` >. + #[ must_use ] pub fn inner( self ) -> data_type::Either< CrateDir, ManifestFile > { self.0 @@ -75,6 +77,7 @@ impl Deref for EitherDirOrFile { type Target = Path; + #[ allow( clippy::explicit_deref_methods ) ] fn deref( &self ) -> &Self::Target { self.0.deref() @@ -83,6 +86,7 @@ impl Deref for EitherDirOrFile impl DerefMut for EitherDirOrFile { + #[ allow( clippy::explicit_deref_methods ) ] fn deref_mut( &mut self ) -> &mut Self::Target { self.0.deref_mut() diff --git a/module/move/willbe/src/entity/files/manifest_file.rs b/module/move/willbe/src/entity/files/manifest_file.rs index 78af49e41b..149b50a365 100644 --- a/module/move/willbe/src/entity/files/manifest_file.rs +++ b/module/move/willbe/src/entity/files/manifest_file.rs @@ -1,3 +1,6 @@ +#![ allow( clippy::std_instead_of_alloc, clippy::std_instead_of_core ) ] + +#[ allow( clippy::wildcard_imports ) ] use crate::*; use entity:: @@ -42,6 +45,7 @@ impl ManifestFile /// Returns inner type whicj is an absolute path. #[ inline( always ) ] + #[ must_use ] pub fn inner( self ) -> AbsolutePath { self.0 @@ -49,6 +53,7 @@ impl ManifestFile /// Returns path to crate dir. #[ inline( always ) ] + #[ must_use ] pub fn crate_dir( self ) -> CrateDir { self.into() diff --git a/module/move/willbe/src/entity/files/source_file.rs b/module/move/willbe/src/entity/files/source_file.rs index b895d3eec2..630f434006 100644 --- a/module/move/willbe/src/entity/files/source_file.rs +++ b/module/move/willbe/src/entity/files/source_file.rs @@ -1,3 +1,7 @@ +#![ allow( clippy::std_instead_of_alloc, clippy::std_instead_of_core ) ] + + +#[ allow( clippy::wildcard_imports ) ] use crate::*; use entity:: @@ -35,6 +39,7 @@ impl SourceFile /// Returns inner type which is an absolute path. #[ inline( always ) ] + #[ must_use ] pub fn inner( self ) -> AbsolutePath { self.0 @@ -229,15 +234,15 @@ impl CodeItems for SourceFile fn items( &self ) -> impl IterTrait< '_, syn::Item > { // xxx : use closures instead of expect - let content = fs::read_to_string( self.as_ref() ).expect( &format!( "Failed to parse file {self}" ) ); - let parsed : syn::File = syn::parse_file( &content ).expect( &format!( "Failed to parse file {self}" ) ); + let content = fs::read_to_string( self.as_ref() ).unwrap_or_else( | _ | panic!( "Failed to parse file {self}" ) ); + let parsed : syn::File = syn::parse_file( &content ).unwrap_or_else( | _ | panic!( "Failed to parse file {self}" ) ); parsed.items.into_iter() } } impl AsCode for SourceFile { - fn as_code< 'a >( &'a self ) -> std::io::Result< Cow< 'a, str > > + fn as_code( &self ) -> std::io::Result< Cow< '_, str > > { Ok( Cow::Owned( std::fs::read_to_string( self.as_ref() )? ) ) } diff --git a/module/move/willbe/src/entity/git.rs b/module/move/willbe/src/entity/git.rs index eeeddfd5a4..00ba0d6b2f 100644 --- a/module/move/willbe/src/entity/git.rs +++ b/module/move/willbe/src/entity/git.rs @@ -1,5 +1,7 @@ +#[ allow( clippy::std_instead_of_alloc, clippy::std_instead_of_core ) ] mod private { + #[ allow( clippy::wildcard_imports ) ] use crate::*; use std::fmt; @@ -53,6 +55,9 @@ mod private } /// Performs a Git commit operation using the provided options + /// # Errors + /// qqq: doc + #[ allow( clippy::needless_pass_by_value ) ] pub fn perform_git_commit( o : GitOptions ) -> error::untyped::Result< ExtendedGitReport > // qqq : use typed error { diff --git a/module/move/willbe/src/entity/manifest.rs b/module/move/willbe/src/entity/manifest.rs index 5a0f7a58aa..89c688be5f 100644 --- a/module/move/willbe/src/entity/manifest.rs +++ b/module/move/willbe/src/entity/manifest.rs @@ -1,6 +1,8 @@ /// Define a private namespace for all its items. +#[ allow( clippy::std_instead_of_alloc, clippy::std_instead_of_core ) ] mod private { + #[ allow( clippy::wildcard_imports ) ] use crate::*; use std:: @@ -100,12 +102,16 @@ mod private } /// Returns path to `Cargo.toml`. + #[ must_use ] pub fn manifest_file( &self ) -> &AbsolutePath { &self.manifest_file } /// Path to directory where `Cargo.toml` located. + /// # Panics + /// qqq: doc + #[ must_use ] pub fn crate_dir( &self ) -> CrateDir { self.manifest_file.parent().unwrap().try_into().unwrap() @@ -113,6 +119,8 @@ mod private } /// Store manifest. + /// # Errors + /// qqq: doc pub fn store( &self ) -> io::Result< () > { fs::write( &self.manifest_file, self.data.to_string() )?; @@ -121,6 +129,7 @@ mod private } /// Check that the current manifest is the manifest of the package (can also be a virtual workspace). + #[ must_use ] pub fn package_is( &self ) -> bool { // let data = self.data.as_ref().ok_or_else( || ManifestError::EmptyManifestData )?; @@ -129,7 +138,8 @@ mod private } /// Check that module is local. - /// The package is defined as local if the `publish` field is set to `false' or the registers are specified. + /// The package is defined as local if the `publish` field is set to `false` or the registers are specified. + #[ must_use ] pub fn local_is( &self ) -> bool { // let data = self.data.as_ref().ok_or_else( || ManifestError::EmptyManifestData )?; @@ -137,7 +147,7 @@ mod private if data.get( "package" ).is_some() && data[ "package" ].get( "name" ).is_some() { let remote = data[ "package" ].get( "publish" ).is_none() - || data[ "package" ][ "publish" ].as_bool().or( Some( true ) ).unwrap(); + || data[ "package" ][ "publish" ].as_bool().unwrap_or( true ); return !remote; } @@ -146,6 +156,8 @@ mod private } /// Retrieves the repository URL of a package from its `Cargo.toml` file. + /// # Errors + /// qqq: doc // qqq : use typed error pub fn repo_url( crate_dir : &CrateDir ) -> error::untyped::Result< String > { @@ -168,7 +180,7 @@ mod private else { let report = tool::git::ls_remote_url( crate_dir.clone().absolute_path() )?; - url::repo_url_extract( &report.out.trim() ).ok_or_else( || format_err!( "Fail to extract repository url from git remote.") ) + url::repo_url_extract( report.out.trim() ).ok_or_else( || format_err!( "Fail to extract repository url from git remote.") ) } } else diff --git a/module/move/willbe/src/entity/package.rs b/module/move/willbe/src/entity/package.rs index 5e53b6ea19..4c5e3cd3e8 100644 --- a/module/move/willbe/src/entity/package.rs +++ b/module/move/willbe/src/entity/package.rs @@ -1,5 +1,7 @@ +#[ allow( clippy::std_instead_of_alloc, clippy::std_instead_of_core ) ] mod private { + #[ allow( clippy::wildcard_imports ) ] use crate::*; use std:: @@ -121,6 +123,9 @@ mod private { /// Path to `Cargo.toml` + /// # Panics + /// qqq: doc + #[ must_use ] pub fn manifest_file( &self ) -> ManifestFile { match self @@ -131,6 +136,9 @@ mod private } /// Path to folder with `Cargo.toml` + /// # Panics + /// qqq: doc + #[ must_use ] pub fn crate_dir( &self ) -> CrateDir { match self @@ -141,6 +149,10 @@ mod private } /// Package version + /// # Errors + /// qqq: doc + /// # Panics + /// qqq: doc pub fn version( &self ) -> Result< String, PackageError > { match self @@ -161,6 +173,7 @@ mod private } /// Check that module is local. + #[ must_use ] pub fn local_is( &self ) -> bool { match self @@ -179,6 +192,8 @@ mod private } /// Returns the `Manifest` + /// # Errors + /// qqq: doc pub fn manifest( &self ) -> Result< Manifest, PackageError > { match self @@ -205,14 +220,16 @@ mod private /// - `false` if there is no need to publish the package. /// /// Panics if the package is not loaded or local package is not packed. + /// # Errors + /// qqq: doc - pub fn publish_need< 'a >( package : &Package< 'a >, path : Option< path::PathBuf > ) -> Result< bool, PackageError > + pub fn publish_need( package : &Package< '_ >, path : Option< path::PathBuf > ) -> Result< bool, PackageError > { let name = package.name()?; let version = package.version()?; let local_package_path = path - .map( | p | p.join( format!( "package/{0}-{1}.crate", name, version ) ) ) - .unwrap_or( packed_crate::local_path( &name, &version, package.crate_dir() ).map_err( | _ | PackageError::LocalPath )? ); + .map( | p | p.join( format!( "package/{name}-{version}.crate" ) ) ) + .unwrap_or( packed_crate::local_path( name, &version, package.crate_dir() ).map_err( | _ | PackageError::LocalPath )? ); let local_package = CrateArchive::read( local_package_path ).map_err( | _ | PackageError::ReadArchive )?; let remote_package = match CrateArchive::download_crates_io( name, version ) diff --git a/module/move/willbe/src/entity/package_md_extension.rs b/module/move/willbe/src/entity/package_md_extension.rs index 29fe95b645..ab31564b83 100644 --- a/module/move/willbe/src/entity/package_md_extension.rs +++ b/module/move/willbe/src/entity/package_md_extension.rs @@ -1,27 +1,42 @@ /// Define a private namespace for all its items. +#[ allow( clippy::std_instead_of_alloc, clippy::std_instead_of_core ) ] mod private { + #[ allow( clippy::wildcard_imports ) ] use crate::*; /// Md's extension for workspace pub trait PackageMdExtension { /// Package name + /// # Errors + /// qqq: doc fn name( &self ) -> Result< &str, package::PackageError >; /// Stability + /// # Errors + /// qqq: doc fn stability( &self ) -> Result< action::readme_health_table_renew::Stability, package::PackageError >; /// Repository + /// # Errors + /// qqq: doc fn repository( &self ) -> Result< Option< String >, package::PackageError >; /// Discord url + /// # Errors + /// qqq: doc fn discord_url( &self ) -> Result< Option< String >, package::PackageError >; } impl < 'a > package::Package< 'a > { /// Package name + /// # Errors + /// qqq: doc + /// + /// # Panics + /// qqq: doc pub fn name( &self ) -> Result< &str, package::PackageError > { match self @@ -43,6 +58,9 @@ mod private } /// Stability + /// + /// # Errors + /// qqq: doc pub fn stability( &self ) -> Result< action::readme_health_table_renew::Stability, package::PackageError > { // aaa : for Petro : bad : first of all it should be in trait. also there is duplicated code @@ -78,6 +96,9 @@ mod private } /// Repository + /// + /// # Errors + /// qqq: doc pub fn repository( &self ) -> Result< Option< String >, package::PackageError > { match self @@ -93,7 +114,7 @@ mod private data[ "package" ] .get( "repository" ) .and_then( | r | r.as_str() ) - .map( | r | r.to_string()) + .map( std::string::ToString::to_string ) ) } Self::WorkspacePackageRef( package ) => @@ -104,6 +125,9 @@ mod private } /// Discord url + /// + /// # Errors + /// qqq: doc pub fn discord_url( &self ) -> Result< Option< String >, package::PackageError > { match self @@ -116,12 +140,12 @@ mod private self.package_metadata() .and_then( | m | m.get( "discord_url" ) ) .and_then( | url | url.as_str() ) - .map( | r | r.to_string() ) + .map( std::string::ToString::to_string ) ) } Self::WorkspacePackageRef( package ) => { - Ok( package.metadata()[ "discord_url" ].as_str().map( | url | url.to_string() ) ) + Ok( package.metadata()[ "discord_url" ].as_str().map( std::string::ToString::to_string ) ) } } } diff --git a/module/move/willbe/src/entity/packages.rs b/module/move/willbe/src/entity/packages.rs index 6dd4006db3..5525c8a99d 100644 --- a/module/move/willbe/src/entity/packages.rs +++ b/module/move/willbe/src/entity/packages.rs @@ -1,5 +1,7 @@ +#[ allow( clippy::std_instead_of_alloc, clippy::std_instead_of_core ) ] mod private { + #[ allow( clippy::wildcard_imports ) ] use crate::*; use std:: { @@ -16,6 +18,7 @@ mod private /// A configuration struct for specifying optional filters when using the /// `filter` function. It allows users to provide custom filtering /// functions for packages and dependencies. + #[ allow( clippy::type_complexity ) ] #[ derive( Default ) ] pub struct FilterMapOptions { diff --git a/module/move/willbe/src/entity/packed_crate.rs b/module/move/willbe/src/entity/packed_crate.rs index 77da22b98e..1cd95c7097 100644 --- a/module/move/willbe/src/entity/packed_crate.rs +++ b/module/move/willbe/src/entity/packed_crate.rs @@ -1,5 +1,7 @@ +#[ allow( clippy::std_instead_of_alloc, clippy::std_instead_of_core ) ] mod private { + #[ allow( clippy::wildcard_imports ) ] use crate::*; use std:: @@ -21,10 +23,13 @@ mod private /// /// # Returns : /// The local packed `.crate` file of the package + /// + /// # Errors + /// qqq: doc // qqq : typed error pub fn local_path< 'a >( name : &'a str, version : &'a str, crate_dir : CrateDir ) -> error::untyped::Result< PathBuf > { - let buf = format!( "package/{0}-{1}.crate", name, version ); + let buf = format!( "package/{name}-{version}.crate" ); let workspace = Workspace::try_from( crate_dir )?; let mut local_package_path = PathBuf::new(); @@ -37,6 +42,11 @@ mod private /// /// Get data of remote package from crates.io. /// + /// # Errors + /// qqq: doc + /// + /// # Panics + /// qqq: doc // qqq : typed error pub fn download< 'a >( name : &'a str, version : &'a str ) -> error::untyped::Result< Vec< u8 > > { @@ -45,7 +55,7 @@ mod private .timeout_write( Duration::from_secs( 5 ) ) .build(); let mut buf = String::new(); - write!( &mut buf, "https://static.crates.io/crates/{0}/{0}-{1}.crate", name, version )?; + write!( &mut buf, "https://static.crates.io/crates/{name}/{name}-{version}.crate" )?; let resp = agent.get( &buf[ .. ] ).call().context( "Get data of remote package" )?; diff --git a/module/move/willbe/src/entity/progress_bar.rs b/module/move/willbe/src/entity/progress_bar.rs index c9fef4cf07..db61b1f078 100644 --- a/module/move/willbe/src/entity/progress_bar.rs +++ b/module/move/willbe/src/entity/progress_bar.rs @@ -1,3 +1,4 @@ +#[ allow( clippy::std_instead_of_alloc, clippy::std_instead_of_core ) ] mod private { /// The `ProgressBar` structure is used to display progress indicators in the terminal. @@ -52,7 +53,8 @@ mod private /// # Returns /// /// A `ProgressBar` instance that can be used to update and display progress. - pub fn progress_bar< 'a >( &'a self, variants_len : u64 ) -> ProgressBar< 'a > + #[ must_use ] + pub fn progress_bar( &self, variants_len : u64 ) -> ProgressBar< '_ > { let progress_bar = { diff --git a/module/move/willbe/src/entity/publish.rs b/module/move/willbe/src/entity/publish.rs index f7cc9965c8..93af0e97fc 100644 --- a/module/move/willbe/src/entity/publish.rs +++ b/module/move/willbe/src/entity/publish.rs @@ -1,5 +1,7 @@ +#[ allow( clippy::std_instead_of_alloc, clippy::std_instead_of_core ) ] mod private { + #[ allow( clippy::wildcard_imports ) ] use crate::*; use std::fmt; @@ -189,6 +191,7 @@ mod private .map_err( |( _, _e )| fmt::Error )?; let action::list::ListReport::Tree( list ) = list else { unreachable!() }; + #[ allow( clippy::items_after_statements ) ] fn callback( name_bump_report : &collection::HashMap< &String, ( String, String ) >, mut r : tool::ListNodeReport ) -> tool::ListNodeReport { if let Some(( old, new )) = name_bump_report.get( &r.name ) @@ -204,10 +207,10 @@ mod private let printer = list; let rep : Vec< tool::ListNodeReport > = printer.iter().map( | printer | printer.info.clone() ).collect(); let list: Vec< tool::ListNodeReport > = rep.into_iter().map( | r | callback( &name_bump_report, r ) ).collect(); - let printer : Vec< tool::TreePrinter > = list.iter().map( | rep | tool::TreePrinter::new( rep ) ).collect(); + let printer : Vec< tool::TreePrinter > = list.iter().map( tool::TreePrinter::new ).collect(); let list = action::list::ListReport::Tree( printer ); - writeln!( f, "{}", list )?; + writeln!( f, "{list}" )?; } Ok( () ) @@ -264,11 +267,11 @@ mod private } if let Some( exclude_dev_dependencies ) = &self.storage.exclude_dev_dependencies { - plan = plan.exclude_dev_dependencies( *exclude_dev_dependencies ) + plan = plan.exclude_dev_dependencies( *exclude_dev_dependencies ); } if let Some( commit_changes ) = &self.storage.commit_changes { - plan = plan.commit_changes( *commit_changes ) + plan = plan.commit_changes( *commit_changes ); } let plan = plan .channel( channel ) @@ -335,11 +338,11 @@ mod private return Ok( () ) } let info = get_info.as_ref().unwrap(); - write!( f, "{}", info )?; + write!( f, "{info}" )?; if let Some( bump ) = bump { - writeln!( f, "{}", bump )?; + writeln!( f, "{bump}" )?; } if let Some( add ) = add { @@ -371,7 +374,10 @@ mod private /// # Returns /// /// * `Result` - The result of the publishing operation, including information about the publish, version bump, and git operations. - + /// + /// # Errors + /// qqq: doc + #[ allow( clippy::option_map_unit_fn ) ] pub fn perform_package_publish( instruction : PackagePublishInstruction ) -> ResultWithReport< PublishReport, Error > { let mut report = PublishReport::default(); @@ -432,7 +438,7 @@ mod private if let Some( git_root ) = git_root.as_ref() { - let res = tool::git::push( &git_root, dry ).err_with_report( &report )?; + let res = tool::git::push( git_root, dry ).err_with_report( &report )?; report.push = Some( res ); } @@ -448,6 +454,9 @@ mod private /// # Returns /// /// Returns a `Result` containing a vector of `PublishReport` if successful, else an error. + /// + /// # Errors + /// qqq: doc pub fn perform_packages_publish( plan : PublishPlan ) -> error::untyped::Result< Vec< PublishReport > > // qqq : use typed error { diff --git a/module/move/willbe/src/entity/table.rs b/module/move/willbe/src/entity/table.rs index 38e789686c..a49acf6350 100644 --- a/module/move/willbe/src/entity/table.rs +++ b/module/move/willbe/src/entity/table.rs @@ -1,3 +1,4 @@ +#[ allow( clippy::std_instead_of_alloc, clippy::std_instead_of_core ) ] mod private { use std::fmt::{Display, Formatter}; @@ -13,13 +14,14 @@ mod private { fn fmt( &self, f : &mut Formatter< '_ > ) -> std::fmt::Result { - writeln!( f, "{}", self.inner.to_string() ) + writeln!( f, "{}", self.inner ) } } impl Table { /// Create an empty table. + #[ must_use ] pub fn new() -> Self { Self @@ -57,7 +59,7 @@ mod private fn default_format() -> prettytable::format::TableFormat { - let format = prettytable::format::FormatBuilder::new() + prettytable::format::FormatBuilder::new() .column_separator( ' ' ) .borders( ' ' ) .separators @@ -66,8 +68,7 @@ mod private prettytable::format::LineSeparator::new( '-', '+', '+', '+' ) ) .padding( 1, 1 ) - .build(); - format + .build() } /// Represent a table row made of cells. @@ -89,9 +90,11 @@ mod private } } + #[ allow( clippy::new_without_default ) ] impl Row { /// Create an row of length size, with empty strings stored. + #[ must_use ] pub fn new() -> Self { Self diff --git a/module/move/willbe/src/entity/test.rs b/module/move/willbe/src/entity/test.rs index b8b7b67227..4ec5fe190d 100644 --- a/module/move/willbe/src/entity/test.rs +++ b/module/move/willbe/src/entity/test.rs @@ -1,7 +1,10 @@ +#[ allow( clippy::std_instead_of_alloc, clippy::std_instead_of_core ) ] mod private { + #[ allow( clippy::wildcard_imports ) ] use crate::*; + #[ allow( clippy::wildcard_imports ) ] use table::*; // qqq : for Bohdan no asterisk imports, but in special cases use std:: @@ -10,6 +13,7 @@ mod private sync, }; use colored::Colorize as _; + #[ allow( clippy::wildcard_imports ) ] use process_tools::process::*; use error:: { @@ -82,6 +86,10 @@ mod private /// `with_all_features` - If it's true - add to powerset one subset which contains all features. /// `with_none_features` - If it's true - add to powerset one empty subset. /// `variants_cap` - Maximum of subset in powerset + /// + /// # Errors + /// qqq: doc + #[ allow( clippy::needless_pass_by_value, clippy::too_many_arguments ) ] pub fn try_from< 'a > ( packages : impl core::iter::Iterator< Item = WorkspacePackageRef< 'a > >, @@ -148,7 +156,7 @@ mod private } all_features.extend( features ); } - let mut ff = Vec::from_iter( self.enabled_features.iter().cloned() ); + let mut ff: Vec< _ > = self.enabled_features.iter().cloned().collect(); for feature in all_features { if !ff.contains( &feature ) @@ -184,7 +192,7 @@ mod private } // aaa : for Petro : bad, DRY // aaa : replace with method - writeln!( f, "{}", table )?; + writeln!( f, "{table}" )?; Ok( () ) } } @@ -202,9 +210,10 @@ mod private /// `with_all_features` - If it's true - add to powerset one subset which contains all features. /// `with_none_features` - If it's true - add to powerset one empty subset. /// `variants_cap` - Maximum of subset in powerset - fn try_from< 'a > + #[ allow( clippy::too_many_arguments ) ] + fn try_from ( - package : WorkspacePackageRef< 'a >, + package : WorkspacePackageRef< '_ >, channels : &collection::HashSet< channel::Channel >, power : u32, include_features : &[ String ], @@ -241,8 +250,8 @@ mod private ( TestVariant { - channel : channel.clone(), - optimization : optimization.clone(), + channel : *channel, + optimization : *optimization, features : feature.clone(), } ); @@ -314,10 +323,11 @@ mod private /// Represents the options for the test. #[ derive( Debug, former::Former, Clone ) ] + #[ allow( clippy::struct_excessive_bools ) ] pub struct SingleTestOptions { /// Specifies the release channels for rust. - /// More details : https://rust-lang.github.io/rustup/concepts/channels.html#:~:text=Rust%20is%20released%20to%20three,releases%20are%20made%20every%20night. + /// More details : . channel : channel::Channel, /// Specifies the optimization for rust. optimization : optimization::Optimization, @@ -335,7 +345,7 @@ mod private temp_directory_path : Option< path::PathBuf >, /// A boolean indicating whether to perform a dry run or not. dry : bool, - /// RUST_BACKTRACE + /// `RUST_BACKTRACE` #[ former( default = true ) ] backtrace : bool, } @@ -373,6 +383,10 @@ mod private /// /// Returns a `Result` containing a `Report` if the command is executed successfully, /// or an error if the command fails to execute. + /// + /// # Errors + /// qqq: doc + #[ allow( clippy::needless_pass_by_value ) ] pub fn _run< P >( path : P, options : SingleTestOptions ) -> Result< Report, Report > // xxx where @@ -414,7 +428,7 @@ mod private /// Plan for testing pub plan : TestPlan, - /// `concurrent` - A usize value indicating how much test`s can be run at the same time. + /// `concurrent` - A usize value indicating how much test's can be run at the same time. pub concurrent : u32, /// `temp_path` - path to temp directory. @@ -430,6 +444,7 @@ mod private // aaa : for Petro : remove after Former fix // aaa : done + #[ allow( clippy::missing_fields_in_debug ) ] impl fmt::Debug for TestOptions { fn fmt( &self, f : &mut fmt::Formatter< '_ > ) -> std::fmt::Result @@ -499,7 +514,7 @@ mod private } all_features.extend( features ); } - let mut ff = Vec::from_iter( self.enabled_features.iter().cloned() ); + let mut ff: Vec< _ > = self.enabled_features.iter().cloned().collect(); for feature in all_features { if !ff.contains( &feature ) @@ -537,8 +552,8 @@ mod private Err( report ) => { failed += 1; - let mut out = report.out.replace( "\n", "\n " ); - out.push_str( "\n" ); + let mut out = report.out.replace( '\n', "\n " ); + out.push( '\n' ); write!( f, " ❌ > {}\n\n{out}", report.command )?; "❌" }, @@ -555,7 +570,7 @@ mod private } // aaa : for Petro : bad, DRY // aaa : replace with method - writeln!( f, "{}", table )?; + writeln!( f, "{table}" )?; writeln!( f, " {}", generate_summary_message( failed, success ) )?; Ok( () ) @@ -617,7 +632,7 @@ mod private writeln!( f, "Successful :" )?; for report in &self.success_reports { - writeln!( f, "{}", report )?; + writeln!( f, "{report}" )?; } } if !self.failure_reports.is_empty() @@ -625,10 +640,11 @@ mod private writeln!( f, "Failure :" )?; for report in &self.failure_reports { - writeln!( f, "{}", report )?; + writeln!( f, "{report}" )?; } } writeln!( f, "Global report" )?; + #[ allow( clippy::cast_possible_wrap, clippy::cast_possible_truncation ) ] writeln!( f, " {}", generate_summary_message( self.failure_reports.len() as i32, self.success_reports.len() as i32 ) )?; Ok( () ) @@ -637,13 +653,17 @@ mod private /// `tests_run` is a function that runs tests on a given package with specified arguments. /// It returns a `TestReport` on success, or a `TestReport` and an `Error` on failure. + /// + /// # Errors + /// qqq: doc + /// + /// # Panics + /// qqq: doc pub fn run( options : &PackageTestOptions< '_ > ) -> ResultWithReport< TestReport, TestError > // -> Result< TestReport, ( TestReport, TestError ) > { - let mut report = TestReport::default(); - report.dry = options.dry; - report.enabled_features = options.plan.enabled_features.clone(); + let report = TestReport { dry: options.dry, enabled_features: options.plan.enabled_features.clone(), ..Default::default() }; let report = sync::Arc::new( sync::Mutex::new( report ) ); let crate_dir = options.plan.crate_dir.clone(); @@ -678,7 +698,7 @@ mod private { let _s = { - let s = options.progress_bar.multi_progress.add( indicatif::ProgressBar::new_spinner().with_message( format!( "{}", variant ) ) ); + let s = options.progress_bar.multi_progress.add( indicatif::ProgressBar::new_spinner().with_message( format!( "{variant}" ) ) ); s.enable_steady_tick( std::time::Duration::from_millis( 100 ) ); s }; @@ -712,6 +732,11 @@ mod private } /// Run tests for given packages. + /// # Errors + /// qqq: doc + /// + /// # Panics + /// qqq: doc pub fn tests_run( args : &TestOptions ) -> ResultWithReport< TestsReport, TestError > // -> Result< TestsReport, ( TestsReport, TestError ) > @@ -720,8 +745,7 @@ mod private let multi_progress = progress_bar::MultiProgress::default(); #[ cfg( feature = "progress_bar" ) ] let mm = &multi_progress; - let mut report = TestsReport::default(); - report.dry = args.dry; + let report = TestsReport { dry: args.dry, ..Default::default() }; let report = sync::Arc::new( sync::Mutex::new( report ) ); let pool = rayon::ThreadPoolBuilder::new().use_current_thread().num_threads( args.concurrent as usize ).build().unwrap(); pool.scope diff --git a/module/move/willbe/src/entity/version.rs b/module/move/willbe/src/entity/version.rs index 7018f00481..085b7494ee 100644 --- a/module/move/willbe/src/entity/version.rs +++ b/module/move/willbe/src/entity/version.rs @@ -1,6 +1,8 @@ /// Define a private namespace for all its items. +#[ allow( clippy::std_instead_of_alloc, clippy::std_instead_of_core ) ] mod private { + #[ allow( clippy::wildcard_imports ) ] use crate::*; use std:: @@ -17,7 +19,7 @@ mod private use package::Package; use { error::untyped::format_err, iter::Itertools }; - /// Wrapper for a SemVer structure + /// Wrapper for a `SemVer` structure #[ derive( Debug, Clone, Eq, PartialEq, Ord, PartialOrd ) ] pub struct Version( SemVersion ); @@ -55,7 +57,7 @@ mod private { fn fmt( &self, f : &mut fmt::Formatter< '_ > ) -> fmt::Result { - write!( f, "{}", self.0.to_string() ) + write!( f, "{}", self.0 ) } } @@ -64,6 +66,7 @@ mod private /// Bump a version with default strategy /// /// This function increases first not 0 number + #[ must_use ] pub fn bump( self ) -> Self { let mut ver = self.0; @@ -187,6 +190,9 @@ mod private /// /// Returns a result containing the extended bump report if successful. /// + /// + /// # Errors + /// qqq: doc // qqq : should be typed error, apply err_with // qqq : don't use 1-prameter Result pub fn bump( o : BumpOptions ) -> Result< ExtendedBumpReport > @@ -211,7 +217,7 @@ mod private { // let data = package_manifest.data.as_mut().unwrap(); let data = &mut package_manifest.data; - data[ "package" ][ "version" ] = value( &o.new_version.to_string() ); + data[ "package" ][ "version" ] = value( o.new_version.to_string() ); package_manifest.store()?; } report.changed_files = vec![ manifest_file ]; @@ -226,9 +232,9 @@ mod private let item = if let Some( item ) = data.get_mut( "package" ) { item } else if let Some( item ) = data.get_mut( "workspace" ) { item } else { return Err( format_err!( "{report:?}\nThe manifest nor the package and nor the workspace" ) ); }; - if let Some( dependency ) = item.get_mut( "dependencies" ).and_then( | ds | ds.get_mut( &name ) ) + if let Some( dependency ) = item.get_mut( "dependencies" ).and_then( | ds | ds.get_mut( name ) ) { - if let Some( previous_version ) = dependency.get( "version" ).and_then( | v | v.as_str() ).map( | v | v.to_string() ) + if let Some( previous_version ) = dependency.get( "version" ).and_then( | v | v.as_str() ).map( std::string::ToString::to_string ) { if previous_version.starts_with('~') { @@ -256,6 +262,12 @@ mod private /// # Returns /// /// Returns `Ok(())` if the version is reverted successfully. Returns `Err` with an error message if there is any issue with reverting the version. + /// + /// # Errors + /// qqq: doc + /// + /// # Panics + /// qqq: doc // qqq : don't use 1-prameter Result pub fn revert( report : &ExtendedBumpReport ) -> error::untyped::Result< () > // qqq : use typed error { @@ -267,13 +279,13 @@ mod private { if let Some( dependency ) = item_maybe_with_dependencies.get_mut( "dependencies" ).and_then( | ds | ds.get_mut( name ) ) { - if let Some( current_version ) = dependency.get( "version" ).and_then( | v | v.as_str() ).map( | v | v.to_string() ) + if let Some( current_version ) = dependency.get( "version" ).and_then( | v | v.as_str() ).map( std::string::ToString::to_string ) { let version = &mut dependency[ "version" ]; if let Some( current_version ) = current_version.strip_prefix( '~' ) { if current_version != new_version { return Err( format_err!( "The current version of the package does not match the expected one. Expected: `{new_version}` Current: `{}`", version.as_str().unwrap_or_default() ) ); } - *version = value( format!( "~{}", old_version ) ); + *version = value( format!( "~{old_version}" ) ); } else { @@ -327,6 +339,12 @@ mod private /// # Returns : /// - `Ok` - the new version number as a string; /// - `Err` - if the manifest file cannot be read, written, parsed. + /// + /// # Errors + /// qqq: doc + /// + /// # Panics + /// qqq: doc pub fn manifest_bump( manifest : &mut Manifest, dry : bool ) -> Result< BumpReport, manifest::ManifestError > { let mut report = BumpReport::default(); diff --git a/module/move/willbe/src/entity/workspace.rs b/module/move/willbe/src/entity/workspace.rs index 3fc37828fd..945e70ba23 100644 --- a/module/move/willbe/src/entity/workspace.rs +++ b/module/move/willbe/src/entity/workspace.rs @@ -1,5 +1,7 @@ +#[ allow( clippy::std_instead_of_alloc, clippy::std_instead_of_core ) ] mod private { + #[ allow( clippy::wildcard_imports ) ] use crate::*; // qqq : for Bohdan : bad @@ -97,6 +99,10 @@ mod private } /// Returns the path to workspace root + /// + /// # Panics + /// qqq: doc + #[ must_use ] pub fn workspace_root( &self ) -> CrateDir { // Safe because workspace_root.as_std_path() is always a path to a directory @@ -104,13 +110,17 @@ mod private } /// Returns the path to target directory + #[ must_use ] pub fn target_directory( &self ) -> &std::path::Path { self.metadata.target_directory.as_std_path() } /// Find a package by its manifest file path - pub fn package_find_by_manifest< 'a, P >( &'a self, manifest_file : P ) -> Option< WorkspacePackageRef< 'a > > + /// + /// # Panics + /// qqq: doc + pub fn package_find_by_manifest< P >( &self, manifest_file : P ) -> Option< WorkspacePackageRef< '_ > > where P : AsRef< std::path::Path >, { @@ -120,7 +130,8 @@ mod private } /// Filter of packages. - pub fn packages_which< 'a >( &'a self ) -> PackagesFilterFormer< 'a > + #[ must_use ] + pub fn packages_which( &self ) -> PackagesFilterFormer< '_ > { // PackagesFilter::new( self ) PackagesFilter::former().workspace( self ) @@ -208,12 +219,13 @@ mod private Self { workspace, - crate_dir : Default::default(), - manifest_file : Default::default(), + crate_dir : Box::default(), + manifest_file : Box::default(), } } #[ inline( always ) ] + #[ allow( clippy::unused_self ) ] pub fn iter( &'a self ) -> impl Iterator< Item = WorkspacePackageRef< 'a > > + Clone { @@ -247,9 +259,8 @@ mod private { if !formed.crate_dir.include( p ) { return false }; if !formed.manifest_file.include( p ) { return false }; - return true; + true }) - .clone() // .unwrap() // let filter_crate_dir = if Some( crate_dir ) = self.crate_dir diff --git a/module/move/willbe/src/entity/workspace_graph.rs b/module/move/willbe/src/entity/workspace_graph.rs index 9d129fdf07..11b592520f 100644 --- a/module/move/willbe/src/entity/workspace_graph.rs +++ b/module/move/willbe/src/entity/workspace_graph.rs @@ -1,8 +1,11 @@ mod private { + #[ allow( clippy::wildcard_imports ) ] use crate::*; /// Returns a graph of packages. + #[ allow( clippy::type_complexity ) ] + #[ must_use ] pub fn graph( workspace : &Workspace ) -> petgraph::Graph< String, String > { let packages = workspace.packages(); diff --git a/module/move/willbe/src/entity/workspace_md_extension.rs b/module/move/willbe/src/entity/workspace_md_extension.rs index a6d10dd9cb..afbc2442a9 100644 --- a/module/move/willbe/src/entity/workspace_md_extension.rs +++ b/module/move/willbe/src/entity/workspace_md_extension.rs @@ -1,6 +1,8 @@ /// Define a private namespace for all its items. +#[ allow( clippy::std_instead_of_alloc, clippy::std_instead_of_core ) ] mod private { + #[ allow( clippy::wildcard_imports ) ] use crate::*; /// Md's extension for workspace @@ -15,7 +17,7 @@ mod private /// Return the repository url fn repository_url( &self ) -> Option< String >; - /// Return the workspace_name + /// Return the `workspace_name` fn workspace_name( &self ) -> Option< String >; } @@ -27,7 +29,7 @@ mod private .metadata .workspace_metadata[ "discord_url" ] .as_str() - .map( | url | url.to_string() ) + .map( std::string::ToString::to_string ) } fn master_branch( &self ) -> Option< String > @@ -37,7 +39,7 @@ mod private .workspace_metadata .get( "master_branch" ) .and_then( | b | b.as_str() ) - .map( | b | b.to_string() ) + .map( std::string::ToString::to_string ) } fn repository_url( &self ) -> Option< String > @@ -47,7 +49,7 @@ mod private .workspace_metadata .get( "repo_url" ) .and_then( | b | b.as_str() ) - .map( | b | b.to_string() ) + .map( std::string::ToString::to_string ) } fn workspace_name( &self ) -> Option< String > @@ -57,7 +59,7 @@ mod private .workspace_metadata .get( "workspace_name" ) .and_then( | b | b.as_str() ) - .map( | b | b.to_string() ) + .map( std::string::ToString::to_string ) } } diff --git a/module/move/willbe/src/entity/workspace_package.rs b/module/move/willbe/src/entity/workspace_package.rs index 6ecada7108..0cbb271103 100644 --- a/module/move/willbe/src/entity/workspace_package.rs +++ b/module/move/willbe/src/entity/workspace_package.rs @@ -1,5 +1,7 @@ +#[ allow( clippy::std_instead_of_alloc, clippy::std_instead_of_core ) ] mod private { + #[ allow( clippy::wildcard_imports ) ] use crate::*; use macros::kw; use collection::BTreeMap; @@ -12,7 +14,7 @@ mod private // xxx : qqq : Deref, DerefMut, AsRef, AsMut - /// Facade for cargo_metadata::Package + /// Facade for `cargo_metadata::Package` #[ derive( Debug, Clone, Copy ) ] #[ repr( transparent ) ] pub struct WorkspacePackageRef< 'a > @@ -35,6 +37,7 @@ mod private impl< 'a > WorkspacePackageRef< 'a > { /// The name field as given in the Cargo.toml + #[ must_use ] pub fn name( &'a self ) -> &'a str { &self.inner.name @@ -56,12 +59,21 @@ mod private } /// Path to the manifest Cargo.toml + /// + /// # Errors + /// qqq: doc pub fn manifest_file( &self ) -> Result< ManifestFile, PathError > { self.inner.manifest_path.as_path().try_into() } /// Path to the directory with manifest Cargo.toml. + /// + /// # Errors + /// qqq: doc + /// + /// # Panics + /// qqq: docs pub fn crate_dir( &self ) -> Result< CrateDir, PathError > { // SAFE because `manifest_path containing the Cargo.toml` @@ -69,6 +81,7 @@ mod private } /// The version field as specified in the Cargo.toml + #[ must_use ] pub fn version( &self ) -> semver::Version { self.inner.version.clone() @@ -77,6 +90,7 @@ mod private /// List of registries to which this package may be published (derived from the publish field). /// Publishing is unrestricted if None, and forbidden if the Vec is empty. /// This is always None if running with a version of Cargo older than 1.39. + #[ must_use ] pub fn publish( &self ) -> Option< &Vec< String > > { self.inner.publish.as_ref() @@ -105,18 +119,21 @@ mod private /// assert_eq!( package_metadata.some_value, 42 ); /// } /// ``` + #[ must_use ] pub fn metadata( &self ) -> &Value { &self.inner.metadata } /// The repository URL as specified in the Cargo.toml + #[ must_use ] pub fn repository( &self ) -> Option< &String > { self.inner.repository.as_ref() } /// Features provided by the crate, mapped to the features required by that feature. + #[ must_use ] pub fn features( &self ) -> &BTreeMap< String, Vec< String > > { &self.inner.features @@ -130,7 +147,7 @@ mod private self.inner.targets.iter().map( | target | { let src_path = &target.src_path; - let source : SourceFile = src_path.try_into().expect( &format!( "Illformed path to source file {src_path}" ) ); + let source : SourceFile = src_path.try_into().unwrap_or_else( | _ | panic!( "Illformed path to source file {src_path}" ) ); // println!( " -- {:?} {:?}", source, target.kind ); source }) @@ -166,7 +183,7 @@ mod private impl< 'a > AsCode for WorkspacePackageRef< 'a > { - fn as_code< 'b >( &'b self ) -> std::io::Result< Cow< 'b, str > > + fn as_code( &self ) -> std::io::Result< Cow< '_, str > > { let mut results : Vec< String > = Vec::new(); // zzz : introduce formatter @@ -178,9 +195,9 @@ mod private .as_ref() .with_extension( "" ) .file_name() - .expect( &format!( "Cant get file name of path {}", source.as_ref().display() ) ) + .unwrap_or_else( || panic!( "Cant get file name of path {}", source.as_ref().display() ) ) .to_string_lossy() - .replace( ".", "_" ); + .replace( '.', "_" ); if kw::is( &filename ) { @@ -190,7 +207,7 @@ mod private // qqq : xxx : use callbacks instead of expect results.push( format!( "// === Begin of File {}", source.as_ref().display() ) ); - results.push( format!( "mod {}\n{{\n", filename ) ); + results.push( format!( "mod {filename}\n{{\n" ) ); results.push( code ); results.push( "\n}".to_string() ); results.push( format!( "// === End of File {}", source.as_ref().display() ) ); diff --git a/module/move/willbe/src/lib.rs b/module/move/willbe/src/lib.rs index 27cf2d036a..a8a72e3d47 100644 --- a/module/move/willbe/src/lib.rs +++ b/module/move/willbe/src/lib.rs @@ -8,6 +8,7 @@ pub use mod_interface::mod_interface; /// Define a private namespace for all its items. mod private { + #[ allow( clippy::wildcard_imports ) ] use crate::*; /// Takes the command line arguments and perform associated function(s). @@ -15,6 +16,9 @@ mod private /// It then terminates the program with an exit code of 1 to indicate an error due to the lack of input. /// /// Do not support interactive mode. + /// + /// # Errors + /// qqq: doc pub fn run( args : Vec< String > ) -> Result< (), error::untyped::Error > { #[ cfg( feature = "tracing" ) ] diff --git a/module/move/willbe/src/tool/cargo.rs b/module/move/willbe/src/tool/cargo.rs index 5a3974ddd9..a8f926860a 100644 --- a/module/move/willbe/src/tool/cargo.rs +++ b/module/move/willbe/src/tool/cargo.rs @@ -1,9 +1,11 @@ /// Define a private namespace for all its items. +#[ allow( clippy::std_instead_of_alloc, clippy::std_instead_of_core ) ] mod private { + #[ allow( clippy::wildcard_imports ) ] use crate::*; - #[ allow( unused_imports ) ] + #[ allow( unused_imports, clippy::wildcard_imports ) ] use crate::tool::*; use std::ffi::OsString; @@ -27,6 +29,7 @@ mod private /// The `PackOptions` struct encapsulates various options that can be configured when packaging a project, /// including the path to the project, the distribution channel, and various flags for controlling the behavior of the packaging process. #[ derive( Debug, Former, Clone ) ] + #[ allow( clippy::struct_excessive_bools ) ] pub struct PackOptions { /// The path to the project to be packaged. @@ -73,6 +76,7 @@ mod private impl PackOptions { + #[ allow( clippy::if_not_else ) ] fn to_pack_args( &self ) -> Vec< String > { [ "run".to_string(), self.channel.to_string(), "cargo".into(), "package".into() ] @@ -170,7 +174,7 @@ mod private command : format!( "{program} {}", options.join( " " ) ), out : String::new(), err : String::new(), - current_path: args.path.to_path_buf(), + current_path: args.path.clone(), error: Ok( () ), } ) @@ -248,7 +252,7 @@ mod private command : format!( "{program} {}", arguments.join( " " ) ), out : String::new(), err : String::new(), - current_path: args.path.to_path_buf(), + current_path: args.path.clone(), error: Ok( () ), } ) @@ -257,7 +261,7 @@ mod private { let mut results = Vec::with_capacity( args.retry_count + 1 ); let run_args : Vec< _ > = arguments.into_iter().map( OsString::from ).collect(); - for _ in 0 .. args.retry_count + 1 + for _ in 0 ..=args.retry_count { let result = process::Run::former() .bin_path( program ) diff --git a/module/move/willbe/src/tool/files.rs b/module/move/willbe/src/tool/files.rs index 95f19de887..5b70f48535 100644 --- a/module/move/willbe/src/tool/files.rs +++ b/module/move/willbe/src/tool/files.rs @@ -1,8 +1,9 @@ /// Define a private namespace for all its items. +#[ allow( clippy::std_instead_of_alloc, clippy::std_instead_of_core ) ] mod private { - #[ allow( unused_imports ) ] + #[ allow( unused_imports, clippy::wildcard_imports ) ] use crate::tool::*; use std::path::{ Path, PathBuf }; @@ -10,8 +11,11 @@ mod private /// /// Find paths. /// + /// # Panics + /// qqq: doc /* xxx : check */ + #[ allow( clippy::useless_conversion ) ] pub fn find< P, S >( base_dir : P, patterns : &[ S ] ) -> Vec< PathBuf > where P : AsRef< Path >, @@ -27,6 +31,7 @@ mod private } /// Check if path is valid. + #[ must_use ] pub fn valid_is( path : &str ) -> bool { std::fs::metadata( path ).is_ok() diff --git a/module/move/willbe/src/tool/git.rs b/module/move/willbe/src/tool/git.rs index 9e240c804b..27c9743f4d 100644 --- a/module/move/willbe/src/tool/git.rs +++ b/module/move/willbe/src/tool/git.rs @@ -1,11 +1,14 @@ /// Define a private namespace for all its items. +#[ allow( clippy::std_instead_of_alloc, clippy::std_instead_of_core ) ] mod private { - #[ allow( unused_imports ) ] + #[ allow( unused_imports, clippy::wildcard_imports ) ] use crate::tool::*; use std::ffi::OsString; use std::path::Path; + + #[ allow( clippy::wildcard_imports ) ] use process_tools::process::*; // use error::err; // qqq : group dependencies @@ -31,7 +34,7 @@ mod private Os : AsRef< [ O ] >, O : AsRef< str >, { - let objects = objects.as_ref().iter().map( | x | x.as_ref() ); + let objects = objects.as_ref().iter().map( std::convert::AsRef::as_ref ); // qqq : for Bohdan : don't enlarge length of lines artificially let ( program, args ) : ( _, Vec< _ > ) = ( "git", Some( "add" ).into_iter().chain( objects ).collect() ); @@ -164,6 +167,9 @@ mod private /// # Returns : /// This function returns a `Result` containing a `Report` if the command is executed successfully. The `Report` contains the command executed, the output /// git reset command wrapper + /// + /// # Errors + /// qqq: doc // qqq : should be typed error, apply err_with @@ -181,7 +187,7 @@ mod private .into_iter() .chain( if hard { Some( "--hard" ) } else { None } ) .map( String::from ) - .chain( Some( format!( "HEAD~{}", commits_count ) ) ) + .chain( Some( format!( "HEAD~{commits_count}" ) ) ) .collect() ); @@ -218,6 +224,9 @@ mod private /// # Returns /// /// A `Result` containing a `Report`, which represents the result of the command execution. + /// + /// # Errors + /// qqq: doc // qqq : should be typed error, apply err_with // qqq : don't use 1-prameter Result diff --git a/module/move/willbe/src/tool/graph.rs b/module/move/willbe/src/tool/graph.rs index dec62fdc9e..e7e06db908 100644 --- a/module/move/willbe/src/tool/graph.rs +++ b/module/move/willbe/src/tool/graph.rs @@ -1,7 +1,8 @@ /// Define a private namespace for all its items. +#[ allow( clippy::std_instead_of_alloc, clippy::std_instead_of_core ) ] mod private { - #[ allow( unused_imports ) ] + #[ allow( unused_imports, clippy::wildcard_imports ) ] use crate::*; // use crate::tool::*; @@ -21,6 +22,7 @@ mod private algo::toposort as pg_toposort, }; use petgraph::graph::NodeIndex; + #[ allow( clippy::wildcard_imports ) ] use petgraph::prelude::*; use error:: @@ -45,6 +47,11 @@ mod private /// /// Returns : /// The graph with all accepted packages + /// + /// # Panics + /// qqq: doc + #[ allow( clippy::implicit_hasher ) ] + #[ must_use ] pub fn construct< PackageIdentifier > ( packages : &HashMap @@ -92,6 +99,10 @@ mod private /// /// # Panics /// If there is a cycle in the dependency graph + /// + /// # Errors + /// qqq: doc + #[ allow( clippy::needless_pass_by_value ) ] pub fn toposort< 'a, PackageIdentifier : Clone + std::fmt::Debug > ( graph : Graph< &'a PackageIdentifier, &'a PackageIdentifier > @@ -123,6 +134,11 @@ mod private /// # Returns /// /// The function returns a vector of vectors, where each inner vector represents a group of nodes that can be executed in parallel. Tasks within each group are sorted in topological order. + /// + /// # Panics + /// qqq: doc + #[ must_use ] + #[ allow( clippy::needless_pass_by_value ) ] pub fn topological_sort_with_grouping< 'a, PackageIdentifier : Clone + std::fmt::Debug > ( graph : Graph< &'a PackageIdentifier, &'a PackageIdentifier > @@ -136,7 +152,7 @@ mod private } let mut roots = VecDeque::new(); - for ( node, °ree ) in in_degree.iter() + for ( node, °ree ) in &in_degree { if degree == 0 { @@ -194,6 +210,10 @@ mod private /// /// # Constraints /// * `N` must implement the `PartialEq` trait. + /// + /// # Panics + /// qqq: doc + #[ allow( clippy::single_match, clippy::map_entry ) ] pub fn subgraph< N, E >( graph : &Graph< N, E >, roots : &[ N ] ) -> Graph< NodeIndex, EdgeIndex > where N : PartialEq< N >, @@ -215,7 +235,7 @@ mod private } } - for ( _, sub_node_id ) in &node_map + for sub_node_id in node_map.values() { let node_id_graph = subgraph[ *sub_node_id ]; @@ -246,11 +266,18 @@ mod private /// # Returns /// /// A new `Graph` with the nodes that are not required to be published removed. + /// + /// # Errors + /// qqq: doc + /// + /// # Panics + /// qqq: doc // qqq : for Bohdan : typed error - pub fn remove_not_required_to_publish< 'a > + #[ allow( clippy::single_match, clippy::needless_pass_by_value, clippy::implicit_hasher ) ] + pub fn remove_not_required_to_publish ( - package_map : &HashMap< String, Package< 'a > >, + package_map : &HashMap< String, Package< '_ > >, graph : &Graph< String, String >, roots : &[ String ], temp_path : Option< PathBuf >, diff --git a/module/move/willbe/src/tool/http.rs b/module/move/willbe/src/tool/http.rs index 81e13a2d74..f62f86005f 100644 --- a/module/move/willbe/src/tool/http.rs +++ b/module/move/willbe/src/tool/http.rs @@ -1,7 +1,8 @@ /// Define a private namespace for all its items. +#[ allow( clippy::std_instead_of_alloc, clippy::std_instead_of_core ) ] mod private { - #[ allow( unused_imports ) ] + #[ allow( unused_imports, clippy::wildcard_imports ) ] use crate::tool::*; use std:: @@ -16,6 +17,12 @@ mod private /// /// Get data of remote package. /// + /// # Errors + /// qqq: doc + /// + /// # Panics + /// qqq: docs + /// // qqq : typed error pub fn download< 'a >( name : &'a str, version : &'a str ) -> error::untyped::Result< Vec< u8 > > { @@ -24,7 +31,7 @@ mod private .timeout_write( Duration::from_secs( 5 ) ) .build(); let mut buf = String::new(); - write!( &mut buf, "https://static.crates.io/crates/{0}/{0}-{1}.crate", name, version )?; + write!( &mut buf, "https://static.crates.io/crates/{name}/{name}-{version}.crate" )?; let resp = agent.get( &buf[ .. ] ).call().context( "Get data of remote package" )?; diff --git a/module/move/willbe/src/tool/query.rs b/module/move/willbe/src/tool/query.rs index 4b8a14d10e..6724a0093f 100644 --- a/module/move/willbe/src/tool/query.rs +++ b/module/move/willbe/src/tool/query.rs @@ -1,7 +1,8 @@ /// Define a private namespace for all its items. +#[ allow( clippy::std_instead_of_alloc, clippy::std_instead_of_core ) ] mod private { - #[ allow( unused_imports ) ] + #[ allow( unused_imports, clippy::wildcard_imports ) ] use crate::tool::*; use std:: @@ -36,10 +37,12 @@ mod private if let Ok( i ) = s.parse::< i32 >() { Ok( Value::Int( i ) ) - } else if let Ok( b ) = s.parse::< bool >() + } + else if let Ok( b ) = s.parse::< bool >() { Ok( Value::Bool( b ) ) - } else + } + else { let s = s.trim_matches( '\'' ); Ok( Value::String( s.to_string() ) ) @@ -85,6 +88,7 @@ mod private /// assert!( result.contains( &Value::Int( 2 ) ) ); /// assert!( result.contains( &Value::Int( 3 ) ) ); /// ``` + #[ must_use ] pub fn into_vec( self ) -> Vec< Value > { match self @@ -111,6 +115,8 @@ mod private /// assert_eq!( HashMap::from( [ ( "1".to_string(), Value::Int( 1 ) ), ( "2".to_string(),Value::Int( 2 ) ), ( "3".to_string(),Value::Int( 3 ) ) ] ), unnamed_map ); /// assert_eq!( HashMap::from( [ ( "var0".to_string(), Value::Int( 1 ) ), ( "1".to_string(),Value::Int( 2 ) ), ( "2".to_string(),Value::Int( 3 ) ) ] ), mixed_map ); /// ``` + #[ allow( clippy::needless_pass_by_value ) ] + #[ must_use ] pub fn into_map( self, names : Vec< String > ) -> HashMap< String, Value > { match self @@ -148,6 +154,12 @@ mod private /// expected_map.insert( "key".to_string(), Value::String( r#"hello\'test\'test"#.into() ) ); /// assert_eq!( parse( r#"{ key : 'hello\'test\'test' }"# ).unwrap().into_map( vec![] ), expected_map ); /// ``` + /// + /// # Errors + /// qqq: doc + /// + /// # Panics + /// qqq: doc // qqq : use typed error pub fn parse( input_string : &str ) -> error::untyped::Result< ParseResult > { @@ -253,6 +265,7 @@ mod private } // qqq : use typed error + #[ allow( clippy::unnecessary_wraps ) ] fn parse_to_vec( input : Vec< String > ) -> error::untyped::Result< Vec< Value > > { Ok( input.into_iter().filter_map( | w | Value::from_str( w.trim() ).ok() ).collect() ) diff --git a/module/move/willbe/src/tool/repository.rs b/module/move/willbe/src/tool/repository.rs index c78d304661..7ec6fb7323 100644 --- a/module/move/willbe/src/tool/repository.rs +++ b/module/move/willbe/src/tool/repository.rs @@ -1,7 +1,7 @@ /// Define a private namespace for all its items. mod private { - #[ allow( unused_imports ) ] + #[ allow( unused_imports, clippy::wildcard_imports ) ] use crate::tool::*; /// Searches for a README file in specific subdirectories of the given directory path. @@ -9,6 +9,9 @@ mod private /// This function attempts to find a README file in the following subdirectories: ".github", /// the root directory, and "./docs". It returns the path to the first found README file, or /// `None` if no README file is found in any of these locations. + /// + /// # Errors + /// qqq: doc pub fn readme_path( dir_path : &std::path::Path ) -> Result< std::path::PathBuf, std::io::Error > { if let Some( path ) = readme_in_dir_find( &dir_path.join( ".github" ) ) diff --git a/module/move/willbe/src/tool/template.rs b/module/move/willbe/src/tool/template.rs index 91c804c8ce..c8ac11af89 100644 --- a/module/move/willbe/src/tool/template.rs +++ b/module/move/willbe/src/tool/template.rs @@ -1,7 +1,7 @@ /// Define a private namespace for all its items. mod private { - #[ allow( unused_imports ) ] + #[ allow( unused_imports, clippy::wildcard_imports ) ] use crate::tool::*; use std:: @@ -49,6 +49,9 @@ mod private /// # Returns /// /// A `Result` which is `Ok` if the files are created successfully, or an `Err` otherwise. + /// + /// # Errors + /// qqq: doc pub fn create_all( self, path : &path::Path ) -> error::untyped::Result< () > // qqq : use typed error { self.files.create_all( path, &self.values ) @@ -59,6 +62,7 @@ mod private /// # Returns /// /// A reference to `TemplateParameters`. + #[ must_use ] pub fn parameters( &self ) -> &TemplateParameters { &self.parameters @@ -71,7 +75,7 @@ mod private /// - `values`: The new `TemplateValues` to be set. pub fn set_values( &mut self, values : TemplateValues ) { - self.values = values + self.values = values; } /// Returns a reference to the template values. @@ -79,6 +83,7 @@ mod private /// # Returns /// /// A reference to `TemplateValues`. + #[ must_use ] pub fn get_values( &self ) -> &TemplateValues { &self.values @@ -130,6 +135,7 @@ mod private } /// Fetches mandatory parameters that are not set yet. + #[ must_use ] pub fn get_missing_mandatory( &self ) -> Vec< &str > { let values = self.get_values(); @@ -137,7 +143,7 @@ mod private .parameters() .list_mandatory() .into_iter() - .filter( | key | values.0.get( *key ).map( | val | val.as_ref() ).flatten().is_none() ) + .filter( | key | values.0.get( *key ).and_then( | val | val.as_ref() ).is_none() ) .collect() } } @@ -150,10 +156,13 @@ mod private /// Creates all files in provided path with values for required parameters. /// /// Consumes owner of the files. + /// + /// # Errors + /// qqq: doc fn create_all( self, path : &Path, values : &TemplateValues ) -> error::untyped::Result< () > // qqq : use typed error { let fsw = FileSystem; - for file in self.into_iter() + for file in self { file.create_file( &fsw, path, values )?; } @@ -172,17 +181,19 @@ mod private impl TemplateParameters { /// Extracts template values from props for parameters required for this template. + #[ must_use ] pub fn values_from_props( &self, props : &wca::executor::Props ) -> TemplateValues { let values = self.descriptors .iter() .map( | d | &d.parameter ) - .map( | param | ( param.clone(), props.get( param ).map( wca::Value::clone ) ) ) + .map( | param | ( param.clone(), props.get( param ).cloned() ) ) .collect(); TemplateValues( values ) } /// Get a list of all mandatory parameters. + #[ must_use ] pub fn list_mandatory( &self ) -> Vec< &str > { self.descriptors.iter().filter( | d | d.is_mandatory ).map( | d | d.parameter.as_str() ).collect() @@ -219,27 +230,28 @@ mod private /// Converts values to a serializable object. /// /// Currently only `String`, `Number`, and `Bool` are supported. + #[ must_use ] pub fn to_serializable( &self ) -> collection::BTreeMap< String, String > { self.0.iter().map ( | ( key, value ) | { - let value = value.as_ref().map + let value = value.as_ref().map_or ( + "___UNSPECIFIED___".to_string(), | value | { match value { wca::Value::String( val ) => val.to_string(), wca::Value::Number( val ) => val.to_string(), - wca::Value::Path( _ ) => "unsupported".to_string(), wca::Value::Bool( val ) => val.to_string(), + wca::Value::Path( _ ) | wca::Value::List( _ ) => "unsupported".to_string(), } } - ) - .unwrap_or( "___UNSPECIFIED___".to_string() ); + ); ( key.to_owned(), value ) } ) @@ -249,7 +261,7 @@ mod private /// Inserts new value if parameter wasn't initialized before. pub fn insert_if_empty( &mut self, key : &str, value : wca::Value ) { - if let None = self.0.get( key ).and_then( | v | v.as_ref() ) + if self.0.get( key ).and_then( | v | v.as_ref() ).is_none() { self.0.insert( key.into() , Some( value ) ); } @@ -258,7 +270,7 @@ mod private /// Interactively asks user to provide value for a parameter. pub fn interactive_if_empty( &mut self, key : &str ) { - if let None = self.0.get( key ).and_then( | v | v.as_ref() ) + if self.0.get( key ).and_then( | v | v.as_ref() ).is_none() { println! ("Parameter `{key}` is not set" ); let answer = wca::ask( "Enter value" ); @@ -299,7 +311,7 @@ mod private WriteMode::TomlExtend => { let instruction = FileReadInstruction { path : path.into() }; - if let Some(existing_contents) = fs.read( &instruction ).ok() + if let Ok( existing_contents ) = fs.read( &instruction ) { let document = contents.parse::< toml_edit::Document >().context( "Failed to parse template toml file" )?; let template_items = document.iter(); @@ -307,10 +319,10 @@ mod private let mut existing_document = existing_toml_contents.parse::< toml_edit::Document >().context( "Failed to parse existing toml file" )?; for ( template_key, template_item ) in template_items { - match existing_document.get_mut( &template_key ) + match existing_document.get_mut( template_key ) { - Some( item ) => *item = template_item.to_owned(), - None => existing_document[ &template_key ] = template_item.to_owned(), + Some( item ) => template_item.clone_into( item ), + None => template_item.clone_into( &mut existing_document[ template_key ] ), } } return Ok( existing_document.to_string() ); @@ -396,9 +408,13 @@ mod private pub trait FileSystemPort { /// Writing to file implementation. + /// # Errors + /// qqq: doc fn write( &self, instruction : &FileWriteInstruction ) -> error::untyped::Result< () >; // qqq : use typed error /// Reading from a file implementation. + /// # Errors + /// qqq: doc fn read( &self, instruction : &FileReadInstruction ) -> error::untyped::Result< Vec< u8 > >; // qqq : use typed error } diff --git a/module/move/willbe/src/tool/tree.rs b/module/move/willbe/src/tool/tree.rs index 3c1e0c670b..8525d0f2e0 100644 --- a/module/move/willbe/src/tool/tree.rs +++ b/module/move/willbe/src/tool/tree.rs @@ -1,3 +1,4 @@ +#[ allow( clippy::std_instead_of_alloc, clippy::std_instead_of_core ) ] mod private { use std::fmt::Write; @@ -26,7 +27,8 @@ mod private /// # Returns /// /// A new instance of `TreePrinter`. - pub fn new(info : &ListNodeReport) -> Self + #[ must_use ] + pub fn new(info : &ListNodeReport) -> Self { TreePrinter { @@ -44,15 +46,21 @@ mod private /// # Returns /// /// * A `Result` containing the formatted string or a `std::fmt::Error` if formatting fails. + /// + /// # Errors + /// qqq: doc + /// + /// # Panics + /// qqq: doc pub fn display_with_spacer( &self, spacer : &str ) -> Result< String, std::fmt::Error > { let mut f = String::new(); write!( f, "{}", self.info.name )?; if let Some( version ) = &self.info.version { write!( f, " {version}" )? } - if let Some( crate_dir ) = &self.info.crate_dir { write!( f, " {}", crate_dir )? } + if let Some( crate_dir ) = &self.info.crate_dir { write!( f, " {crate_dir}" )? } if self.info.duplicate { write!( f, "(*)" )? } - write!( f, "\n" )?; + writeln!( f )?; let mut new_spacer = format!( "{spacer}{} ", if self.info.normal_dependencies.len() < 2 { " " } else { self.symbols.down } ); @@ -72,7 +80,7 @@ mod private { let mut dev_dependencies_iter = self.info.dev_dependencies.iter(); let last = dev_dependencies_iter.next_back(); - write!( f, "{spacer}[dev-dependencies]\n" )?; + writeln!( f, "{spacer}[dev-dependencies]" )?; for dep in dev_dependencies_iter { write!( f, "{spacer}{}{} {}", self.symbols.tee, self.symbols.right, Self::display_with_spacer( &TreePrinter::new( dep ), &new_spacer )? )?; @@ -84,7 +92,7 @@ mod private { let mut build_dependencies_iter = self.info.build_dependencies.iter(); let last = build_dependencies_iter.next_back(); - write!( f, "{spacer}[build-dependencies]\n" )?; + writeln!( f, "{spacer}[build-dependencies]" )?; for dep in build_dependencies_iter { write!( f, "{spacer}{}{} {}", self.symbols.tee, self.symbols.right, Self::display_with_spacer( &TreePrinter::new( dep ), &new_spacer )? )?; @@ -146,15 +154,15 @@ mod private /// This field is a flag indicating whether the Node is a duplicate or not. pub duplicate : bool, /// A list that stores normal dependencies. - /// Each element in the list is also of the same 'ListNodeReport' type to allow + /// Each element in the list is also of the same '`ListNodeReport`' type to allow /// storage of nested dependencies. pub normal_dependencies : Vec< ListNodeReport >, /// A list that stores dev dependencies(dependencies required for tests or examples). - /// Each element in the list is also of the same 'ListNodeReport' type to allow + /// Each element in the list is also of the same '`ListNodeReport`' type to allow /// storage of nested dependencies. pub dev_dependencies : Vec< ListNodeReport >, /// A list that stores build dependencies. - /// Each element in the list is also of the same 'ListNodeReport' type to allow + /// Each element in the list is also of the same '`ListNodeReport`' type to allow /// storage of nested dependencies. pub build_dependencies : Vec< ListNodeReport >, } diff --git a/module/move/willbe/src/tool/url.rs b/module/move/willbe/src/tool/url.rs index 58c2792e4c..a7f76716c4 100644 --- a/module/move/willbe/src/tool/url.rs +++ b/module/move/willbe/src/tool/url.rs @@ -1,7 +1,7 @@ /// Define a private namespace for all its items. mod private { - #[ allow( unused_imports ) ] + #[ allow( unused_imports, clippy::wildcard_imports ) ] use crate::tool::*; use error::untyped:: @@ -11,15 +11,16 @@ mod private }; /// Extracts the repository URL from a full URL. + #[ must_use ] pub fn repo_url_extract( full_url : &str ) -> Option< String > { let parts : Vec< &str > = full_url.split( '/' ).collect(); - if parts.len() >= 4 && parts[ 0 ] == "https:" && parts[ 1 ] == "" && parts[ 2 ] == "github.com" + if parts.len() >= 4 && parts[ 0 ] == "https:" && parts[ 1 ].is_empty() && parts[ 2 ] == "github.com" { let user = parts[ 3 ]; let repo = parts[ 4 ]; - let repo_url = format!( "https://github.com/{}/{}", user, repo ); + let repo_url = format!( "https://github.com/{user}/{repo}" ); Some( repo_url ) } else @@ -29,8 +30,10 @@ mod private } /// Extracts the username and repository name from a given URL. + /// # Errors + /// qqq: doc // qqq : use typed error - pub fn git_info_extract( url : &String ) -> error::untyped::Result< String > + pub fn git_info_extract( url : &str ) -> error::untyped::Result< String > { let parts : Vec< &str > = url.split( '/' ).collect(); if parts.len() >= 2 diff --git a/module/move/willbe/tests/asset/single_module/Cargo.toml b/module/move/willbe/tests/asset/single_module/Cargo.toml index 7e5912d446..a78145c170 100644 --- a/module/move/willbe/tests/asset/single_module/Cargo.toml +++ b/module/move/willbe/tests/asset/single_module/Cargo.toml @@ -5,6 +5,7 @@ members = [ ] [workspace.metadata] +workspace_name = "single_module" master_branch = "test_branch" project_name = "test" repo_url = "https://github.com/Username/test" diff --git a/module/move/willbe/tests/inc/action_tests/readme_modules_headers_renew.rs b/module/move/willbe/tests/inc/action_tests/readme_modules_headers_renew.rs index 1d4d012d5d..eed7873ef5 100644 --- a/module/move/willbe/tests/inc/action_tests/readme_modules_headers_renew.rs +++ b/module/move/willbe/tests/inc/action_tests/readme_modules_headers_renew.rs @@ -32,7 +32,7 @@ fn tags_should_stay() let temp = arrange( "single_module" ); // Act - _ = action::main_header::action( CrateDir::try_from( temp.path() ).unwrap() ).unwrap(); + _ = action::readme_modules_headers_renew::readme_modules_headers_renew( CrateDir::try_from( temp.path() ).unwrap() ).unwrap(); // _ = action::main_header::action( CrateDir::try_from( temp.path() ).unwrap() ).unwrap(); let mut file = std::fs::File::open( temp.path().join( "test_module" ).join( "Readme.md" ) ).unwrap(); @@ -53,7 +53,7 @@ fn default_stability() let temp = arrange( "single_module" ); // Act - _ = action::main_header::action( CrateDir::try_from( temp.path() ).unwrap() ).unwrap(); + _ = action::readme_modules_headers_renew::readme_modules_headers_renew( CrateDir::try_from( temp.path() ).unwrap() ).unwrap(); let mut file = std::fs::File::open( temp.path().join( "test_module" ).join( "Readme.md" ) ).unwrap(); let mut actual = String::new(); @@ -72,7 +72,7 @@ fn docs() let temp = arrange( "single_module" ); // Act - _ = action::main_header::action( CrateDir::try_from( temp.path() ).unwrap() ).unwrap(); + _ = action::readme_modules_headers_renew::readme_modules_headers_renew( CrateDir::try_from( temp.path() ).unwrap() ).unwrap(); let mut file = std::fs::File::open( temp.path().join( "test_module" ).join( "Readme.md" ) ).unwrap(); let mut actual = String::new(); @@ -90,7 +90,7 @@ fn no_gitpod() let temp = arrange("single_module"); // Act - _ = action::main_header::action( CrateDir::try_from( temp.path() ).unwrap() ).unwrap(); + _ = action::readme_modules_headers_renew::readme_modules_headers_renew( CrateDir::try_from( temp.path() ).unwrap() ).unwrap(); let mut file = std::fs::File::open(temp.path().join("test_module").join("Readme.md")).unwrap(); let mut actual = String::new(); @@ -107,7 +107,7 @@ fn with_gitpod() let temp = arrange( "single_module_with_example" ); // Act - _ = action::main_header::action( CrateDir::try_from( temp.path() ).unwrap() ).unwrap(); + _ = action::readme_modules_headers_renew::readme_modules_headers_renew( CrateDir::try_from( temp.path() ).unwrap() ).unwrap(); let mut file = std::fs::File::open( temp.path().join( "module" ).join( "test_module" ).join( "Readme.md" ) ).unwrap(); let mut actual = String::new(); @@ -125,7 +125,7 @@ fn discord() let temp = arrange( "single_module" ); // Act - _ = action::main_header::action( CrateDir::try_from( temp.path() ).unwrap() ).unwrap(); + _ = action::readme_modules_headers_renew::readme_modules_headers_renew(CrateDir::try_from(temp.path()).unwrap()).unwrap(); let mut file = std::fs::File::open( temp.path().join( "test_module" ).join( "Readme.md" ) ).unwrap(); let mut actual = String::new(); @@ -143,7 +143,7 @@ fn status() let temp = arrange( "single_module" ); // Act - _ = action::main_header::action( CrateDir::try_from( temp.path() ).unwrap() ).unwrap(); + _ = action::readme_modules_headers_renew::readme_modules_headers_renew( CrateDir::try_from( temp.path() ).unwrap() ).unwrap(); let mut file = std::fs::File::open( temp.path().join( "test_module" ).join( "Readme.md" ) ).unwrap(); let mut actual = String::new(); @@ -161,13 +161,13 @@ fn idempotency() let temp = arrange( "single_module" ); // Act - _ = action::main_header::action( CrateDir::try_from( temp.path() ).unwrap() ).unwrap(); + _ = action::readme_modules_headers_renew::readme_modules_headers_renew( CrateDir::try_from( temp.path() ).unwrap() ).unwrap(); let mut file = std::fs::File::open( temp.path().join( "test_module" ).join( "Readme.md" ) ).unwrap(); let mut actual1 = String::new(); _ = file.read_to_string( &mut actual1 ).unwrap(); drop( file ); - _ = action::main_header::action( CrateDir::try_from( temp.path() ).unwrap() ).unwrap(); + _ = action::readme_modules_headers_renew::readme_modules_headers_renew( CrateDir::try_from( temp.path() ).unwrap() ).unwrap(); let mut file = std::fs::File::open( temp.path().join( "test_module" ).join( "Readme.md" ) ).unwrap(); let mut actual2 = String::new(); _ = file.read_to_string( &mut actual2 ).unwrap(); @@ -182,7 +182,7 @@ fn with_many_members_and_varius_config() { let temp = arrange( "three_packages" ); - _ = action::main_header::action( CrateDir::try_from( temp.path() ).unwrap() ).unwrap(); + _ = action::readme_modules_headers_renew::readme_modules_headers_renew( CrateDir::try_from( temp.path() ).unwrap() ).unwrap(); let mut file_b = std::fs::File::open( temp.path().join( "b" ).join( "Readme.md" ) ).unwrap(); let mut file_c = std::fs::File::open( temp.path().join( "c" ).join( "Readme.md" ) ).unwrap(); From b0a22e636021db47a3b21be60e3dbd58230a0de1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A1=D0=B5=D0=B2=D0=B0=20=D0=91=D0=B0=D0=BA=D1=83=D1=82?= =?UTF-8?q?=D0=BE=D0=B2?= <122987843+sevabakutov@users.noreply.github.com> Date: Mon, 30 Dec 2024 17:15:19 +0100 Subject: [PATCH 66/67] FIX GSPREAD: fix gspread structure (#1516) * added: error handling and nice output for cell set * fixed: cell get * fixed: error handling for all actions * added: help description * fixed gspread_cell_set action returning value * fixed: sheet name in description * fixed: commands examples * added mock for oauth2 * with_online added to tests * added third oauth2 endpoint * feature with_online * added mock tests * with_online fixed * fixed gspread_cells_set return type * fixed display * draft wrap_function * something bad * update_row function and documentation for some functions and structures were added --------- Co-authored-by: Vsevolod --- module/core/format_tools/src/format/print.rs | 2 + module/move/gspread/Cargo.toml | 4 + module/move/gspread/src/actions/gspread.rs | 150 ++++++++++++++++- .../gspread/src/actions/gspread_cell_get.rs | 25 ++- .../gspread/src/actions/gspread_cell_set.rs | 25 ++- .../gspread/src/actions/gspread_cells_set.rs | 157 +++++++++--------- .../gspread/src/actions/gspread_get_header.rs | 30 +++- .../gspread/src/actions/gspread_get_rows.rs | 26 ++- module/move/gspread/src/commands/gspread.rs | 27 ++- .../move/gspread/src/commands/gspread_cell.rs | 71 +++++--- .../gspread/src/commands/gspread_cells.rs | 45 +++-- .../gspread/src/commands/gspread_header.rs | 21 ++- .../move/gspread/src/commands/gspread_rows.rs | 17 +- module/move/gspread/src/debug.rs | 5 +- module/move/gspread/src/debug/row_wrapper.rs | 119 ++++++------- module/move/gspread/tests/inc/header_tests.rs | 1 + module/move/gspread/tests/mock/cell_tests.rs | 93 +++++++++++ module/move/gspread/tests/mock/cells_tests.rs | 67 ++++++++ .../move/gspread/tests/mock/header_tests.rs | 123 ++++++++++++++ module/move/gspread/tests/mock/mod.rs | 8 + module/move/gspread/tests/mock/oauth_tests.rs | 102 ++++++++++++ module/move/gspread/tests/mock/rows_tests.rs | 93 +++++++++++ module/move/gspread/tests/smoke_test.rs | 1 - module/move/gspread/tests/tests.rs | 9 +- 24 files changed, 984 insertions(+), 237 deletions(-) create mode 100644 module/move/gspread/tests/mock/cell_tests.rs create mode 100644 module/move/gspread/tests/mock/cells_tests.rs create mode 100644 module/move/gspread/tests/mock/header_tests.rs create mode 100644 module/move/gspread/tests/mock/mod.rs create mode 100644 module/move/gspread/tests/mock/oauth_tests.rs create mode 100644 module/move/gspread/tests/mock/rows_tests.rs diff --git a/module/core/format_tools/src/format/print.rs b/module/core/format_tools/src/format/print.rs index f1aa104c24..8ad31f189b 100644 --- a/module/core/format_tools/src/format/print.rs +++ b/module/core/format_tools/src/format/print.rs @@ -229,6 +229,8 @@ mod private #[ derive( Debug, Default ) ] pub struct RowDescriptor { + + /// Index of the row. pub irow : usize, /// Height of the row. diff --git a/module/move/gspread/Cargo.toml b/module/move/gspread/Cargo.toml index 8d1d86b4a3..21a63abb7b 100644 --- a/module/move/gspread/Cargo.toml +++ b/module/move/gspread/Cargo.toml @@ -18,6 +18,7 @@ name = "main" path = "src/bin/main.rs" [features] +with_online = [] default = [ "enabled" ] full = [ "enabled" ] enabled = [ @@ -44,6 +45,9 @@ error_tools = "0.19.0" derive_tools = { version = "0.32.0", features = ["full"] } serde_json = "1.0.132" regex = "1.11.1" +unicode-width = "0.2.0" [dev-dependencies] test_tools = { workspace = true } +httpmock = "0.7.0-rc.1" +reqwest = "0.12.9" \ No newline at end of file diff --git a/module/move/gspread/src/actions/gspread.rs b/module/move/gspread/src/actions/gspread.rs index 60b0fd980c..711547926d 100644 --- a/module/move/gspread/src/actions/gspread.rs +++ b/module/move/gspread/src/actions/gspread.rs @@ -10,13 +10,35 @@ mod private use error_tools::typed::Error; use derive_tools::AsRefStr; use crate::*; - use ser::DisplayFromStr; + use ser:: + { + DisplayFromStr, + JsonValue + }; + use std::collections::HashMap; + use google_sheets4::api:: + { + BatchUpdateValuesResponse, + BatchUpdateValuesRequest, + ValueRange + }; #[ ser::serde_as ] #[ derive( Debug, Error, AsRefStr, ser::Serialize ) ] #[ serde( tag = "type", content = "data" ) ] + + /// Represents errors that can occur while interacting with the Google Sheets API + /// or during related operations in the application. pub enum Error { + /// Represents an error returned by the Google Sheets API. + /// + /// # Details + /// This error occurs when the API returns a specific error message. + /// The error message from the Google Sheets API is stored and displayed. + /// + /// # Fields + /// - `google_sheets4::Error`: The raw error returned by the API. #[ error( "Google Sheets returned error:\n{0}" ) ] ApiError ( @@ -25,13 +47,75 @@ mod private google_sheets4::Error ), - #[ error( "Invalid URL format: {0}" ) ] + /// Represents an error that occurs while initializing Google Sheets Hub. + /// + /// # Details + /// This error indicates that the application failed to properly configure with the Google Sheets Hub. + /// + /// # Fields + /// - `String`: A detailed error message describing the issue. + #[ error( "Hub Error:\n{0}" ) ] + HubError + ( + String + ), + + /// Represents an error caused by an invalid URL format. + /// + /// # Details + /// This error occurs when the provided URL does not match the expected format + /// + /// # Fields + /// - `String`: The invalid URL or a message describing the issue. + #[ error( "Invalid URL format:\n{0}" ) ] InvalidUrl ( String ), + + /// Represents an error related to a cell in the spreadsheet. + /// + /// # Details + /// This error indicates that a cell was not got or updated + /// + /// # Fields + /// - `String`: A message describing the issue with the cell. + #[ error( "Cell error:\n{0}" ) ] + CellError + ( + String + ), + + /// Represents an error caused by invalid JSON input or parsing issues. + /// + /// # Details + /// This error occurs when the provided JSON data does not conform to the expected + /// structure or format. + /// + /// # Fields + /// - `String`: A detailed error message describing the JSON issue. + #[ error( "Invalid JSON format:\n{0}" ) ] + InvalidJSON + ( + String + ), + + /// Represents a generic parsing error. + /// + /// # Details + /// This error is raised when a string or other input cannot be parsed + /// into the expected format or structure. + /// + /// # Fields + /// - `String`: A message describing the parse error. + #[ error( "Parse error:\n{0}" ) ] + ParseError + ( + String + ) } + /// Retrive spreadsheet id from url pub fn get_spreadsheet_id_from_url ( url : &str @@ -53,6 +137,66 @@ mod private ) } + /// Function to update a row on a Google Sheet. + /// + /// It sends HTTP request to Google Sheets API and change row wich provided values. + /// + /// **Params** + /// - `spreadsheet_id` : Spreadsheet identifire. + /// - `sheet_name` : Sheet name. + /// - `row_key` : row's key. + /// - `row_key_val` : pairs of key value, where key is a column name and value is a new value. + /// + /// **Returns** + /// - `Result` + pub async fn update_row + ( + spreadsheet_id : &str, + sheet_name : &str, + row_key : &str, + row_key_val : HashMap< String, String > + ) -> Result< BatchUpdateValuesResponse > + { + let secret = Secret::read(); + let hub = hub(&secret) + .await + .map_err( | _ | { + Error::HubError( format!( "Failed to create a hub. Ensure that you have a .env file with Secrets" ) ) + })?; + + let mut value_ranges = Vec::with_capacity( row_key_val.len() ); + + for ( col_name, value ) in row_key_val { + value_ranges.push + ( + ValueRange + { + major_dimension: Some( String::from( "ROWS" ) ), + values: Some( vec![ vec![ JsonValue::String( value ) ] ] ), + range: Some( format!( "{}!{}{}", sheet_name, col_name, row_key ) ), + } + ) + } + + let req = BatchUpdateValuesRequest + { + value_input_option: Some( "USER_ENTERED".to_string() ), + data: Some( value_ranges ), + include_values_in_response: Some( true ), + ..Default::default() + }; + + match hub + .spreadsheets() + .values_batch_update( req, spreadsheet_id ) + .doit() + .await + { + Ok( ( _, response ) ) => Ok( response ), + Err( error ) => Err( Error::ApiError( error ) ), + } + } + pub type Result< T > = core::result::Result< T, Error >; } @@ -60,7 +204,9 @@ crate::mod_interface! { own use { + Error, Result, + update_row, get_spreadsheet_id_from_url, }; } \ No newline at end of file diff --git a/module/move/gspread/src/actions/gspread_cell_get.rs b/module/move/gspread/src/actions/gspread_cell_get.rs index 3a4d6b1be3..effa3a9170 100644 --- a/module/move/gspread/src/actions/gspread_cell_get.rs +++ b/module/move/gspread/src/actions/gspread_cell_get.rs @@ -6,8 +6,14 @@ mod private { + + use crate::*; - use actions::gspread::Result; + use actions::gspread:: + { + Error, + Result + }; use client::SheetsType; use ser::JsonValue; @@ -19,18 +25,19 @@ mod private cell_id : &str, ) -> Result< JsonValue > { - let result = hub + match hub .spreadsheets() .values_get( spreadsheet_id, format!( "{}!{}", table_name, cell_id ).as_str() ) .doit() - .await? - .1 - .values; - - match result + .await { - Some( values ) => Ok( values.get( 0 ).unwrap().get( 0 ).unwrap().clone() ), - None => Ok( JsonValue::Null.clone() ) + Ok( (_, response ) ) => + match response.values + { + Some( values ) => Ok( values.get( 0 ).unwrap().get( 0 ).unwrap().clone() ), + None => Ok( JsonValue::Null.clone() ) + } + Err( error ) => Err( Error::ApiError( error ) ) } } diff --git a/module/move/gspread/src/actions/gspread_cell_set.rs b/module/move/gspread/src/actions/gspread_cell_set.rs index 818a667f1c..5743a45f97 100644 --- a/module/move/gspread/src/actions/gspread_cell_set.rs +++ b/module/move/gspread/src/actions/gspread_cell_set.rs @@ -9,7 +9,11 @@ mod private { use google_sheets4::api::ValueRange; use crate::*; - use actions::gspread::Result; + use actions::gspread:: + { + Result, + Error + }; use client::SheetsType; use ser::JsonValue; @@ -30,17 +34,24 @@ mod private ..ValueRange::default() }; - let result = hub + match hub .spreadsheets() .values_update( value_range, spreadsheet_id, format!( "{}!{}", table_name, cell_id ).as_str() ) .value_input_option( "USER_ENTERED" ) .doit() - .await? - .1 - .updated_cells - .unwrap(); + .await + { + Ok( ( _, response) ) => + { + match response.updated_cells + { + Some( number ) => Ok( number ), + None => Err( Error::CellError( "Some problem with cell updating".to_string() ) ) + } + } + Err( error) => Err( Error::ApiError( error ) ) + } - Ok( result ) } } diff --git a/module/move/gspread/src/actions/gspread_cells_set.rs b/module/move/gspread/src/actions/gspread_cells_set.rs index a6528b6c4b..1099a613d4 100644 --- a/module/move/gspread/src/actions/gspread_cells_set.rs +++ b/module/move/gspread/src/actions/gspread_cells_set.rs @@ -5,35 +5,75 @@ mod private { use crate::*; - use google_sheets4::api:: + use actions::gspread:: { - BatchUpdateValuesRequest, - ValueRange - }; - use ser:: - { - Deserialize, - JsonValue + Error, + Result, + update_row }; + use ser:: Deserialize; use std::collections::HashMap; - /// Structure for --json value + /// Structure to keep rows key and new values for cells updating. #[ derive( Deserialize, Debug ) ] - struct ParsedJson + struct ParsedJson< 'a > { - #[ serde( flatten ) ] - columns : HashMap< String, String > + row_key : &'a str, + row_key_val : HashMap< String, String > } - /// Parse --json value - fn parse_json + /// Function to parse `--json` flag. + /// + /// It retirive `--select-row-by-key` flag from json and set it to `row_key` field. + /// Other pairs it set to `row_key_val` + /// + /// **Returns** + /// - `ParsedJson` object + fn parse_json< 'a > ( - json_str : &str - ) -> Result< ParsedJson, String > + json_str : &'a str, + select_row_by_key : &str, + ) -> Result< ParsedJson< 'a > > { - serde_json::from_str::< ParsedJson >( json_str ).map_err + let mut parsed_json: HashMap< String, String > = serde_json::from_str( json_str ) + .map_err( | error | Error::InvalidJSON( format!( "Failed to parse JSON: {}", error ) ) )?; + + let row_key = if let Some( row_key ) = parsed_json.remove( select_row_by_key ) + { + Box::leak( row_key.into_boxed_str() ) + } + else + { + return Err + ( + Error::InvalidJSON + ( + format!( "Key '{}' not found in JSON", select_row_by_key) + ) + ); + }; + + for ( col_name, _ ) in &parsed_json + { + if !col_name.chars().all( | c | c.is_alphabetic() && c.is_uppercase() ) + { + return Err + ( + Error::InvalidJSON + ( + format!( "Invalid column name: {}. Allowed only uppercase alphabetic letters (A-Z)", col_name ) + ) + ); + } + }; + + Ok ( - | err | format!( "Failed to parse JSON: {}", err ) + ParsedJson + { + row_key : row_key, + row_key_val : parsed_json + } ) } @@ -42,7 +82,7 @@ mod private fn check_select_row_by_key ( key : &str - ) -> Result< (), String > + ) -> Result< () > { let keys = vec![ "id" ]; if keys.contains( &key ) @@ -51,79 +91,42 @@ mod private } else { - Err( format!( "Invalid select_row_by_key: '{}'. Allowed keys: {:?}", key, keys ) ) - } - } - - fn is_all_uppercase_letters - ( - s : &str - ) -> Result< (), String > - { - if s.chars().all( | c | c.is_ascii_uppercase() ) - { - Ok( () ) - } - else - { - Err( format!( "The string '{}' contains invalid characters. Only uppercase letters (A-Z) are allowed.", s ) ) + Err + ( + Error::ParseError( format!( "Invalid select_row_by_key: '{}'. Allowed keys: {:?}", key, keys ) ) + ) } } pub async fn action ( - hub : &SheetsType, select_row_by_key : &str, json_str : &str, spreadsheet_id : &str, table_name : &str - ) -> Result< String, String > + ) -> Result< i32 > { check_select_row_by_key( select_row_by_key )?; - let mut pairs = parse_json( json_str )?; - - let row_id = pairs - .columns - .remove( select_row_by_key ) - .ok_or_else( || format!( "Key '{}' not found in JSON", select_row_by_key ) )?; - - let mut value_ranges= Vec::new(); - - for ( key, value ) in pairs.columns.into_iter() - { - is_all_uppercase_letters( key.as_str() )?; - value_ranges.push - ( - ValueRange - { - range: Some( format!( "{}!{}{}", table_name, key, row_id ) ), - values: Some( vec![ vec![ JsonValue::String( value.to_string() ) ] ] ), - ..Default::default() - } - ); - }; - - let req = BatchUpdateValuesRequest + match parse_json( json_str, select_row_by_key ) { - value_input_option: Some( "USER_ENTERED".to_string() ), - data: Some( value_ranges ), - include_values_in_response: Some( true ), - ..Default::default() - }; - - let result = hub - .spreadsheets() - .values_batch_update( req, spreadsheet_id ) - .doit() - .await; - - match result - { - Ok( _ ) => Ok( format!( "Cells were sucsessfully updated!" ) ), - Err( error ) => Err( format!( "{}", error ) ) + Ok( parsed_json ) => + match update_row( spreadsheet_id, table_name, parsed_json.row_key, parsed_json.row_key_val ).await + { + Ok( response ) => + { + match response.total_updated_cells + { + Some( val ) => Ok( val ), + None => Ok( 0 ), + } + }, + Err( error ) => Err( error ) + } + Err( error ) => Err( error ), } } + } crate::mod_interface! diff --git a/module/move/gspread/src/actions/gspread_get_header.rs b/module/move/gspread/src/actions/gspread_get_header.rs index 8f7b83c477..e8de1dc4bc 100644 --- a/module/move/gspread/src/actions/gspread_get_header.rs +++ b/module/move/gspread/src/actions/gspread_get_header.rs @@ -10,7 +10,11 @@ mod private use std::fmt; use crate::*; use client::SheetsType; - use actions::gspread::Result; + use actions::gspread:: + { + Error, + Result + }; use format_tools::AsTable; use util::display_table::display_header; use ser::JsonValue; @@ -37,19 +41,27 @@ mod private ( hub : &SheetsType, spreadsheet_id : &str, - table_name: &str) -> Result< Vec< Vec< JsonValue > > > + table_name : &str + ) -> Result< Vec< Vec< JsonValue > > > { - let result = hub + match hub .spreadsheets() .values_get( spreadsheet_id, format!( "{}!A1:Z1", table_name ).as_str() ) .doit() - .await? - .1 - .values - .unwrap_or_else( | | Vec::new() ); - - Ok( result ) + .await + { + Ok( ( _, response ) ) => + { + match response.values + { + Some( values ) => Ok( values ), + None => Ok( Vec::new() ) + } + }, + Err( error ) => Err( Error::ApiError( error ) ) + } } + } crate::mod_interface! diff --git a/module/move/gspread/src/actions/gspread_get_rows.rs b/module/move/gspread/src/actions/gspread_get_rows.rs index 3a083217ed..7f1f7a5c26 100644 --- a/module/move/gspread/src/actions/gspread_get_rows.rs +++ b/module/move/gspread/src/actions/gspread_get_rows.rs @@ -9,7 +9,11 @@ mod private { use crate::*; use client::SheetsType; - use actions::gspread::Result; + use actions::gspread:: + { + Error, + Result + }; use ser::JsonValue; pub async fn action @@ -19,16 +23,22 @@ mod private table_name : &str ) -> Result< Vec< Vec < JsonValue > > > { - let result = hub + match hub .spreadsheets() .values_get( spreadsheet_id, format!( "{}!A2:Z", table_name ).as_str() ) .doit() - .await? - .1 - .values - .unwrap_or_else( | | Vec::new() ); - - Ok( result ) + .await + { + Ok( ( _, response ) ) => + { + match response.values + { + Some( values ) => Ok( values ), + None => Ok( Vec::new() ) + } + }, + Err( error ) => Err( Error::ApiError( error ) ) + } } } diff --git a/module/move/gspread/src/commands/gspread.rs b/module/move/gspread/src/commands/gspread.rs index 8398aa3ec6..7150856df1 100644 --- a/module/move/gspread/src/commands/gspread.rs +++ b/module/move/gspread/src/commands/gspread.rs @@ -22,35 +22,53 @@ mod private #[ derive( Debug, Parser ) ] pub struct CommonArgs { - #[ arg( long ) ] + #[ arg( long, help = "Full URL of Google Sheet.\n\ + It has to be inside of '' to avoid parse errors.\n\ + Example: 'https://docs.google.com/spreadsheets/d/your_spreadsheet_id/edit?gid=0#gid=0'" ) ] pub url : String, - #[ arg( long ) ] + #[ arg( long, help = "Sheet name.\nExample: Sheet1" ) ] pub tab : String } #[ derive( Debug, Subcommand ) ] pub enum Command { - + + /// Command to get header of a sheet. Header is a first raw. + /// + /// Command example: + /// + /// gspread header + /// --url 'https://docs.google.com/spreadsheets/d/1EAEdegMpitv-sTuxt8mV8xQxzJE7h_J0MxQoyLH7xxU/edit?gid=0#gid=0' + /// --tab tab1 #[ command ( name = "header" ) ] Header ( CommonArgs ), + /// Command to get all raws of a sheet but not header. + /// + /// Command example: + /// + /// gspread rows + /// --url 'https://docs.google.com/spreadsheets/d/1EAEdegMpitv-sTuxt8mV8xQxzJE7h_J0MxQoyLH7xxU/edit?gid=0#gid=0' + /// --tab tab1 #[ command( name = "rows" ) ] Rows ( CommonArgs ), + /// Command to get or update a cell from a sheet. #[ command ( subcommand, name = "cell" ) ] Cell ( gspread_cell::Commands ), + /// Commands to set a new value to a cell or get a value from a cell. #[ command ( subcommand, name = "cells" ) ] Cells ( @@ -85,7 +103,8 @@ mod private Command::Cells( cells_command) => { - gspread_cells::command( hub, cells_command ).await; + // hub + gspread_cells::command( cells_command ).await; }, } diff --git a/module/move/gspread/src/commands/gspread_cell.rs b/module/move/gspread/src/commands/gspread_cell.rs index 057da2dd09..a0fd55f361 100644 --- a/module/move/gspread/src/commands/gspread_cell.rs +++ b/module/move/gspread/src/commands/gspread_cell.rs @@ -15,32 +15,57 @@ mod private #[ derive( Debug, Subcommand ) ] pub enum Commands { + /// Command to get a value from a sheet's cell + /// + /// Command example: + /// + /// gspread cell get + /// --url 'https://docs.google.com/spreadsheets/d/1EAEdegMpitv-sTuxt8mV8xQxzJE7h_J0MxQoyLH7xxU/edit?gid=0#gid=0' + /// --tab tab1 + /// --cell A1 #[ command( name = "get" ) ] Get { - #[ arg( long ) ] + #[ arg( long, help = "Full URL of Google Sheet.\n\ + It has to be inside of '' to avoid parse errors.\n\ + Example: 'https://docs.google.com/spreadsheets/d/your_spreadsheet_id/edit?gid=0#gid=0'" ) ] url : String, - #[ arg( long ) ] + #[ arg( long, help = "Sheet name.\nExample: Sheet1" ) ] tab : String, - #[ arg( long ) ] - cel : String, + #[ arg( long, help = "Cell id. You can set it in format:\n \ + - A1, where A is column name and 1 is row number\n\ + Example: --cell A4" ) ] + cell : String, }, + /// Command to set a new value to a sheet's cell. + /// + /// Command example: + /// + /// gspread cell set + /// --url 'https://docs.google.com/spreadsheets/d/1EAEdegMpitv-sTuxt8mV8xQxzJE7h_J0MxQoyLH7xxU/edit?gid=0#gid=0' + /// --tab tab1 + /// --cell A1 + /// --val 13 #[ command( name = "set" ) ] Set { - #[ arg( long ) ] + #[ arg( long, help = "Full URL of Google Sheet.\n\ + It has to be inside of '' to avoid parse errors.\n\ + Example: 'https://docs.google.com/spreadsheets/d/your_spreadsheet_id/edit?gid=0#gid=0'" ) ] url : String, - #[ arg( long ) ] + #[ arg( long, help = "Sheet name.\nExample: Sheet1" ) ] tab : String, - #[ arg( long ) ] - cel : String, + #[ arg( long, help = "Cell id. You can set it in format:\n \ + - A1, where A is column name and 1 is row number\n\ + Example: --cell A4" ) ] + cell : String, - #[ arg( long ) ] + #[ arg( long, help = "Value you want to set. It can be written on any language.\nExample: --val hello" ) ] val : String } } @@ -53,7 +78,7 @@ mod private { match commands { - Commands::Get { url, tab, cel } => + Commands::Get { url, tab, cell } => { let spreadsheet_id = match get_spreadsheet_id_from_url( url.as_str() ) { @@ -65,22 +90,21 @@ mod private } }; - let result = actions::gspread_cell_get::action + match actions::gspread_cell_get::action ( hub, spreadsheet_id, tab.as_str(), - cel.as_str() - ).await; - - match result + cell.as_str() + ) + .await { Ok( value ) => println!( "Value: {}", value ), - Err( error ) => println!( "Error: {}", error ), + Err( error ) => println!( "Error:\n{}", error ), } }, - Commands::Set { url, tab, cel, val } => + Commands::Set { url, tab, cell, val } => { let spreadsheet_id = match get_spreadsheet_id_from_url( url.as_str() ) { @@ -92,19 +116,18 @@ mod private } }; - let result = actions::gspread_cell_set::action + match actions::gspread_cell_set::action ( hub, spreadsheet_id, tab.as_str(), - cel.as_str(), + cell.as_str(), val.as_str() - ).await; - - match result + ) + .await { - Ok( value ) => println!( "Success: {:?}", value ), - Err( error ) => println!( "Error: {}", error ), + Ok( number ) => println!( "You successfully update {} cell!", number ), + Err( error ) => println!( "Error:\n{}", error ), } } diff --git a/module/move/gspread/src/commands/gspread_cells.rs b/module/move/gspread/src/commands/gspread_cells.rs index 13ecf1e378..dd53d80d10 100644 --- a/module/move/gspread/src/commands/gspread_cells.rs +++ b/module/move/gspread/src/commands/gspread_cells.rs @@ -13,19 +13,39 @@ mod private #[ derive( Debug, Subcommand ) ] pub enum Commands { + /// Command to set values range to a google sheet + /// + /// Command example: + /// + /// gspread cells set + /// --url 'https://docs.google.com/spreadsheets/d/1EAEdegMpitv-sTuxt8mV8xQxzJE7h_J0MxQoyLH7xxU/edit?gid=0#gid=0' + /// --tab tab1 + /// --select-row-by-key "id" + /// --json '{"id": "2", "A": "1", "B": "2"}' #[ command( name = "set" ) ] Set { - #[ arg( long ) ] + #[ arg( long, help = "Identifier of a row. Available identifiers: id (row's unique identifier).\n\ + Example: --select_row_by_key \"id\"" ) ] select_row_by_key : String, - - #[ arg( long ) ] + + #[ arg( long, help = "Value range. It must contain select_row_by_key. + The key is a column name (not a header name, but a column name, which can only contain Latin letters). + Every key and value must be a string. + Depending on the shell, different handling might be required.\n\ + Examples:\n\ + 1. --json '{\"id\": \"3\", \"A\": \"1\", \"B\": \"2\"}'\n\ + 2. --json \"{\"id\": \"3\", \"A\": \"1\", \"B\": \"2\"}\"\n\ + 3. --json '{\\\"id\\\": \\\"3\\\", \\\"A\\\": \\\"1\\\", \\\"B\\\": \\\"2\\\"}'\n\ + 4. --json \"{\\\"id\\\": \\\"3\\\", \\\"A\\\": \\\"1\\\", \\\"B\\\": \\\"2\\\"}\" " ) ] json : String, - #[ arg( long ) ] + #[ arg( long, help = "Full URL of Google Sheet.\n\ + It has to be inside of '' to avoid parse errors.\n\ + Example: 'https://docs.google.com/spreadsheets/d/your_spreadsheet_id/edit?gid=0#gid=0'" ) ] url : String, - #[ arg( long ) ] + #[ arg( long, help = "Sheet name.\nExample: Sheet1" ) ] tab : String } @@ -33,7 +53,7 @@ mod private pub async fn command ( - hub : &SheetsType, + // hub : &SheetsType, commands : Commands ) { @@ -51,19 +71,18 @@ mod private } }; - let result = actions::gspread_cells_set::action + match actions::gspread_cells_set::action ( - &hub, + // &hub, select_row_by_key.as_str(), json.as_str(), spreadsheet_id, tab.as_str() - ).await; - - match result + ) + .await { - Ok( msg ) => println!( "{}", msg ), - Err( error ) => println!( "{}", error ) + Ok( val ) => println!( "{} cells were sucsessfully updated!", val ), + Err( error ) => println!( "Error:\n{}", error ) } } } diff --git a/module/move/gspread/src/commands/gspread_header.rs b/module/move/gspread/src/commands/gspread_header.rs index 5048d3e4ed..6ee00db284 100644 --- a/module/move/gspread/src/commands/gspread_header.rs +++ b/module/move/gspread/src/commands/gspread_header.rs @@ -51,14 +51,13 @@ mod private } }; - let result = actions::gspread_get_header::action - ( - hub, - spreadsheet_id, - tab.as_str() - ).await; - - match result + match actions::gspread_get_header::action + ( + hub, + spreadsheet_id, + tab.as_str() + ) + .await { Ok( header ) => { @@ -66,10 +65,10 @@ mod private .into_iter() .map( | row | RowWrapper{ max_len: row.len(), row } ) .collect(); - - println!( "Header: \n {}", Report{ rows: header_wrapped } ); + + println!( "Header:\n{}", Report{ rows: header_wrapped } ); } - Err( error ) => println!( "Error: {}", error ), + Err( error ) => eprintln!( "Error:\n{}", error ), } } } diff --git a/module/move/gspread/src/commands/gspread_rows.rs b/module/move/gspread/src/commands/gspread_rows.rs index 426d7f2dde..6c526d0f78 100644 --- a/module/move/gspread/src/commands/gspread_rows.rs +++ b/module/move/gspread/src/commands/gspread_rows.rs @@ -50,26 +50,25 @@ mod private } }; - let result = actions::gspread_get_rows::action + match actions::gspread_get_rows::action ( hub, spreadsheet_id, tab.as_str() - ).await; - - match result + ) + .await { Ok( rows ) => { let max_len = rows.iter().map(|row| row.len()).max().unwrap_or(0); let rows_wrapped: Vec = rows - .into_iter() - .map(|row| RowWrapper { row, max_len }) - .collect(); + .into_iter() + .map(|row| RowWrapper { row, max_len }) + .collect(); - println!( "Rows: \n {}", Report{ rows: rows_wrapped } ); + println!( "Rows:\n{}", Report{ rows: rows_wrapped } ); } - Err( error ) => println!( "Error: {}", error ), + Err( error ) => eprintln!( "Error:\n{}", error ), } } } diff --git a/module/move/gspread/src/debug.rs b/module/move/gspread/src/debug.rs index 11f63d821e..7f1d303941 100644 --- a/module/move/gspread/src/debug.rs +++ b/module/move/gspread/src/debug.rs @@ -15,6 +15,9 @@ crate::mod_interface! { exposed use { - row_wrapper::RowWrapper, + row_wrapper:: + { + RowWrapper + } }; } diff --git a/module/move/gspread/src/debug/row_wrapper.rs b/module/move/gspread/src/debug/row_wrapper.rs index b8e1635ac7..7802773c47 100644 --- a/module/move/gspread/src/debug/row_wrapper.rs +++ b/module/move/gspread/src/debug/row_wrapper.rs @@ -1,59 +1,62 @@ -//! -//! Gspread wrapper for outputting data to console -//! -//! It is used for "header" and "rows" commands -//! - -use super::*; -use crate::*; -use ser::JsonValue; - - -#[ derive( Debug ) ] -pub struct RowWrapper -{ - pub row: Vec< JsonValue >, - pub max_len: usize -} - -impl Clone for RowWrapper -{ - fn clone( &self ) -> Self - { - Self - { - row: self.row.clone(), - max_len: self.max_len.clone() - } - } -} - -impl TableWithFields for RowWrapper {} -impl Fields< &'_ str, Option< Cow< '_, str > > > -for RowWrapper -{ - type Key< 'k > = &'k str; - type Val< 'v > = Option< Cow< 'v, str > >; - - fn fields( &self ) -> impl IteratorTrait< Item= ( &'_ str, Option > ) > - { - let mut dst = Vec::new(); - - for ( index, value ) in self.row.iter().enumerate() - { - let column_name = format!( "Column{}", index ); - let title = Box::leak( column_name.into_boxed_str() ) as &str; - dst.push( ( title, Some( Cow::Owned( value.to_string() ) ) ) ) - } - - //adding empty values for missing cells - for index in self.row.len()..self.max_len - { - let column_name = format!( "Column{}", index ); - let title = Box::leak( column_name.into_boxed_str() ) as &str; - dst.push( ( title, Some( Cow::Owned( "".to_string() ) ) ) ); - } - - dst.into_iter() - } +//! +//! Gspread wrapper for outputting data to console +//! +//! It is used for "header" and "rows" commands +//! +use super::*; +use crate::*; +use ser::JsonValue; + + +#[ derive( Debug ) ] +pub struct RowWrapper +{ + pub row: Vec< JsonValue >, + pub max_len: usize +} +impl Clone for RowWrapper +{ + fn clone( &self ) -> Self + { + Self + { + row: self.row.clone(), + max_len: self.max_len.clone() + } + } +} +impl TableWithFields for RowWrapper {} +impl Fields< &'_ str, Option< Cow< '_, str > > > +for RowWrapper +{ + type Key< 'k > = &'k str; + type Val< 'v > = Option< Cow< 'v, str > >; + fn fields( &self ) -> impl IteratorTrait< Item= ( &'_ str, Option > ) > + { + let mut dst = Vec::new(); + + for ( index, value ) in self.row.iter().enumerate() + { + let column_name = format!( "{} ", index ); + let title = Box::leak( column_name.into_boxed_str() ) as &str; + let cleaned: String = value + .to_string() + .chars() + .skip( 1 ) + .take( value.to_string().chars().count() - 2 ) + .collect(); + + dst.push( ( title, Some( Cow::Owned( cleaned ) ) ) ) + } + + //adding empty values for missing cells + for index in self.row.len()..self.max_len + { + let column_name = format!( "Column{}", index ); + let column_name = format!( "{}", index ); + let title = Box::leak( column_name.into_boxed_str() ) as &str; + dst.push( ( title, Some( Cow::Owned( "".to_string() ) ) ) ); + } + dst.into_iter() + } } \ No newline at end of file diff --git a/module/move/gspread/tests/inc/header_tests.rs b/module/move/gspread/tests/inc/header_tests.rs index 3009a63bb1..046d8e1d69 100644 --- a/module/move/gspread/tests/inc/header_tests.rs +++ b/module/move/gspread/tests/inc/header_tests.rs @@ -17,6 +17,7 @@ async fn setup() -> ( SheetsType, &'static str ) ( hub, spreadsheet_id ) } + #[ tokio::test ] async fn test_get_header() { diff --git a/module/move/gspread/tests/mock/cell_tests.rs b/module/move/gspread/tests/mock/cell_tests.rs new file mode 100644 index 0000000000..488eb60d02 --- /dev/null +++ b/module/move/gspread/tests/mock/cell_tests.rs @@ -0,0 +1,93 @@ +//! +//! Get and set cell tests. +//! In these examples: +//! - url is /v4/spreadsheets/{spreadsheet_id}}/values/{range} +//! - everything is fake: spreadsheet_id, sheet's name, range and response json +//! + +use httpmock::prelude::*; +use reqwest; + +#[ tokio::test ] +async fn test_get_cell_with_mock() +{ + let server = MockServer::start(); + let body = r#"{ "A2": "Steeve" }"#; + let mock = server.mock( | when, then | { + when.method( GET ) + .path( "/v4/spreadsheets/12345/values/tab2!R2C1" ); + then.status( 200 ) + .header( "Content-Type", "application/json" ) + .body( body ); + } ); + + let response = reqwest::get + ( + server.url + ( + "/v4/spreadsheets/12345/values/tab2!R2C1" + ) + ) + .await + .unwrap(); + + mock.assert(); + + assert_eq!( response.status(), 200 ); +} + +#[ tokio::test ] +async fn test_get_cell_empty_with_mock() +{ + let server = MockServer::start(); + let mock = server.mock( | when, then | { + when.method( GET ) + .path( "/v4/spreadsheets/12345/values/tab2!R2C1" ); + then.status( 200 ) + .header( "Content-Type", "application/json" ) + .body( r#"{}"# ); + } ); + + let response = reqwest::get + ( + server.url + ( + "/v4/spreadsheets/12345/values/tab2!R2C1" + ) + ) + .await + .unwrap(); + + mock.assert(); + + assert_eq!( response.status(), 200 ); +} + +#[ tokio::test ] +async fn test_set_cell_with_mock() +{ + let server = MockServer::start(); + let body = r#"A2": "Some value"#; + let mock = server.mock( | when, then | { + when.method( POST ) + .path( "/v4/spreadsheets/12345/values/tab2!R2C1" ) + .header("content-type", "application/json") + .body( body ); + // returns amount of updated cells + then.status( 201 ) + .header( "Content-Type", "application/json" ) + .body( "1" ); + } ); + + let response = reqwest::Client::new() + .post( server.url( "/v4/spreadsheets/12345/values/tab2!R2C1" ) ) + .header( "Content-Type", "application/json" ) + .body( body ) + .send() + .await + .unwrap(); + + mock.assert(); + + assert_eq!( response.status(), 201 ); +} \ No newline at end of file diff --git a/module/move/gspread/tests/mock/cells_tests.rs b/module/move/gspread/tests/mock/cells_tests.rs new file mode 100644 index 0000000000..b03b78fd71 --- /dev/null +++ b/module/move/gspread/tests/mock/cells_tests.rs @@ -0,0 +1,67 @@ +//! +//! Set cells tests. +//! In these examples: +//! - url is /v4/spreadsheets/{spreadsheet_id}}/values/{range} +//! - everything is fake: spreadsheet_id, sheet's name, range and response json +//! + +use httpmock::prelude::*; +use reqwest; + +#[ tokio::test ] +async fn test_set_cells_with_mock() +{ + let server = MockServer::start(); + let body = r#"{ "id": "2", "A": "new_val1", "B": "new_val2"}"#; + let mock = server.mock( | when, then | { + when.method( POST ) + .path( "/v4/spreadsheets/12345/values/tab3!A2:B2" ) + .header( "Content-Type", "application/json" ) + .body( body ); + // returns amount of updated cells + then.status( 201 ) + .header( "Content-Type", "application/json" ) + .body( "2" ); + } ); + + let response = reqwest::Client::new() + .post( server.url( "/v4/spreadsheets/12345/values/tab3!A2:B2" ) ) + .header( "Content-Type", "application/json" ) + .body( body ) + .send() + .await + .unwrap(); + + mock.assert(); + + assert_eq!( response.status(), 201 ); +} + +#[ tokio::test ] +async fn test_set_cells_wrong_row_with_mock() +{ + let server = MockServer::start(); + let body = r#"{ "id": "a", "A": "new_val1", "B": "new_val2"}"#; + let response_body = r#"{"error":{"code":400,"message":"Invalid data[0]: Unable to parse range: tab3!Aa","status":"INVALID_ARGUMENT"}}"#; + let mock = server.mock( | when, then | { + when.method( POST ) + .path( "/v4/spreadsheets/12345/values/tab3!Aa:Ba" ) + .header( "Content-Type", "application/json" ) + .body( body ); + then.status( 400 ) + .header( "Content-Type", "application/json" ) + .body( response_body ); + } ); + + let response = reqwest::Client::new() + .post( server.url( "/v4/spreadsheets/12345/values/tab3!Aa:Ba" ) ) + .header( "Content-Type", "application/json" ) + .body( body ) + .send() + .await + .unwrap(); + + mock.assert(); + + assert_eq!( response.status(), 400 ); +} \ No newline at end of file diff --git a/module/move/gspread/tests/mock/header_tests.rs b/module/move/gspread/tests/mock/header_tests.rs new file mode 100644 index 0000000000..5927b1a8a6 --- /dev/null +++ b/module/move/gspread/tests/mock/header_tests.rs @@ -0,0 +1,123 @@ +//! +//! Get header tests. +//! In these examples: +//! - url is /v4/spreadsheets/{spreadsheet_id}}/values/{range} +//! - everything is fake: spreadsheet_id, sheet's name, range and response json +//! + +use httpmock::prelude::*; +use reqwest; + + +#[ tokio::test ] +async fn test_get_header() +{ + let server = MockServer::start(); + let body = r#"{ "A1": "Name", "B1": "Surname", "C1": "Age" }"#; + let mock = server.mock( | when, then | { + when.method( GET ) + .path( "/v4/spreadsheets/12345/values/tab1!A1:Z1" ); + then.status( 200 ) + .header("Content-Type", "application/json" ) + .body( body ); + } ); + + let response = reqwest::get + ( + server.url + ( + "/v4/spreadsheets/12345/values/tab1!A1:Z1" + ) + ) + .await + .unwrap(); + + mock.assert(); + + assert_eq!( response.status(), 200 ) + +} + +#[ tokio::test ] +async fn test_get_header_with_spaces_with_mock() +{ + let server = MockServer::start(); + let body = r#"{ "A1": "Name", "B1": "", "C1": "Age" }"#; + let mock = server.mock( | when, then | { + when.method( GET ) + .path( "/v4/spreadsheets/12345/values/tab1!A1:Z1" ); + then.status( 200 ) + .header("Content-Type", "application/json" ) + .body( body ); + } ); + + let response = reqwest::get + ( + server.url + ( + "/v4/spreadsheets/12345/values/tab1!A1:Z1" + ) + ) + .await + .unwrap(); + + mock.assert(); + + assert_eq!( response.status(), 200 ) +} + +#[ tokio::test ] +async fn test_get_header_empty_with_mock() +{ + let server = MockServer::start(); + + let mock = server.mock( | when, then | { + when.method( GET ) + .path( "/v4/spreadsheets/12345/values/tab1!A1:Z1" ); + then.status( 200 ) + .header("Content-Type", "application/json" ) + .body( r#"{}"# ); + } ); + + let response = reqwest::get + ( + server.url + ( + "/v4/spreadsheets/12345/values/tab1!A1:Z1" + ) + ) + .await + .unwrap(); + + mock.assert(); + + assert_eq!( response.status(), 200 ) +} + +#[ tokio::test ] +async fn test_get_header_with_empty_end_with_mock() +{ + let server = MockServer::start(); + let body = r#"{ "A1": "Name", "B1": "Surname" }"#; + let mock = server.mock( | when, then | { + when.method( GET ) + .path( "/v4/spreadsheets/12345/values/tab1!A1:Z1" ); + then.status( 200 ) + .header("Content-Type", "application/json" ) + .body( body ); + } ); + + let response = reqwest::get + ( + server.url + ( + "/v4/spreadsheets/12345/values/tab1!A1:Z1" + ) + ) + .await + .unwrap(); + + mock.assert(); + + assert_eq!( response.status(), 200 ) +} \ No newline at end of file diff --git a/module/move/gspread/tests/mock/mod.rs b/module/move/gspread/tests/mock/mod.rs new file mode 100644 index 0000000000..1c6c49e281 --- /dev/null +++ b/module/move/gspread/tests/mock/mod.rs @@ -0,0 +1,8 @@ +#[ allow( unused_imports ) ] +use super::*; + +mod oauth_tests; +mod header_tests; +mod cell_tests; +mod cells_tests; +mod rows_tests; \ No newline at end of file diff --git a/module/move/gspread/tests/mock/oauth_tests.rs b/module/move/gspread/tests/mock/oauth_tests.rs new file mode 100644 index 0000000000..a22ef9a792 --- /dev/null +++ b/module/move/gspread/tests/mock/oauth_tests.rs @@ -0,0 +1,102 @@ +//! +//! OAuth2 tests. +//! + +use httpmock::prelude::*; +use reqwest; +use serde_json::json; + +#[ tokio::test ] +async fn oauth2_first_endpoint_with_mock() +{ + let server = MockServer::start(); + let mock = server.mock( | when, then | { + when.method( GET ) + .path( "/o/oauth2/auth" ) + .query_param( "scope", "https://www.googleapis.com/auth/drive.readonly" ) + .query_param( "access_type", "offline" ) + .query_param( "redirect_uri", "http://localhost:44444" ) + .query_param( "response_type", "code" ) + .query_param( "client_id", "YOUR_CLIENT_ID" ); + then.status( 302 ); + }); + + let response = reqwest::get + ( + server.url + ( + "/o/oauth2/auth?\ + scope=https://www.googleapis.com/auth/drive.readonly&\ + access_type=offline&\ + redirect_uri=http://localhost:44444&\ + response_type=code&\ + client_id=YOUR_CLIENT_ID" + ) + ) + .await + .unwrap(); + + mock.assert(); + + assert_eq!( response.status(), 302 ); +} + + +#[ tokio::test ] +async fn oauth2_second_endpoint_with_mock() +{ + let server = MockServer::start(); + + // url == first endpoint + let mock = server.mock( | when, then | { + when.path( "/o/oauth2/auth" ) + .query_param( "scope", "https://..." ); + then.status( 302 ); + } ); + + // in real program at that point we have to open generated url and give access to our program from browser + let response = reqwest::get( server.url( "/o/oauth2/auth?scope=https://..." ) ).await.unwrap(); + + mock.assert(); + + assert_eq!( response.status(), 302 ); +} + +#[ tokio::test ] +async fn oauth2_third_endpoint_with_mock() +{ + let server = MockServer::start(); + let body = r#"code=AUTHORIZATION_CODE&client_secret=YOUR_CLIENT_SECRET&"#; + let mock = server.mock( | when, then | { + when.method( POST ) + .path( "/token" ) + .header("Content-Type", "application/json" ) + .body( body ); + then.status( 200 ) + .header("Content-Type", "application/json" ) + .json_body + ( + json! + ( + { + "access_token" : "access_token", + "token_type" : "Bearer", + "expires_in" : "3600", + "scope" : "https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/userinfo.email openid" + } + ) + ); + }); + + let response = reqwest::Client::new() + .post( server.url( "/token" ) ) + .header( "Content-Type", "application/json" ) + .body( body ) + .send() + .await + .unwrap(); + + mock.assert(); + + assert_eq!( response.status(), 200 ); +} \ No newline at end of file diff --git a/module/move/gspread/tests/mock/rows_tests.rs b/module/move/gspread/tests/mock/rows_tests.rs new file mode 100644 index 0000000000..05b43663b3 --- /dev/null +++ b/module/move/gspread/tests/mock/rows_tests.rs @@ -0,0 +1,93 @@ +//! +//! Get rows tests. +//! In these examples: +//! - url is /v4/spreadsheets/{spreadsheet_id}}/values/{range} +//! - everything is fake: spreadsheet_id, sheet's name, range and response json +//! + +use httpmock::prelude::*; +use reqwest; + +#[ tokio::test ] +async fn test_get_rows_with_mock() +{ + let server = MockServer::start(); + let body = r#"{"A2" : "Steeve", "B2": "John", "A3": "Seva", "B3": "Oleg" }"#; + let mock = server.mock( | when, then | { + when.method( GET ) + .path( "/v4/spreadsheets/12345/values/A2:B3" ); + then.status( 200 ) + .header( "Content-Type", "application/json" ) + .body( body ); + }); + + let response = reqwest::get + ( + server.url + ( + "/v4/spreadsheets/12345/values/A2:B3" + ) + ) + .await + .unwrap(); + + mock.assert(); + + assert_eq!( response.status(), 200 ); +} + +#[ tokio::test ] +async fn test_get_rows_with_spaces_with_mock() +{ + let server = MockServer::start(); + let body = r#"{"A2" : "Steeve", "B2": "", "A3": "Seva", "B3": "Oleg" }"#; + let mock = server.mock( | when, then | { + when.method( GET ) + .path( "/v4/spreadsheets/12345/values/A2:B3" ); + then.status( 200 ) + .header( "Content-Type", "application/json" ) + .body( body ); + }); + + let response = reqwest::get + ( + server.url + ( + "/v4/spreadsheets/12345/values/A2:B3" + ) + ) + .await + .unwrap(); + + mock.assert(); + + assert_eq!( response.status(), 200 ); +} + +#[ tokio::test ] +async fn test_get_rows_empty_with_mock() +{ + let server = MockServer::start(); + let body = r#"{}"#; + let mock = server.mock( | when, then | { + when.method( GET ) + .path( "/v4/spreadsheets/12345/values/A2:B3" ); + then.status( 200 ) + .header( "Content-Type", "application/json" ) + .body( body ); + }); + + let response = reqwest::get + ( + server.url + ( + "/v4/spreadsheets/12345/values/A2:B3" + ) + ) + .await + .unwrap(); + + mock.assert(); + + assert_eq!( response.status(), 200 ); +} \ No newline at end of file diff --git a/module/move/gspread/tests/smoke_test.rs b/module/move/gspread/tests/smoke_test.rs index c3163b32ed..28e533e551 100644 --- a/module/move/gspread/tests/smoke_test.rs +++ b/module/move/gspread/tests/smoke_test.rs @@ -1,4 +1,3 @@ - #[ test ] fn local_smoke_test() { diff --git a/module/move/gspread/tests/tests.rs b/module/move/gspread/tests/tests.rs index 201ae26926..31c81bb6b2 100644 --- a/module/move/gspread/tests/tests.rs +++ b/module/move/gspread/tests/tests.rs @@ -1,9 +1,10 @@ - - #[ allow( unused_imports ) ] use gspread as the_module; #[ allow( unused_imports ) ] use test_tools::exposed::*; -#[ cfg( feature = "enabled" ) ] -mod inc; \ No newline at end of file +#[ cfg( feature = "with_online" ) ] +mod inc; + +#[ cfg( feature = "default" ) ] +mod mock; \ No newline at end of file From 5e94a932297eab215bdbfeec32b944f2e9056476 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A1=D0=B5=D0=B2=D0=B0=20=D0=91=D0=B0=D0=BA=D1=83=D1=82?= =?UTF-8?q?=D0=BE=D0=B2?= <122987843+sevabakutov@users.noreply.github.com> Date: Mon, 30 Dec 2024 17:17:34 +0100 Subject: [PATCH 67/67] Gspread fix readme (#1515) * CHANGED: README.md * changed readme.md * fixed: .secret/readme.md * changed .secret/readme.md --------- Co-authored-by: Vsevolod --- module/move/gspread/.secret/readme.md | 27 +++++++++++++++++++++++---- module/move/gspread/readme.md | 8 +++++++- 2 files changed, 30 insertions(+), 5 deletions(-) diff --git a/module/move/gspread/.secret/readme.md b/module/move/gspread/.secret/readme.md index e3e100f72d..5200f5ef71 100644 --- a/module/move/gspread/.secret/readme.md +++ b/module/move/gspread/.secret/readme.md @@ -2,7 +2,26 @@ Follow these steps to create and configure your OAuth credentials for using Google APIs. -## 1. Create API Credentials +## 1. Configure Consent Screen + +1. Go to the [Google API Console](https://console.developers.google.com/). +2. From the projects list, select an existing project or create a new one. +3. Go to **OAuth consent screen** +4. Choose **Extrenal** User Type +5. Fill **App name**, **User support email** and **Developer contact information**. Click **continue** +6. Click on **ADD OR REMOVE SCOPES** +7. Add **.../auth/userinfo.email** and **.../auth/userinfo.profile** spoces. +8. Finish configuration + +## 2. Enable Google Sheets API + +1. Go to the [Google API Console](https://console.developers.google.com/). +2. In the left side menu, select **Enabled APIs & Services**. +3. Click on **ENABLE APIS AND SERVICES** +4. Search for **Google Sheets API** +5. Click on **Enable** + +## 2. Create API Credentials 1. Go to the [Google API Console](https://console.developers.google.com/). 2. From the projects list, select an existing project or create a new one. @@ -15,7 +34,7 @@ Follow these steps to create and configure your OAuth credentials for using Goog Once the credential is created, you will receive a **Client ID** and **Client Secret**. These are required for accessing the API. -## 2. Store Your Credentials +## 3. Store Your Credentials Save the **Client ID** and **Client Secret** in a `.env` within a `.secret` directory. The file should look like this: @@ -24,11 +43,11 @@ CLIENT_ID=YOUR_CLIENT_ID CLIENT_SECRET=YOUR_SECRET_KEY ``` -## 3. Why do we need it? +## 4. Why do we need it? After executing each command, you need to grant the GSPREAD program access to the Google API. You will receive a link that begin with 'Please direct your browser to https://....' that will redirect you to your browser, where you must authorize the access. You will need to select the appropriate Google account that has the credentials for the application. The **CLIENT_ID** and **CLIENT_SECRET** are set up to do this process. -## 4. Troubleshooting +## 5. Troubleshooting If you encounter a page displaying an error instead of the Google account selection screen, it is likely that you need to add **AUTH_URI** or **TOKEN_URI** to the .env file. In this case, all four secrets are required. To retrieve them, download the API key you created in JSON format. Open the file and copy the necessary keys into the .env file. After making these changes, your .env file should look like this: diff --git a/module/move/gspread/readme.md b/module/move/gspread/readme.md index 2fdeb0f40a..991a5abe04 100644 --- a/module/move/gspread/readme.md +++ b/module/move/gspread/readme.md @@ -1 +1,7 @@ -## Google Sheets CLI \ No newline at end of file +## Module :: gspread + +[![experimental](https://img.shields.io/badge/status-experimental-orange)](https://github.com/emersion/stability-badges#experimental) +[![ask](https://img.shields.io/badge/discord-join%20chat-7289DA)](https://discord.gg/RzQGqF5z) + + +**NOT ready for production** \ No newline at end of file