Support multiple strings in a token

This commit is contained in:
emilis 2023-01-17 12:12:37 +00:00
parent 7890424ea9
commit 80425f9aff
1 changed files with 80 additions and 50 deletions

View File

@ -8,16 +8,37 @@ pub enum Token {
CharPad(Box<Token>, char, usize), CharPad(Box<Token>, char, usize),
Fg(Box<Token>, Color), Fg(Box<Token>, Color),
Bg(Box<Token>, Color), Bg(Box<Token>, Color),
String(String), String(Box<Token>, String),
End,
} }
impl From<String> for Token { impl From<String> for Token {
fn from(value: String) -> Self { fn from(value: String) -> Self {
Token::String(value) Self::text(value)
}
}
impl From<&str> for Token {
fn from(value: &str) -> Self {
Self::text(value)
} }
} }
impl Token { impl Token {
pub fn text<T>(t: T) -> Self
where
T: Into<String>,
{
Self::End.string(t)
}
pub fn string<S>(self, s: S) -> Self
where
S: Into<String>,
{
Token::String(Box::new(self), s.into())
}
pub fn centered(self) -> Self { pub fn centered(self) -> Self {
Self::Centered(Box::new(self)) Self::Centered(Box::new(self))
} }
@ -31,7 +52,17 @@ impl Token {
} }
pub fn str_len(&self) -> usize { pub fn str_len(&self) -> usize {
self.walk_to_end().len() match self {
Token::String(t, s) => s.len() + t.str_len(),
Token::Fg(t, _) => t.str_len(),
Token::Bg(t, _) => t.str_len(),
Token::Centered(t) => t.str_len(),
Token::Limited(t, lim) => (*lim).min(t.str_len()),
Token::CharPad(t, _, pad_to) => {
(*pad_to).max(t.str_len())
}
Token::End => 0,
}
} }
pub fn pad_char(self, c: char, pad_to: usize) -> Self { pub fn pad_char(self, c: char, pad_to: usize) -> Self {
@ -46,38 +77,25 @@ impl Token {
Self::Bg(Box::new(self), c) Self::Bg(Box::new(self), c)
} }
fn walk_to_end(&self) -> &String {
match self {
Token::String(s) => s,
Token::Fg(t, _) => t.walk_to_end(),
Token::Bg(t, _) => t.walk_to_end(),
Token::Centered(t) => t.walk_to_end(),
Token::Limited(t, _) => t.walk_to_end(),
Token::CharPad(t, _, _) => t.walk_to_end(),
}
}
fn with_width(self, width: usize) -> Vec<Component> { fn with_width(self, width: usize) -> Vec<Component> {
match self { match self {
Token::String(s) => vec![Component::String(s)], Token::String(t, s) => vec![Component::String(s)]
Token::Centered(cnt) => cnt
.with_width(width)
.into_iter() .into_iter()
.map(|comp| { .chain(t.with_width(width))
if let Component::String(s) = comp {
let str_len = s.len();
let x = if str_len > width {
0
} else {
(width - str_len) / 2
};
vec![Component::X(x), Component::String(s)]
} else {
vec![comp]
}
})
.flatten()
.collect(), .collect(),
Token::Centered(cnt) => {
let mut str_len = cnt.str_len();
let components = if str_len > width {
str_len = width;
cnt.limited(width).with_width(width)
} else {
cnt.with_width(width)
};
vec![Component::X((width - str_len) / 2)]
.into_iter()
.chain(components)
.collect()
}
Token::Limited(s, lim) => s Token::Limited(s, lim) => s
.with_width(width) .with_width(width)
.into_iter() .into_iter()
@ -116,6 +134,7 @@ impl Token {
.into_iter() .into_iter()
.chain(t.with_width(width)) .chain(t.with_width(width))
.collect(), .collect(),
Token::End => vec![],
} }
} }
} }
@ -130,29 +149,25 @@ mod tests {
vec![ vec![
( (
"string -> string", "string -> string",
Token::String("hello".into()), Token::text("hello"),
vec![Component::String("hello".into())], vec![Component::String("hello".into())],
), ),
( (
"string -> limited", "string -> limited",
Token::String( Token::text("This string is too long for this!")
"This string is too long for this!".into(), .limited(4),
)
.limited(4),
vec![Component::String("This".into())], vec![Component::String("This".into())],
), ),
( (
"string -> limit -> pad_to", "string -> limit -> pad_to",
Token::String( Token::text("This string is too long, but some")
"This string is too long, but some".into(), .limited(10)
) .padded(15),
.limited(10)
.padded(15),
vec![Component::String("This strin ".into())], vec![Component::String("This strin ".into())],
), ),
( (
"center limited string", "center limited string",
Token::String("Ahhh this won't go far".into()) Token::text("Ahhh this won't go far")
.limited(10) .limited(10)
.centered(), .centered(),
vec![ vec![
@ -162,9 +177,7 @@ mod tests {
), ),
( (
"padded string with underscores is centered", "padded string with underscores is centered",
Token::String("It was...".into()) Token::text("It was...").pad_char('_', 15).centered(),
.pad_char('_', 15)
.centered(),
vec![ vec![
Component::X((WIDTH - 15) / 2), Component::X((WIDTH - 15) / 2),
Component::String("It was...______".into()), Component::String("It was...______".into()),
@ -172,7 +185,7 @@ mod tests {
), ),
( (
"prefixes color", "prefixes color",
Token::String("this is red".into()).fg(Color::RED), Token::text("this is red").fg(Color::RED),
vec![ vec![
Component::Fg(Color::RED), Component::Fg(Color::RED),
Component::String("this is red".into()), Component::String("this is red".into()),
@ -180,9 +193,7 @@ mod tests {
), ),
( (
"color at beginning of line", "color at beginning of line",
Token::String("colored".into()) Token::text("colored").centered().bg(Color::RED),
.centered()
.bg(Color::RED),
vec![ vec![
Component::Bg(Color::RED), Component::Bg(Color::RED),
Component::X((WIDTH - 7) / 2), Component::X((WIDTH - 7) / 2),
@ -191,7 +202,7 @@ mod tests {
), ),
( (
"color isnt part of limit", "color isnt part of limit",
Token::String("ten chars!".into()) Token::text("ten chars!")
.bg(Color::RED) .bg(Color::RED)
.fg(Color::GREEN) .fg(Color::GREEN)
.bg(Color::BLUE) .bg(Color::BLUE)
@ -205,6 +216,25 @@ mod tests {
Component::String("ten chars!".into()), Component::String("ten chars!".into()),
], ],
), ),
(
"multicolor string centered",
Token::text("end")
.fg(Color::RED)
.string("mid")
.fg(Color::BLUE)
.string("start")
.fg(Color::GREEN)
.centered(),
vec![
Component::X(0),
Component::Fg(Color::GREEN),
Component::String("start".into()),
Component::Fg(Color::BLUE),
Component::String("mid".into()),
Component::Fg(Color::RED),
Component::String("end".into()),
],
),
] ]
.into_iter() .into_iter()
.for_each(|test| { .for_each(|test| {