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}