Skip to main content

figlet_rs/
lib.rs

1//! You can visit [`figlet`] and [`figfont`] to find more details.
2//! You can visit [`fontdb`] to find more fonts.
3//!
4//! # Examples
5//!
6//! Convert string literals using built-in FIGlet or Toilet fonts:
7//!
8//! ```
9//! use figlet_rs::{FIGlet, Toilet};
10//!
11//! let standard_font = FIGlet::standard().unwrap();
12//! assert!(standard_font.convert("FIGlet").is_some());
13//!
14//! let slant_font = FIGlet::slant().unwrap();
15//! assert!(slant_font.convert("FIGlet").is_some());
16//!
17//! let smblock_font = Toilet::smblock().unwrap();
18//! assert!(smblock_font.convert("Toilet").is_some());
19//! ```
20//!
21//! [`figlet`]: http://www.figlet.org
22//! [`figfont`]: http://www.jave.de/figlet/figfont.html
23//! [`fontdb`]: http://www.figlet.org/fontdb.cgi
24
25mod figlet;
26mod shared;
27mod toilet;
28
29pub use figlet::FIGlet;
30pub use shared::{FIGcharacter, FIGure, HeaderLine};
31pub use toilet::Toilet;
32
33#[cfg(test)]
34mod tests {
35    use super::*;
36    use crate::shared::{
37        FontData, SM_BIGX, SM_EQUAL, SM_HARDBLANK, SM_HIERARCHY, SM_KERN, SM_LOWLINE, SM_PAIR,
38        SM_SMUSH,
39    };
40    use std::fs;
41    use std::path::Path;
42
43    fn fixture(path: &str) -> String {
44        let root = Path::new(env!("CARGO_MANIFEST_DIR"));
45        fs::read_to_string(root.join(path)).unwrap()
46    }
47
48    fn full_smush_font() -> FIGlet {
49        let mut font = FIGlet::standard().unwrap();
50        font.header_line.full_layout = Some(
51            SM_EQUAL | SM_LOWLINE | SM_HIERARCHY | SM_PAIR | SM_BIGX | SM_HARDBLANK | SM_SMUSH,
52        );
53        font
54    }
55
56    fn assert_golden_fixture_figlet(font: &FIGlet, message: &str, fixture_path: &str) {
57        let figure = font.convert(message).unwrap();
58        assert_eq!(fixture(fixture_path), figure.as_str());
59    }
60
61    fn assert_golden_fixture_toilet(font: &Toilet, message: &str, fixture_path: &str) {
62        let figure = font.convert(message).unwrap();
63        assert_eq!(fixture(fixture_path), figure.as_str());
64    }
65
66    #[test]
67    fn test_new_headerline() {
68        let line = "flf2a$ 6 5 20 15 3 0 143 229";
69        let headerline = HeaderLine::try_from(line).unwrap();
70
71        assert_eq!(line, headerline.header_line);
72        assert_eq!("flf2a", headerline.signature);
73        assert_eq!('$', headerline.hardblank);
74        assert_eq!(6, headerline.height);
75        assert_eq!(5, headerline.baseline);
76        assert_eq!(20, headerline.max_length);
77        assert_eq!(15, headerline.old_layout);
78        assert_eq!(3, headerline.comment_lines);
79        assert_eq!(Some(0), headerline.print_direction);
80        assert_eq!(Some(143), headerline.full_layout);
81        assert_eq!(Some(229), headerline.codetag_count);
82    }
83
84    #[test]
85    fn test_new_figfont() {
86        let font = FIGlet::standard().unwrap();
87
88        assert_eq!("flf2a$ 6 5 16 15 11 0 24463", font.header_line.header_line);
89        assert_eq!("flf2a", font.header_line.signature);
90        assert_eq!('$', font.header_line.hardblank);
91        assert_eq!(6, font.header_line.height);
92        assert_eq!(5, font.header_line.baseline);
93        assert_eq!(16, font.header_line.max_length);
94        assert_eq!(15, font.header_line.old_layout);
95        assert_eq!(11, font.header_line.comment_lines);
96        assert_eq!(Some(0), font.header_line.print_direction);
97        assert_eq!(Some(24463), font.header_line.full_layout);
98        assert_eq!(None, font.header_line.codetag_count);
99
100        let one_font = font.fonts.get(&('F' as u32)).unwrap();
101        assert_eq!(70, one_font.code);
102        assert_eq!(8, one_font.width);
103        assert_eq!(6, one_font.height);
104        assert_eq!(6, one_font.characters.len());
105        assert_eq!("  _____ ", one_font.characters.first().unwrap());
106    }
107
108    #[test]
109    fn test_convert() {
110        let standard_font = FIGlet::standard().unwrap();
111        let figure = standard_font.convert("FIGlet").unwrap();
112
113        assert_eq!(6, figure.height);
114        assert_eq!(6, figure.characters.len());
115        assert_eq!("  _____ ", figure.characters[0].characters.first().unwrap());
116    }
117
118    #[test]
119    fn test_convert_empty_string() {
120        let font = FIGlet::standard().unwrap();
121        assert!(font.convert("").is_none());
122    }
123
124    #[test]
125    fn test_convert_single_character() {
126        let font = FIGlet::standard().unwrap();
127        let figure = font.convert("A").unwrap();
128        assert_eq!(1, figure.characters.len());
129        assert_eq!(6, figure.height);
130    }
131
132    #[test]
133    fn test_convert_all_ascii_printable() {
134        let font = FIGlet::standard().unwrap();
135        let all_ascii: String = (32..=126).map(|c| char::from_u32(c).unwrap()).collect();
136        let figure = font.convert(&all_ascii).unwrap();
137        assert_eq!(95, figure.characters.len());
138    }
139
140    #[test]
141    fn test_convert_with_unknown_characters() {
142        let font = FIGlet::standard().unwrap();
143        let figure = font.convert("Hello世界").unwrap();
144        assert_eq!(5, figure.characters.len());
145    }
146
147    #[test]
148    fn test_figure_as_str() {
149        let font = FIGlet::standard().unwrap();
150        let figure = font.convert("Hi").unwrap();
151        let s = figure.as_str();
152        assert!(!s.is_empty());
153        assert_eq!(figure.height as usize, s.lines().count());
154    }
155
156    #[test]
157    fn test_figure_display() {
158        let font = FIGlet::standard().unwrap();
159        let figure = font.convert("AB").unwrap();
160        let display_output = format!("{}", figure);
161        let debug_output = format!("{:?}", figure);
162
163        assert!(!display_output.is_empty());
164        assert!(display_output.contains('\n'));
165        assert!(!debug_output.is_empty());
166    }
167
168    #[test]
169    fn test_standard_golden_samples() {
170        let font = FIGlet::standard().unwrap();
171        assert_golden_fixture_figlet(&font, "Test", "tests/fixtures/figlet_standard_test.txt");
172        assert_golden_fixture_figlet(&font, "FIGlet", "tests/fixtures/figlet_standard_figlet.txt");
173        assert_golden_fixture_figlet(
174            &font,
175            "-4.5",
176            "tests/fixtures/figlet_standard_negative_float.txt",
177        );
178        assert_golden_fixture_figlet(
179            &font,
180            "Hello Rust",
181            "tests/fixtures/figlet_standard_hello_rust.txt",
182        );
183    }
184
185    #[test]
186    fn test_small_golden_samples() {
187        let font = FIGlet::small().unwrap();
188        assert_golden_fixture_figlet(&font, "Test", "tests/fixtures/figlet_small_test.txt");
189        assert_golden_fixture_figlet(&font, "FIGlet", "tests/fixtures/figlet_small_figlet.txt");
190        assert_golden_fixture_figlet(
191            &font,
192            "-4.5",
193            "tests/fixtures/figlet_small_negative_float.txt",
194        );
195        assert_golden_fixture_figlet(
196            &font,
197            "Hello Rust",
198            "tests/fixtures/figlet_small_hello_rust.txt",
199        );
200    }
201
202    #[test]
203    fn test_effective_layout_prefers_full_layout() {
204        let header = HeaderLine::try_from("flf2a$ 6 5 20 15 3 0 143 229").unwrap();
205        assert_eq!(143, header.effective_layout());
206    }
207
208    #[test]
209    fn test_effective_layout_uses_old_layout_compatibility() {
210        let kerning = HeaderLine::try_from("flf2a$ 6 5 20 0 3").unwrap();
211        let full_width = HeaderLine::try_from("flf2a$ 6 5 20 -1 3").unwrap();
212        let smushing = HeaderLine::try_from("flf2a$ 6 5 20 15 3").unwrap();
213
214        assert_eq!(SM_KERN, kerning.effective_layout());
215        assert_eq!(0, full_width.effective_layout());
216        assert_eq!(143, smushing.effective_layout());
217    }
218
219    #[test]
220    fn test_smush_rule_equal() {
221        let font = full_smush_font();
222        let renderer = crate::shared::render(&font.header_line, &font.fonts, "||").unwrap();
223        assert!(renderer.is_not_empty());
224    }
225
226    #[test]
227    fn test_smush_rule_lowline_and_hierarchy() {
228        let font = full_smush_font();
229        let figure = font.convert("_/|>").unwrap();
230        assert!(figure.is_not_empty());
231    }
232
233    #[test]
234    fn test_figure_is_not_empty() {
235        let font = FIGlet::standard().unwrap();
236        assert!(font.convert("Test").unwrap().is_not_empty());
237    }
238
239    #[test]
240    fn test_figure_with_only_unknown_chars() {
241        let font = FIGlet::standard().unwrap();
242        assert!(font.convert("\u{4E2D}\u{6587}").is_none());
243    }
244
245    #[test]
246    fn test_figfont_clone() {
247        let font1 = FIGlet::standard().unwrap();
248        let font2 = font1.clone();
249
250        assert!(font1.convert("Test").is_some());
251        assert!(font2.convert("Test").is_some());
252    }
253
254    #[test]
255    fn test_standard_font_loading() {
256        let font1 = FIGlet::standard().unwrap();
257        let font2 = FIGlet::from_file("resources/standard.flf").unwrap();
258
259        assert_eq!(font1.header_line.header_line, font2.header_line.header_line);
260        assert_eq!(font1.comments, font2.comments);
261    }
262
263    #[test]
264    fn test_small_font_loading() {
265        let font1 = FIGlet::small().unwrap();
266        let font2 = FIGlet::from_file("resources/small.flf").unwrap();
267
268        assert_eq!(font1.header_line.header_line, font2.header_line.header_line);
269        assert_eq!(font1.comments, font2.comments);
270    }
271
272    #[test]
273    fn test_big_golden_samples() {
274        let font = FIGlet::big().unwrap();
275        assert_golden_fixture_figlet(&font, "Test", "tests/fixtures/figlet_big_test.txt");
276        assert_golden_fixture_figlet(&font, "FIGlet", "tests/fixtures/figlet_big_figlet.txt");
277        assert_golden_fixture_figlet(
278            &font,
279            "-4.5",
280            "tests/fixtures/figlet_big_negative_float.txt",
281        );
282        assert_golden_fixture_figlet(
283            &font,
284            "Hello Rust",
285            "tests/fixtures/figlet_big_hello_rust.txt",
286        );
287    }
288
289    #[test]
290    fn test_slant_golden_samples() {
291        let font = FIGlet::slant().unwrap();
292        assert_golden_fixture_figlet(&font, "Test", "tests/fixtures/figlet_slant_test.txt");
293        assert_golden_fixture_figlet(&font, "FIGlet", "tests/fixtures/figlet_slant_figlet.txt");
294        assert_golden_fixture_figlet(
295            &font,
296            "-4.5",
297            "tests/fixtures/figlet_slant_negative_float.txt",
298        );
299        assert_golden_fixture_figlet(
300            &font,
301            "Hello Rust",
302            "tests/fixtures/figlet_slant_hello_rust.txt",
303        );
304    }
305
306    #[test]
307    fn test_big_font_loading() {
308        let font1 = FIGlet::big().unwrap();
309        let font2 = FIGlet::from_file("resources/big.flf").unwrap();
310
311        assert_eq!(font1.header_line.header_line, font2.header_line.header_line);
312        assert_eq!(font1.comments, font2.comments);
313    }
314
315    #[test]
316    fn test_slant_font_loading() {
317        let font1 = FIGlet::slant().unwrap();
318        let font2 = FIGlet::from_file("resources/slant.flf").unwrap();
319
320        assert_eq!(font1.header_line.header_line, font2.header_line.header_line);
321        assert_eq!(font1.comments, font2.comments);
322    }
323
324    #[test]
325    fn test_figure_character_width_and_height() {
326        let font = FIGlet::standard().unwrap();
327        let figure = font.convert("ABC").unwrap();
328
329        for char in &figure.characters {
330            assert_eq!(figure.height, char.height);
331            assert!(char.width > 0);
332        }
333    }
334
335    #[test]
336    fn test_latin_characters() {
337        let font = FIGlet::standard().unwrap();
338        let figure = font.convert("\u{00C4}\u{00D6}\u{00DC}").unwrap();
339        assert_eq!(3, figure.characters.len());
340    }
341
342    #[test]
343    fn test_from_content_invalid() {
344        assert!(FIGlet::from_content("").is_err());
345        assert!(FIGlet::from_content("invalid").is_err());
346    }
347
348    #[test]
349    fn test_headerline_invalid() {
350        assert!(HeaderLine::try_from("flf2a$ 6").is_err());
351    }
352
353    #[test]
354    fn test_toilet_header_supports_tlf_signature() {
355        let header = HeaderLine::try_from("tlf2a$ 4 3 8 0 16 0 64 0").unwrap();
356        assert_eq!("tlf2a", header.signature);
357        assert_eq!(4, header.height);
358    }
359
360    #[test]
361    fn test_toilet_from_content() {
362        let content = fixture("resources/smblock.tlf");
363        let font = Toilet::from_content(&content).unwrap();
364        assert_eq!("tlf2a", font.header_line.signature);
365        assert!(font.convert("Test").is_some());
366    }
367
368    #[test]
369    fn test_toilet_builtin_text_font_loading() {
370        let font1 = Toilet::smblock().unwrap();
371        let font2 = Toilet::from_file("resources/smblock.tlf").unwrap();
372        assert_eq!(font1.header_line.header_line, font2.header_line.header_line);
373        assert_eq!(font1.comments, font2.comments);
374    }
375
376    #[test]
377    fn test_toilet_builtin_zipped_font_loading() {
378        let font1 = Toilet::mono12().unwrap();
379        let font2 = Toilet::from_file("resources/mono12.tlf").unwrap();
380        assert_eq!(font1.header_line.header_line, font2.header_line.header_line);
381        assert_eq!(font1.comments, font2.comments);
382    }
383
384    #[test]
385    fn test_toilet_smblock_golden_samples() {
386        let font = Toilet::smblock().unwrap();
387        assert_golden_fixture_toilet(&font, "Test", "tests/fixtures/toilet_smblock_test.txt");
388        assert_golden_fixture_toilet(&font, "FIGlet", "tests/fixtures/toilet_smblock_figlet.txt");
389        assert_golden_fixture_toilet(
390            &font,
391            "-4.5",
392            "tests/fixtures/toilet_smblock_negative_float.txt",
393        );
394    }
395
396    #[test]
397    fn test_toilet_future_golden_samples() {
398        let font = Toilet::future().unwrap();
399        assert_golden_fixture_toilet(&font, "Test", "tests/fixtures/toilet_future_test.txt");
400        assert_golden_fixture_toilet(
401            &font,
402            "Hello Rust",
403            "tests/fixtures/toilet_future_hello_rust.txt",
404        );
405    }
406
407    #[test]
408    fn test_toilet_wideterm_golden_samples() {
409        let font = Toilet::wideterm().unwrap();
410        assert_golden_fixture_toilet(&font, "FIGlet", "tests/fixtures/toilet_wideterm_figlet.txt");
411    }
412
413    #[test]
414    fn test_toilet_mono12_golden_samples() {
415        let font = Toilet::mono12().unwrap();
416        assert_golden_fixture_toilet(&font, "Test", "tests/fixtures/toilet_mono12_test.txt");
417    }
418
419    #[test]
420    fn test_toilet_mono9_golden_samples() {
421        let font = Toilet::mono9().unwrap();
422        assert_golden_fixture_toilet(
423            &font,
424            "Hello Rust",
425            "tests/fixtures/toilet_mono9_hello_rust.txt",
426        );
427    }
428
429    #[test]
430    fn test_toilet_external_zipped_font_matches_builtin() {
431        let external = Toilet::from_file("resources/mono9.tlf").unwrap();
432        let builtin = Toilet::mono9().unwrap();
433        assert_eq!(
434            builtin.convert("Test").unwrap().as_str(),
435            external.convert("Test").unwrap().as_str()
436        );
437    }
438
439    #[test]
440    fn test_toilet_unknown_chars_are_skipped() {
441        let font = Toilet::smblock().unwrap();
442        let figure = font.convert("Toilet世界").unwrap();
443        assert_eq!(6, figure.characters.len());
444    }
445
446    #[test]
447    fn test_toilet_from_content_invalid() {
448        assert!(Toilet::from_content("").is_err());
449    }
450
451    #[test]
452    fn test_from_font_data_roundtrip() {
453        let font = FIGlet::standard().unwrap();
454        let data = FontData {
455            header_line: font.header_line.clone(),
456            comments: font.comments.clone(),
457            fonts: font.fonts.clone(),
458        };
459        let cloned = FIGlet::from(data);
460        assert_eq!(
461            font.convert("Test").unwrap().as_str(),
462            cloned.convert("Test").unwrap().as_str()
463        );
464    }
465}