1#[macro_export]
25macro_rules! clsx {
26 () => {
28 String::new()
29 };
30
31 (@internal $classes:ident ,) => {};
33
34 (@internal $classes:ident) => {};
36
37 ($class:expr) => {
39 $class.to_string()
40 };
41
42 (@internal $classes:ident $class:expr => $cond:expr, $($rest:tt)*) => {{
44 if $cond {
45 $classes.push($class.to_string());
46 }
47 $crate::clsx!(@internal $classes $($rest)*);
48 }};
49
50 (@internal $classes:ident $class:expr => $cond:expr) => {{
52 if $cond {
53 $classes.push($class.to_string());
54 }
55 }};
56
57 (@internal $classes:ident $class:expr, $($rest:tt)*) => {{
59 $classes.push($class.to_string());
60 $crate::clsx!(@internal $classes $($rest)*);
61 }};
62
63 (@internal $classes:ident $class:expr) => {{
65 $classes.push($class.to_string());
66 }};
67
68 ($($args:tt)*) => {{
70 let mut classes = Vec::new();
71 $crate::clsx!(@internal classes $($args)*);
72 classes.into_iter()
73 .filter(|s| !s.is_empty())
74 .collect::<Vec<_>>()
75 .join(" ")
76 }};
77}
78
79#[cfg(test)]
80mod tests {
81 use super::*;
82
83 #[test]
84 fn test_basic_usage() {
85 assert_eq!(clsx!("class1"), "class1");
86 assert_eq!(clsx!("class1", "class2"), "class1 class2");
87 assert_eq!(clsx!("class1", "", "class2"), "class1 class2");
88 }
89
90 #[test]
91 fn test_conditional_usage() {
92 assert_eq!(clsx!("class1" => true, "class2" => false), "class1");
93 assert_eq!(clsx!("class1" => false, "class2" => true), "class2");
94 }
95
96 #[test]
97 fn test_mixed_usage() {
98 let dynamic_class = "dynamic";
99 assert_eq!(
100 clsx!("static", dynamic_class, "conditional" => true),
101 "static dynamic conditional"
102 );
103 assert_eq!(
104 clsx!("base", "always", "active" => true, "disabled" => false),
105 "base always active"
106 );
107 }
108
109 #[test]
110 fn test_empty_input() {
111 assert_eq!(clsx!(), "");
112 }
113
114 #[test]
115 fn test_complex_usage() {
116 let is_active = true;
117 let is_disabled = false;
118 assert_eq!(
119 clsx!("header", "button" => is_active, "hidden" => is_disabled, "footer"),
120 "header button footer"
121 );
122 }
123
124 #[test]
125 fn test_trailing_comma() {
126 assert_eq!(clsx!("class1", "class2",), "class1 class2");
127 assert_eq!(clsx!("class1" => true, "class2" => false,), "class1");
128 }
129}