From 41ec0f5e523fbf1e78daf05fb49e3ec571a4dab7 Mon Sep 17 00:00:00 2001 From: Gabriel Lopes Veiga Date: Wed, 30 Oct 2024 06:26:09 -0300 Subject: [PATCH 1/3] Expand domain of the `classes` macro --- stylance/src/lib.rs | 160 ++++++++------------------------- stylance/tests/test_classes.rs | 39 +++++--- 2 files changed, 63 insertions(+), 136 deletions(-) diff --git a/stylance/src/lib.rs b/stylance/src/lib.rs index c2d5396..5e8f00c 100644 --- a/stylance/src/lib.rs +++ b/stylance/src/lib.rs @@ -62,58 +62,37 @@ #[doc(hidden)] pub mod internal { pub use stylance_macros::*; +} - pub struct NormalizeOptionStr<'a>(Option<&'a str>); +pub struct NormalizedClass<'a>(pub Option<&'a str>); - impl<'a> From<&'a str> for NormalizeOptionStr<'a> { - fn from(value: &'a str) -> Self { - NormalizeOptionStr::<'a>(Some(value)) - } - } - - impl<'a> From<&'a String> for NormalizeOptionStr<'a> { - fn from(value: &'a String) -> Self { - NormalizeOptionStr::<'a>(Some(value.as_ref())) - } +impl<'a> From<&'a str> for NormalizedClass<'a> { + fn from(value: &'a str) -> Self { + NormalizedClass::<'a>(Some(value)) } +} - impl<'a, T> From> for NormalizeOptionStr<'a> - where - T: AsRef + ?Sized, - { - fn from(value: Option<&'a T>) -> Self { - Self(value.map(AsRef::as_ref)) - } - } - - impl<'a, T> From<&'a Option> for NormalizeOptionStr<'a> - where - T: AsRef, - { - fn from(value: &'a Option) -> Self { - Self(value.as_ref().map(AsRef::as_ref)) - } +impl<'a> From<&'a String> for NormalizedClass<'a> { + fn from(value: &'a String) -> Self { + NormalizedClass::<'a>(Some(value.as_ref())) } +} - pub fn normalize_option_str<'a>(value: impl Into>) -> Option<&'a str> { - value.into().0 +impl<'a, T> From> for NormalizedClass<'a> +where + T: AsRef + ?Sized, +{ + fn from(value: Option<&'a T>) -> Self { + Self(value.map(AsRef::as_ref)) } +} - pub fn join_opt_str_slice(slice: &[Option<&str>]) -> String { - let mut iter = slice.iter().flat_map(|c| *c); - let first = match iter.next() { - Some(first) => first, - None => return String::new(), - }; - let size = iter.clone().map(|v| v.len()).sum::() + slice.len() - 1; - let mut result = String::with_capacity(size); - result.push_str(first); - - for v in iter { - result.push(' '); - result.push_str(v); - } - result +impl<'a, T> From<&'a Option> for NormalizedClass<'a> +where + T: AsRef, +{ + fn from(value: &'a Option) -> Self { + Self(value.as_ref().map(AsRef::as_ref)) } } @@ -225,87 +204,19 @@ pub trait JoinClasses { fn join_classes(self) -> String; } -macro_rules! impl_join_classes_for_tuples { - (($($types:ident),*), ($($idx:tt),*)) => { - impl<'a, $($types),*> JoinClasses for ($($types,)*) - where - $($types: Into>),* - { - fn join_classes(self) -> String { - let list = &[ - $(internal::normalize_option_str(self.$idx)),* - ]; - internal::join_opt_str_slice(list) - } - } - }; +impl<'a, T> JoinClasses for T +where + T: AsRef<[NormalizedClass<'a>]>, +{ + fn join_classes(self) -> String { + self.as_ref() + .iter() + .filter_map(|x| x.0) + .collect::>() + .join(" ") + } } -impl_join_classes_for_tuples!( - (T1, T2), // - (0, 1) -); -impl_join_classes_for_tuples!( - (T1, T2, T3), // - (0, 1, 2) -); -impl_join_classes_for_tuples!( - (T1, T2, T3, T4), // - (0, 1, 2, 3) -); -impl_join_classes_for_tuples!( - (T1, T2, T3, T4, T5), // - (0, 1, 2, 3, 4) -); -impl_join_classes_for_tuples!( - (T1, T2, T3, T4, T5, T6), // - (0, 1, 2, 3, 4, 5) -); -impl_join_classes_for_tuples!( - (T1, T2, T3, T4, T5, T6, T7), // - (0, 1, 2, 3, 4, 5, 6) -); -impl_join_classes_for_tuples!( - (T1, T2, T3, T4, T5, T6, T7, T8), // - (0, 1, 2, 3, 4, 5, 6, 7) -); -impl_join_classes_for_tuples!( - (T1, T2, T3, T4, T5, T6, T7, T8, T9), - (0, 1, 2, 3, 4, 5, 6, 7, 8) -); -impl_join_classes_for_tuples!( - (T1, T2, T3, T4, T5, T6, T7, T8, T9, T10), - (0, 1, 2, 3, 4, 5, 6, 7, 8, 9) -); -impl_join_classes_for_tuples!( - (T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11), - (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10) -); -impl_join_classes_for_tuples!( - (T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12), - (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11) -); -impl_join_classes_for_tuples!( - (T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13), - (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12) -); -impl_join_classes_for_tuples!( - (T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14), - (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13) -); -impl_join_classes_for_tuples!( - (T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15), - (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14) -); -impl_join_classes_for_tuples!( - (T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16), - (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15) -); -impl_join_classes_for_tuples!( - (T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17), - (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16) -); - /// Utility macro for joining multiple class names. /// /// The macro accepts `&str` `&String` and any refs of `T` where `T` implements `AsRef` @@ -328,7 +239,8 @@ impl_join_classes_for_tuples!( /// ``` #[macro_export] macro_rules! classes { + () => { "" }; ($($exp:expr),+) => { - ::stylance::JoinClasses::join_classes(($($exp),*)) + ::stylance::JoinClasses::join_classes([$($exp.into()),*]) }; } diff --git a/stylance/tests/test_classes.rs b/stylance/tests/test_classes.rs index 96b36b5..85b7a1a 100644 --- a/stylance/tests/test_classes.rs +++ b/stylance/tests/test_classes.rs @@ -1,23 +1,38 @@ #[test] fn test_join_classes() { - use stylance::JoinClasses; + use stylance::{JoinClasses, NormalizedClass}; + assert_eq!( - ( - "one", - Some("two"), - false.then_some("three"), - true.then_some("four"), - &String::from("five"), - Some(&String::from("six")), - &("seven", "eight").join_classes() - ) - .join_classes(), + ([ + Into::::into("one"), + Some("two").into(), + false.then_some("three").into(), + true.then_some("four").into(), + (&String::from("five")).into(), + Some(&String::from("six")).into(), + (&(&["seven".into(), "eight".into()]).join_classes()).into(), + ]) + .join_classes(), "one two four five six seven eight" ); } #[test] -fn test_classes_macro() { +fn test_classes_macro_none() { + use stylance::classes; + assert_eq!(classes!(), ""); +} + +#[test] +fn test_classes_macro_one() { + use stylance::classes; + assert_eq!(classes!("one"), "one"); + assert_eq!(classes!(Some("one")), "one"); + assert_eq!(classes!(false.then_some("one")), ""); +} + +#[test] +fn test_classes_macro_many() { use stylance::classes; assert_eq!( classes!( From 81e97a6165d09d89a08e1babcad0004b2db82986 Mon Sep 17 00:00:00 2001 From: Gabriel Lopes Veiga Date: Wed, 30 Oct 2024 10:59:10 -0300 Subject: [PATCH 2/3] Narrow JoinClasses impl and reintroduce tuples --- stylance/src/lib.rs | 140 ++++++++++++++++++++++++++++++--- stylance/tests/test_classes.rs | 22 +++--- 2 files changed, 139 insertions(+), 23 deletions(-) diff --git a/stylance/src/lib.rs b/stylance/src/lib.rs index 5e8f00c..e89baff 100644 --- a/stylance/src/lib.rs +++ b/stylance/src/lib.rs @@ -59,12 +59,54 @@ #![cfg_attr(docsrs, feature(doc_cfg))] +pub struct NormalizedClass<'a>(pub Option<&'a str>); + #[doc(hidden)] pub mod internal { pub use stylance_macros::*; -} -pub struct NormalizedClass<'a>(pub Option<&'a str>); + pub fn normalize_option_str<'a>( + value: impl Into>, + ) -> Option<&'a str> { + value.into().0 + } + + #[inline(always)] + fn join_opt_str_iter<'a, Iter>(iter: &mut Iter, length: usize) -> String + where + Iter: Iterator + Clone, + { + let first = match iter.next() { + Some(first) => first, + None => return String::new(), + }; + + let head_size = first.len(); + let tail_size = iter.clone().map(|v| v.len()).sum::(); + let boundaries_size = length - 1; + let size = head_size + tail_size + boundaries_size; + + let mut result = String::with_capacity(size); + result.push_str(first); + + for v in iter { + result.push(' '); + result.push_str(v); + } + + result + } + + pub fn join_opt_str_slice(slice: &[Option<&str>]) -> String { + let mut iter = slice.iter().flat_map(|c| *c); + join_opt_str_iter(&mut iter, slice.len()) + } + + pub fn join_normalized_class_slice(slice: &[crate::NormalizedClass<'_>]) -> String { + let mut iter = slice.iter().flat_map(|c| c.0); + join_opt_str_iter(&mut iter, slice.len()) + } +} impl<'a> From<&'a str> for NormalizedClass<'a> { fn from(value: &'a str) -> Self { @@ -204,19 +246,93 @@ pub trait JoinClasses { fn join_classes(self) -> String; } -impl<'a, T> JoinClasses for T -where - T: AsRef<[NormalizedClass<'a>]>, -{ +impl<'a> JoinClasses for &[NormalizedClass<'a>] { fn join_classes(self) -> String { - self.as_ref() - .iter() - .filter_map(|x| x.0) - .collect::>() - .join(" ") + internal::join_normalized_class_slice(self) } } +macro_rules! impl_join_classes_for_tuples { + (($($types:ident),*), ($($idx:tt),*)) => { + impl<'a, $($types),*> JoinClasses for ($($types,)*) + where + $($types: Into>),* + { + fn join_classes(self) -> String { + let list = &[ + $(internal::normalize_option_str(self.$idx)),* + ]; + internal::join_opt_str_slice(list) + } + } + }; +} + +impl_join_classes_for_tuples!( + (T1, T2), // + (0, 1) +); +impl_join_classes_for_tuples!( + (T1, T2, T3), // + (0, 1, 2) +); +impl_join_classes_for_tuples!( + (T1, T2, T3, T4), // + (0, 1, 2, 3) +); +impl_join_classes_for_tuples!( + (T1, T2, T3, T4, T5), // + (0, 1, 2, 3, 4) +); +impl_join_classes_for_tuples!( + (T1, T2, T3, T4, T5, T6), // + (0, 1, 2, 3, 4, 5) +); +impl_join_classes_for_tuples!( + (T1, T2, T3, T4, T5, T6, T7), // + (0, 1, 2, 3, 4, 5, 6) +); +impl_join_classes_for_tuples!( + (T1, T2, T3, T4, T5, T6, T7, T8), // + (0, 1, 2, 3, 4, 5, 6, 7) +); +impl_join_classes_for_tuples!( + (T1, T2, T3, T4, T5, T6, T7, T8, T9), + (0, 1, 2, 3, 4, 5, 6, 7, 8) +); +impl_join_classes_for_tuples!( + (T1, T2, T3, T4, T5, T6, T7, T8, T9, T10), + (0, 1, 2, 3, 4, 5, 6, 7, 8, 9) +); +impl_join_classes_for_tuples!( + (T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11), + (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10) +); +impl_join_classes_for_tuples!( + (T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12), + (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11) +); +impl_join_classes_for_tuples!( + (T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13), + (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12) +); +impl_join_classes_for_tuples!( + (T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14), + (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13) +); +impl_join_classes_for_tuples!( + (T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15), + (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14) +); +impl_join_classes_for_tuples!( + (T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16), + (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15) +); +impl_join_classes_for_tuples!( + (T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17), + (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16) +); + /// Utility macro for joining multiple class names. /// /// The macro accepts `&str` `&String` and any refs of `T` where `T` implements `AsRef` @@ -241,6 +357,6 @@ where macro_rules! classes { () => { "" }; ($($exp:expr),+) => { - ::stylance::JoinClasses::join_classes([$($exp.into()),*]) + ::stylance::JoinClasses::join_classes([$($exp.into()),*].as_slice()) }; } diff --git a/stylance/tests/test_classes.rs b/stylance/tests/test_classes.rs index 85b7a1a..8fd79d2 100644 --- a/stylance/tests/test_classes.rs +++ b/stylance/tests/test_classes.rs @@ -1,18 +1,18 @@ #[test] fn test_join_classes() { - use stylance::{JoinClasses, NormalizedClass}; + use stylance::JoinClasses; assert_eq!( - ([ - Into::::into("one"), - Some("two").into(), - false.then_some("three").into(), - true.then_some("four").into(), - (&String::from("five")).into(), - Some(&String::from("six")).into(), - (&(&["seven".into(), "eight".into()]).join_classes()).into(), - ]) - .join_classes(), + ( + "one", + Some("two"), + false.then_some("three"), + true.then_some("four"), + &String::from("five"), + Some(&String::from("six")), + &("seven", "eight").join_classes() + ) + .join_classes(), "one two four five six seven eight" ); } From c0a7afd7837bba0a7025aa307e706366b1166551 Mon Sep 17 00:00:00 2001 From: Gabriel Lopes Veiga Date: Wed, 30 Oct 2024 11:16:22 -0300 Subject: [PATCH 3/3] Lint --- stylance/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stylance/src/lib.rs b/stylance/src/lib.rs index e89baff..b0357d5 100644 --- a/stylance/src/lib.rs +++ b/stylance/src/lib.rs @@ -246,7 +246,7 @@ pub trait JoinClasses { fn join_classes(self) -> String; } -impl<'a> JoinClasses for &[NormalizedClass<'a>] { +impl JoinClasses for &[NormalizedClass<'_>] { fn join_classes(self) -> String { internal::join_normalized_class_slice(self) }