stylish_stringlike/text/
width.rs

1use std::iter::Sum;
2use std::ops::{Add, AddAssign};
3
4/// An enum representing the unicode width of a (possibly infinte) text object
5#[derive(Copy, Clone, Debug, Eq, PartialEq)]
6pub enum Width {
7    /// A finite width
8    Bounded(usize),
9    /// An infinite width
10    Unbounded,
11}
12
13impl Add for Width {
14    type Output = Width;
15    fn add(self, other: Self) -> Self::Output {
16        use Width::{Bounded, Unbounded};
17        match (self, other) {
18            (Unbounded, _) | (_, Unbounded) => Unbounded,
19            (Bounded(left), Bounded(right)) => Bounded(left + right),
20        }
21    }
22}
23
24impl AddAssign for Width {
25    fn add_assign(&mut self, other: Self) {
26        *self = *self + other;
27    }
28}
29
30impl Sum for Width {
31    fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
32        iter.fold(Width::Bounded(0), |a, b| a + b)
33    }
34}
35
36/// Support for returning the unicode width of a text object
37pub trait HasWidth {
38    /// Return the unicode width of an object
39    ///
40    /// # Example
41    /// ```
42    /// use stylish_stringlike::text::{HasWidth, Width};
43    /// let foo = "foobar";
44    /// assert_eq!(foo.width(), Width::Bounded(6));
45    /// let bar = String::from("🙈🙉🙊");
46    /// assert_eq!(bar.width(), Width::Bounded(6));
47    /// ```
48    fn width(&self) -> Width;
49}
50
51impl<T> HasWidth for Option<T>
52where
53    T: HasWidth,
54{
55    fn width(&self) -> Width {
56        match self {
57            Some(t) => t.width(),
58            None => Width::Bounded(0),
59        }
60    }
61}
62
63/// Support for returing the unicode width of text objects that are finite
64pub trait BoundedWidth {
65    /// Return the finite unicode width of an object
66    ///
67    /// # Example
68    /// ```
69    /// use stylish_stringlike::text::BoundedWidth;
70    /// let foo = "foobar";
71    /// assert_eq!(foo.bounded_width(), 6);
72    /// let bar = String::from("🙈🙉🙊");
73    /// assert_eq!(bar.bounded_width(), 6);
74    /// ```
75    fn bounded_width(&self) -> usize;
76}
77
78impl BoundedWidth for String {
79    fn bounded_width(&self) -> usize {
80        unicode_width::UnicodeWidthStr::width(self.as_str())
81    }
82}
83
84impl HasWidth for String {
85    fn width(&self) -> Width {
86        Width::Bounded(self.bounded_width())
87    }
88}
89
90impl BoundedWidth for &str {
91    fn bounded_width(&self) -> usize {
92        unicode_width::UnicodeWidthStr::width(*self)
93    }
94}
95
96impl HasWidth for &str {
97    fn width(&self) -> Width {
98        Width::Bounded(self.bounded_width())
99    }
100}
101
102impl<T> BoundedWidth for Option<T>
103where
104    T: BoundedWidth,
105{
106    fn bounded_width(&self) -> usize {
107        match self {
108            Some(t) => t.bounded_width(),
109            None => 0,
110        }
111    }
112}
113
114#[cfg(test)]
115mod test {
116    use super::*;
117    #[test]
118    fn add_bounded() {
119        let actual = Width::Bounded(4) + Width::Bounded(6);
120        let expected = Width::Bounded(10);
121        assert_eq!(expected, actual);
122    }
123    #[test]
124    fn add_bound_unbound() {
125        let actual = Width::Bounded(4) + Width::Unbounded;
126        let expected = Width::Unbounded;
127        assert_eq!(expected, actual);
128    }
129    #[test]
130    fn add_unbound_bound() {
131        let actual = Width::Unbounded + Width::Bounded(4);
132        let expected = Width::Unbounded;
133        assert_eq!(expected, actual);
134    }
135    #[test]
136    fn add_bound_bound() {
137        let actual = Width::Unbounded + Width::Unbounded;
138        let expected = Width::Unbounded;
139        assert_eq!(expected, actual);
140    }
141    #[test]
142    fn sum() {
143        let v = vec![Width::Bounded(5), Width::Bounded(6), Width::Bounded(7)];
144        let actual: Width = v.iter().cloned().sum();
145        let expected = Width::Bounded(18);
146        assert_eq!(expected, actual);
147    }
148    #[test]
149    fn add_assign() {
150        let mut actual = Width::Bounded(1);
151        actual += Width::Bounded(4);
152        let expected = Width::Bounded(5);
153        assert_eq!(expected, actual);
154    }
155}