lorem_rustum/
lib.rs

1//!
2//! A simple lib for generating random lorem-ipsum with a rusty fleur
3//! using [`rand::thread_rng()`](https://docs.rs/rand/latest/rand/).
4//!
5//! # Quick Start
6//!```
7//! use lorem_rustum::LoremRustum;
8//!
9//! let length = 42;
10//! let lorem = LoremRustum::new(length);
11//! println!("{}", lorem.to_string());
12//!```
13//!
14//! # Other examples
15//! ```
16//! use lorem_rustum::{LoremRustum,lorem};
17//!
18//! let mut lorem = LoremRustum::default();
19//! let text1 = lorem.to_string();
20//!
21//! lorem.shuffle();
22//! let text2 = lorem.to_string();
23//!
24//! assert_ne!(text1, text2);
25//!
26//! let text3 = lorem!(156);
27//! let text4 = lorem!();   // similar to LoremRustum::default().to_string()
28//! ```
29//! # Example Output
30//! ```bash,ignore
31//! checking heap rust-analyzer cargo package manager control
32//! over memory allocation static borrowing wasm the most admired
33//! language fast and reliable impl derive Ferris mascot actix multiple
34//! threads can work on shared data without introducing memory-related
35//! issues to_owned() no garbage collector safety sqlx scope mod rocket
36//! concurrency pub code execution loved by developers rusty stack async
37//! efficient and organized development
38//! ```
39
40use rand::prelude::*;
41
42mod data;
43
44#[derive(PartialEq, Debug, Clone)]
45pub struct LoremRustum {
46    pub body: Vec<&'static str>,
47    pub length: usize,
48}
49
50impl LoremRustum {
51    /// Build `LoremRustum` struct with specified length
52    /// by choosing random elements from `RUSTY_PHRASES` using `rand::thread_rng()`.
53    ///
54    /// # Examples
55    /// ```
56    /// let lorem = lorem_rustum::LoremRustum::new(42);
57    /// println!("{}", lorem.to_string());
58    /// ```
59    ///
60    /// ### Rusty Phrases:
61    /// ```
62    /// pub static RUSTY_PHRASES: [&str; 132] = [
63    ///     "rust", "borrowing", "tokio", "async", "unsafe", "thread", "trait", "&str",
64    ///     "rust-analyzer", "scope", "await", "ryan", "ownership", "safety", "nightly",
65    ///     "allocated", "rustlings", "stack", "heap", "no garbage collector", "runtime",
66    ///     "macros", "code execution", "fmt", "clippy", "memory safe", "cargo", "built-in",
67    ///     "performance", "golang sucks", "better than c++", "friendly community",
68    ///     "loved by developers", "wasm", "webassembly", "actix", "axum", "rocket", "yew",
69    ///     "diesel", "sqlx", "pub", "mod", "enum", "static", "missing lifetime", "rusty",
70    ///     "the most admired language", "a safer alternative to C and C++", "cargo package manager",
71    ///     "performance-critical services", "Ferris mascot", "fast and reliable", "control over memory allocation",
72    ///     "deallocation", "deref", "derive", "impl", "implement trait for", "to_owned()",
73    ///     "i'm not looking for a job", "<'static>", "mut", "&mut", "efficient and organized development workflows",
74    ///     "concurrency", "multiple threads can work on shared data without introducing memory-related issues",
75    ///     "low-level tools and kernels", "type checking", "unwrap", "please gouge out my eyes",
76    ///     "Sync + Send", "thread safety", "spawn concurrent task", "non-blocking i/o",
77    ///     "smart pointer", "<'a>", "cargo test", "async-std", "println!", "dbg!", "dyn",
78    ///     "stderr", "mpsc", "async move", "Arc", "Rc", "RefCell", "Box", "||", "expect",
79    ///     "map", "making", "building", "produce", "consume", "out of scope", "rustc",
80    ///     "rustup", "panic", "generics", "<T>", "impl fmt::Display for", "macro",
81    ///     "#[derive()]", "multiple bounds", "traits", "macro_rules!", "Some()", "Option<&T>",
82    ///     "None", "RAII", "drop", "destructor", "mutability of data", "ref", "as",
83    ///     "closures", "HOF", "Higher Order Functions", "lazy loading", "err", "error",
84    ///     "Result<T, Error>", "()", "Err(_)", "std", "#[cfg(test)]", "assert!",
85    ///     "cargo run", "publish crate", "code blocks below"
86    /// ];
87    /// ```
88    pub fn new(length: usize) -> LoremRustum {
89        let mut rng = rand::thread_rng();
90        let body = LoremRustum::get_body(&mut rng, length);
91        LoremRustum { body, length }
92    }
93
94    fn get_body(rng: &mut ThreadRng, length: usize) -> Vec<&'static str> {
95        if length > data::RUSTY_PHRASES.len() {
96            return LoremRustum::get_bigger_body(rng, length);
97        }
98        let mut rusty_words: Vec<&str> = data::RUSTY_PHRASES.to_vec();
99        rusty_words.shuffle(rng);
100        rusty_words.drain(0..length).collect()
101    }
102
103    fn get_bigger_body(rng: &mut ThreadRng, length: usize) -> Vec<&'static str> {
104        let mut body = vec![];
105        for _ in 0..length {
106            body.push(data::RUSTY_PHRASES.choose(rng).unwrap().to_owned())
107        }
108        body
109    }
110
111    /// Shuffle body of `LoremRustum` in place using `shuffle()` from `rand` crate.
112    ///
113    /// # Examples
114    /// ```
115    /// let mut lorem = lorem_rustum::LoremRustum::default();
116    /// let text = lorem.to_string();
117    ///
118    /// lorem.shuffle();
119    /// let new_text = lorem.to_string();
120    ///
121    /// assert_ne!(text, new_text);
122    /// ```
123    pub fn shuffle(&mut self) {
124        let mut rng = rand::thread_rng();
125        self.body.shuffle(&mut rng);
126    }
127}
128
129impl ToString for LoremRustum {
130    fn to_string(&self) -> String {
131        self.body.join(" ")
132    }
133}
134
135impl Default for LoremRustum {
136    /// Build `LoremRustum` with all `RUSTY_PHRASES` (contains 132 phrases).
137    ///
138    /// # Examples
139    /// ```
140    /// let lorem = lorem_rustum::LoremRustum::default();
141    /// println!("{}", lorem.to_string());
142    /// ```
143    fn default() -> Self {
144        let length = data::RUSTY_PHRASES.len();
145        LoremRustum::new(length)
146    }
147}
148
149/// Generates lorem rustum text with given length
150/// or with default length if no argument was passed.
151///
152/// Examples
153///
154/// ```
155/// use lorem_rustum::{LoremRustum, lorem};
156///
157/// let text: String = lorem!(17);
158///
159/// println!("{}", lorem!(5));
160/// println!("{}", lorem!());
161/// ```
162///
163#[macro_export]
164macro_rules! lorem {
165    ($length:expr) => {{
166        LoremRustum::new($length).to_string()
167    }};
168    () => {{
169        LoremRustum::default().to_string()
170    }};
171}
172
173#[cfg(test)]
174mod tests {
175    use super::*;
176
177    fn sort_text(text: String) -> String {
178        let mut s: Vec<char> = text.chars().collect();
179        s.sort();
180        let text_sorted: String = s.into_iter().filter(|x| !x.is_whitespace()).collect();
181        text_sorted
182    }
183
184    #[test]
185    fn test_new() {
186        let length = data::RUSTY_PHRASES.len();
187        let full_text = String::from_iter(data::RUSTY_PHRASES);
188        let result = LoremRustum::new(length);
189
190        assert!(result.body.len() == length);
191        for i in 0..length {
192            assert!(full_text.contains(result.body[i]))
193        }
194    }
195
196    #[test]
197    fn test_default() {
198        let length = data::RUSTY_PHRASES.len();
199        let lorem_rustum = LoremRustum::new(length);
200        let result = LoremRustum::default();
201
202        let full_text = String::from_iter(data::RUSTY_PHRASES);
203        let full_text_sorted = sort_text(full_text);
204        let result_sorted = sort_text(result.to_string());
205
206        assert_eq!(result.body.len(), lorem_rustum.body.len());
207        assert_eq!(full_text_sorted.len(), result_sorted.len());
208        assert_eq!(result_sorted, result_sorted);
209    }
210
211    #[test]
212    fn test_length() {
213        let rusty_words_len = data::RUSTY_PHRASES.len();
214        let length = rusty_words_len / 2;
215        let result = LoremRustum::new(length);
216
217        assert_eq!(result.body.len(), length);
218
219        let length = rusty_words_len * 4;
220        let result = LoremRustum::new(length);
221
222        assert_eq!(result.body.len(), length);
223    }
224
225    #[test]
226    fn test_to_string() {
227        let result = LoremRustum::new(5);
228        let string = result.body.join(" ");
229
230        assert_eq!(string, result.to_string());
231    }
232
233    #[test]
234    fn test_random() {
235        let length = 25;
236        let result = LoremRustum::new(length);
237        let body: Vec<&str> = data::RUSTY_PHRASES
238            .into_iter()
239            .enumerate()
240            .filter(|&(i, _)| i < length)
241            .map(|(_, e)| e)
242            .collect();
243        let lorem = LoremRustum { body, length };
244
245        assert_ne!(result, lorem);
246    }
247
248    #[test]
249    fn test_get_body() {
250        let length = data::RUSTY_PHRASES.len() / 2;
251        let mut rng = rand::thread_rng();
252        let result = LoremRustum::get_body(&mut rng, length);
253        let body: Vec<&str> = data::RUSTY_PHRASES
254            .into_iter()
255            .enumerate()
256            .filter(|&(i, _)| i < length)
257            .map(|(_, e)| e)
258            .collect();
259
260        assert!(result.len() == length);
261        assert_eq!(result.len(), body.len());
262    }
263
264    #[test]
265    fn test_get_bigger_body() {
266        use rand::prelude::*;
267        let length = data::RUSTY_PHRASES.len() * 2;
268        let mut rng = rand::thread_rng();
269
270        let result = LoremRustum::get_bigger_body(&mut rng, length);
271        let mut body = vec![];
272        for _ in 0..length {
273            body.push(data::RUSTY_PHRASES.choose(&mut rng).unwrap().to_owned())
274        }
275
276        assert!(result.len() == length);
277        assert_eq!(result.len(), body.len())
278    }
279
280    #[test]
281    fn test_shuffle() {
282        let mut lorem = LoremRustum::default();
283        let text = lorem.to_string();
284        lorem.shuffle();
285        let new_text = lorem.to_string();
286
287        assert_eq!(text.len(), new_text.len());
288        assert_ne!(text, new_text);
289    }
290
291    #[test]
292    fn test_lorem_macro() {
293        let length = 16;
294        let full_text = String::from_iter(data::RUSTY_PHRASES);
295        let text = lorem!(length);
296        let split_words: Vec<&str> = text.split(' ').collect();
297
298        for word in split_words {
299            assert!(full_text.contains(word));
300        }
301    }
302
303    #[test]
304    fn test_lorem_macro_without_args() {
305        let full_text = String::from_iter(data::RUSTY_PHRASES);
306        let full_text_sorted = sort_text(full_text);
307
308        let result = lorem!();
309        let result_sorted = sort_text(result);
310
311        assert_eq!(full_text_sorted, result_sorted);
312    }
313}