classnames/classes/
attr_class.rs

1use crate::classes::DuoClass;
2use crate::classes::ElClass;
3use crate::classes::OptionClass;
4use crate::Class;
5use ::smallvec::SmallVec;
6use ::std::borrow::Cow;
7use ::std::convert::From;
8use ::std::fmt;
9use ::std::ops::Add;
10
11const ATTR_SMALL_VEC_SIZE: usize = 3;
12
13#[derive(Clone, PartialEq, Debug)]
14pub struct AttrClass<N> {
15    parent: N,
16    attrs: SmallVec<[&'static str; ATTR_SMALL_VEC_SIZE]>,
17}
18
19impl<N> Class for AttrClass<N> where N: fmt::Display + Sized + PartialEq + Clone {}
20
21impl<N: fmt::Display + Sized + Copy> AttrClass<N> {
22    pub(crate) fn new(parent: N) -> Self {
23        Self {
24            parent,
25            attrs: SmallVec::new(),
26        }
27    }
28
29    pub fn el<'a>(&self, class: &'a str) -> ElClass<N, &'a str> {
30        ElClass::new(self.parent, class)
31    }
32
33    pub fn attr(mut self, attr: &'static str) -> Self {
34        self.attrs.push(attr);
35        self
36    }
37
38    pub fn maybe_attr(self, attr: &'static str, is_set: bool) -> Self {
39        if is_set {
40            self.attr(attr)
41        } else {
42            self
43        }
44    }
45}
46
47impl<N, O> Add<O> for AttrClass<N>
48where
49    N: Class,
50    O: Class,
51{
52    type Output = DuoClass<Self, O>;
53
54    fn add(self, other: O) -> Self::Output {
55        DuoClass::new(self, other)
56    }
57}
58
59impl<'s, N> Add<&'s str> for AttrClass<N>
60where
61    N: Class,
62{
63    type Output = DuoClass<Self, &'s str>;
64
65    fn add(self, other: &'s str) -> Self::Output {
66        DuoClass::new(self, other)
67    }
68}
69
70impl<N, O> Add<Option<O>> for AttrClass<N>
71where
72    N: Class,
73    O: Class,
74{
75    type Output = DuoClass<Self, OptionClass<O>>;
76
77    fn add(self, other: Option<O>) -> Self::Output {
78        DuoClass::new(self, OptionClass::new(other))
79    }
80}
81
82impl<N: fmt::Display> fmt::Display for AttrClass<N> {
83    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
84        write!(f, "{}", self.parent)?;
85
86        for attr in &self.attrs {
87            write!(f, " {}--{}", self.parent, attr)?;
88        }
89
90        Ok(())
91    }
92}
93
94impl<'a, N: fmt::Display> From<AttrClass<N>> for Cow<'a, str> {
95    fn from(class: AttrClass<N>) -> Self {
96        class.to_string().into()
97    }
98}
99
100impl<'a, N: fmt::Display> From<AttrClass<N>> for String {
101    fn from(class: AttrClass<N>) -> Self {
102        class.to_string()
103    }
104}
105
106#[cfg(test)]
107mod maybe_attr {
108    use super::*;
109    use crate::classes::*;
110
111    #[test]
112    fn it_should_set_attr_if_is_set() {
113        let attr = AttrClass::new(BaseClass::new("mr-component")).attr("large");
114        assert_eq!(
115            attr.maybe_attr("red", true).to_string(),
116            "mr-component mr-component--large mr-component--red",
117        )
118    }
119
120    #[test]
121    fn it_should_not_set_attr_if_is_set_is_false() {
122        let attr = AttrClass::new(BaseClass::new("mr-component")).attr("large");
123        assert_eq!(
124            attr.maybe_attr("red", false).to_string(),
125            "mr-component mr-component--large",
126        )
127    }
128
129    #[test]
130    fn it_should_still_set_more_attr_after_false_maybe_attr() {
131        let attr = AttrClass::new(BaseClass::new("mr-component")).attr("large");
132        assert_eq!(
133            attr.maybe_attr("red", false).attr("blue").to_string(),
134            "mr-component mr-component--large mr-component--blue",
135        )
136    }
137}
138
139#[cfg(test)]
140mod el {
141    use super::*;
142    use crate::classes::*;
143
144    #[test]
145    fn it_should_use_parent_element_when_creating_child_classes() {
146        let class = AttrClass::new(BaseClass::new("mr-component")).attr("bold");
147        assert_eq!(class.el("child").to_string(), "mr-component__child");
148    }
149}