gradient_string/
lib.rs

1#![doc(issue_tracker_base_url = "https://github.com/gabrielfalcao/unique-pointer/issues/")]
2//! # Gradient String
3//!
4//! gradient-string is a safe crate to iterate over a gradient of
5//! permutations of string slices
6//!
7//! ## Example
8//!
9//! ```
10//! use gradient_string::Gradient;
11//! let result = Gradient::new(" abc ")
12//!     .collect::<Vec<String>>();
13//! assert_eq!(
14//!     result,
15//!     vec![
16//!         " ", "a", "b", "c", " ", " a", "ab", "bc", "c ", " ab", "abc", "bc ", " abc",
17//!         "abc ", " abc "
18//!     ]
19//! );
20//! ```
21use std::fmt::Display;
22
23/// ```
24/// use gradient_string::Gradient;
25/// let result = Gradient::new(" abc ")
26///     .collect::<Vec<String>>();
27/// assert_eq!(
28///     result,
29///     vec![
30///         " ", "a", "b", "c", " ", " a", "ab", "bc", "c ", " ab", "abc", "bc ", " abc",
31///         "abc ", " abc "
32///     ]
33/// );
34/// ```
35#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
36pub struct Gradient {
37    input: String,
38    start: usize,
39    end: usize,
40    width: usize,
41    wide: bool,
42    max_width: Option<usize>,
43}
44impl Iterator for Gradient {
45    type Item = String;
46
47    fn next(&mut self) -> Option<String> {
48        if self.finished() {
49            return None;
50        }
51        self.end += 1;
52        if !self.wide {
53            self.wide = true;
54            self.width += 1;
55            self.start = 0;
56            self.end = self.width;
57        }
58
59        self.start = self.end - self.width;
60        if self.end == self.len() {
61            self.wide = false;
62        }
63        if let Some(max_width) = self.max_width {
64            if self.width > max_width {
65                return None;
66            }
67        }
68        Some(self.window())
69    }
70}
71impl<'a> Gradient {
72    pub fn input(&self) -> &'a str {
73        unsafe { core::mem::transmute::<&str, &'a str>(&self.input[self.range()]) }
74    }
75}
76impl Gradient {
77    pub fn window(&self) -> String {
78        self.input[self.range()].to_string()
79    }
80
81    pub fn finished(&self) -> bool {
82        if self.len() == 0 {
83            return true;
84        }
85        if self.end == self.len() {
86            if self.width == self.len() {
87                return true;
88            }
89        }
90        false
91    }
92
93    pub fn width(&self) -> usize {
94        self.width
95    }
96
97    pub fn start(&self) -> usize {
98        self.start
99    }
100
101    pub fn end(&self) -> usize {
102        self.end
103    }
104
105    pub fn range(&self) -> core::ops::Range<usize> {
106        self.start()..self.end()
107    }
108
109    pub fn len(&self) -> usize {
110        self.input.len()
111    }
112
113    pub fn new<T: Display>(s: T) -> Gradient {
114        Gradient::with_max_width(s, None)
115    }
116
117    /// Creates a [Gradient](Self) that optionally spans to a maximum
118    /// string width.
119    ///
120    /// ```
121    /// use gradient_string::Gradient;
122    ///
123    /// let result = Gradient::with_max_width(" abc ", Some(2))
124    ///     .collect::<Vec<String>>();
125    /// assert_eq!(
126    ///     result,
127    ///     vec![" ", "a", "b", "c", " ", " a", "ab", "bc", "c "]
128    /// );
129    /// ```
130
131    pub fn with_max_width<T: Display>(s: T, max_width: Option<usize>) -> Gradient {
132        Gradient {
133            input: s.to_string(),
134            start: 0,
135            end: 0,
136            width: 1,
137            wide: true,
138            max_width,
139        }
140    }
141}
142
143#[cfg(test)]
144mod tests {
145    use super::*;
146
147    #[test]
148    fn gradient() {
149        let result = Gradient::new(" abc ").collect::<Vec<String>>();
150        assert_eq!(
151            result,
152            vec![
153                " ", "a", "b", "c", " ", " a", "ab", "bc", "c ", " ab", "abc", "bc ", " abc",
154                "abc ", " abc "
155            ]
156        );
157    }
158    #[test]
159    fn empty() {
160        assert_eq!(Gradient::new("").collect::<Vec<_>>().len(), 0);
161    }
162
163    #[test]
164    fn max_width() {
165        let result = Gradient::with_max_width(" abc ", Some(2)).collect::<Vec<String>>();
166        assert_eq!(
167            result,
168            vec![" ", "a", "b", "c", " ", " a", "ab", "bc", "c "]
169        );
170    }
171}