1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
use std::fs::read_to_string;
use std::io;
use std::path::Path;
const CHAR_OFFSET: usize = 32;
#[derive(Debug, Clone)]
pub struct Figlet {
height: usize,
chars: Vec<Char>,
}
#[derive(Debug, Clone)]
struct Char {
text: Vec<String>,
}
impl Figlet {
pub fn convert(&self, s: &str) -> String {
let mut output: Vec<String> = (0..self.height).map(|_| String::new()).collect();
let chars: Vec<_> = s
.chars()
.filter_map(|c| self.chars.get(c as usize - CHAR_OFFSET))
.collect();
for (i, row) in output.iter_mut().enumerate() {
let line: String = chars
.iter()
.filter_map(|c| c.text.get(i).map(|s| s.as_str()))
.collect();
row.push_str(&line);
}
output.join("\n")
}
pub fn from_file(path: impl AsRef<Path>) -> Result<Self, io::Error> {
let contents = read_to_string(path)?;
parse(contents.lines()).ok_or_else(|| {
io::Error::new(
io::ErrorKind::Other,
"file exists but appears to be invalid",
)
})
}
fn from_default() -> Self {
let contents = include_str!("univers.flf");
parse(contents.lines()).expect("embedded font should be parsable")
}
pub fn height(&self) -> usize {
self.height
}
}
impl Default for Figlet {
fn default() -> Self {
Figlet::from_default()
}
}
fn parse<'a>(mut iter: impl Iterator<Item = &'a str>) -> Option<Figlet> {
let header: Vec<_> = iter.next()?.split(' ').collect();
let height: usize = header.get(1)?.parse().ok()?;
let hard_blank = header.get(0)?.chars().last()?;
let comments: usize = header.get(5)?.parse().ok()?;
let mut chars: Vec<Char> = Vec::new();
let mut current: Vec<String> = Vec::with_capacity(height);
for line in iter.skip(comments) {
let mut len = line.len();
for c in line.as_bytes().iter().rev() {
if *c == b'@' {
len -= 1;
} else {
break;
}
}
if len == line.len() {
break;
}
let slice = &line[0..len];
current.push(slice.replace(hard_blank, " "));
if current.len() == height {
let width = current.iter().fold(0, |max, s| std::cmp::max(max, s.len()));
if current.len() == height {
for line in current.iter_mut() {
while line.len() < width {
line.push(' ');
}
}
chars.push(Char {
text: std::mem::replace(&mut current, Vec::with_capacity(height)),
})
}
}
}
Some(Figlet { height, chars })
}