fixes and testing for the instructions -> set of components converter

This commit is contained in:
emilis 2023-01-18 12:04:18 +00:00
parent 4a3c5ac492
commit ec6998ff30
2 changed files with 190 additions and 12 deletions

View File

@ -2,15 +2,39 @@ use crate::{
theme::{Color, ColorSet}, theme::{Color, ColorSet},
token::Token, token::Token,
}; };
use std::iter::Extend;
#[derive(Eq, Debug, Clone)] #[derive(Eq, Debug, Clone)]
pub enum Component { pub enum Component {
NextLine,
X(usize), X(usize),
String(String), String(String),
Fg(Color), Fg(Color),
Bg(Color), Bg(Color),
} }
impl Component {
fn make_for_line(
comp: Vec<Component>,
line: usize,
offset: usize,
) -> String {
comp.into_iter()
.map(|c| match c {
Component::X(x) => termion::cursor::Goto(
(x + offset) as u16,
line as u16,
)
.into(),
Component::String(s) => s,
Component::Fg(c) => c.fg(),
Component::Bg(c) => c.bg(),
Component::NextLine => "\r\n".into(),
})
.collect()
}
}
impl PartialEq for Component { impl PartialEq for Component {
fn eq(&self, other: &Self) -> bool { fn eq(&self, other: &Self) -> bool {
match self { match self {
@ -30,6 +54,10 @@ impl PartialEq for Component {
Self::Bg(other_c) => c == other_c, Self::Bg(other_c) => c == other_c,
_ => false, _ => false,
}, },
Self::NextLine => match other {
Self::NextLine => true,
_ => false,
},
} }
} }
} }
@ -86,7 +114,7 @@ impl Widget {
} }
fn get_line(&self, line: usize) -> Option<&Token> { fn get_line(&self, line: usize) -> Option<&Token> {
self.per_line.get(line - 1) self.per_line.get(line)
} }
} }
@ -131,11 +159,62 @@ impl PartialEq for Instruction {
} }
impl Instruction { impl Instruction {
pub fn make( pub fn into_components(
self, self,
(term_width, term_height): (u16, u16), line_width: usize,
) -> String { height_left: usize,
todo!() ) -> Vec<Component> {
if height_left == 0 {
return vec![];
}
match self {
Instruction::FixedHeight(next, lines, wdg) => (0..lines)
.map(|line| {
let mut offset = 0;
(&wdg)
.into_iter()
.map(|w| {
let (width, token) = (
w.want_width.abs_size(line_width),
w.get_line(line).map(|t| t.clone()),
);
let mut result = match token {
Some(tok) => tok
.with_width(width)
.into_iter()
.map(|comp| {
if let Component::X(x) = comp
{
Component::X(offset + x)
} else {
comp
}
})
.collect(),
None => Vec::new(),
};
offset += width;
if offset < line_width {
result.push(Component::X(offset));
}
result
})
.flatten()
.chain(vec![Component::NextLine])
.collect::<Vec<Component>>()
})
.flatten()
.chain(
next.into_components(
line_width,
height_left - lines,
),
)
.collect(),
Instruction::End => (0..height_left)
.map(|_| Component::NextLine)
.collect(),
}
} }
pub fn start() -> Self { pub fn start() -> Self {
@ -251,13 +330,11 @@ impl Plan {
mod tests { mod tests {
use super::*; use super::*;
#[test] fn test_widgets() -> (Widget, Widget, Widget) {
fn test_plan_to_instructions() { (
const HEIGHT: usize = 40;
let (widget_1, widget_2, widget_3) = (
Widget::new( Widget::new(
SectionWidth::Third, SectionWidth::Third,
vec![Token::text("hello")], vec![Token::text("hello").centered()],
), ),
Widget::new( Widget::new(
SectionWidth::Third, SectionWidth::Third,
@ -269,7 +346,108 @@ mod tests {
SectionWidth::Third, SectionWidth::Third,
vec![Token::text("hello").limited(16).padded(20)], vec![Token::text("hello").limited(16).padded(20)],
), ),
); )
}
#[test]
fn test_instructions_to_components() {
const WIDTH: usize = 120;
const HEIGHT: usize = 40;
let (w1, w2, w3) = test_widgets();
vec![
(
"all the widgets, 30 lines",
Instruction::start().fixed(
30,
vec![w1.clone(), w2.clone(), w3.clone()],
),
w1.clone()
.get_line(0)
.unwrap()
.clone()
.with_width(WIDTH / 3)
.into_iter()
.chain(vec![Component::X(WIDTH / 3)])
.chain(
w2.clone()
.get_line(0)
.unwrap()
.clone()
.with_width(WIDTH / 3),
)
.chain(vec![Component::X((WIDTH / 3) * 2)])
.chain(
w3.clone()
.get_line(0)
.unwrap()
.clone()
.with_width(WIDTH / 3),
)
.chain(vec![Component::NextLine])
.chain(
(1..30)
.map(|_| {
vec![
Component::X(WIDTH / 3),
Component::X((WIDTH / 3) * 2),
Component::NextLine,
]
})
.flatten(),
)
.chain(
(0..(HEIGHT - 30))
.map(|_| Component::NextLine),
)
.collect::<Vec<Component>>(),
),
(
"Single widget, single 10 lines section",
Instruction::start().fixed(10, vec![w1.clone()]),
w1.clone()
.get_line(0)
.unwrap()
.clone()
.with_width(WIDTH / 3)
.into_iter()
.chain(
(0..10)
.map(|_| {
vec![
Component::X(WIDTH / 3),
Component::NextLine,
]
})
.flatten(),
)
.chain(
(0..(HEIGHT - 10))
.map(|_| Component::NextLine),
)
.collect(),
),
]
.into_iter()
.for_each(|(name, instruction, expected)| {
let actual = instruction.into_components(WIDTH, HEIGHT);
assert!(
expected == actual,
"<{}>:
expected({}):\n{:#?}
actual({}):\n{:#?}",
name,
expected.len(),
expected,
actual.len(),
actual,
);
});
}
#[test]
fn test_plan_to_instructions() {
const HEIGHT: usize = 40;
let (widget_1, widget_2, widget_3) = test_widgets();
vec![ vec![
("end -> end", Plan::start(), Instruction::End), ("end -> end", Plan::start(), Instruction::End),

View File

@ -76,7 +76,7 @@ impl Token {
Self::Bg(Box::new(self), c) Self::Bg(Box::new(self), c)
} }
fn with_width(self, width: usize) -> Vec<Component> { pub fn with_width(self, width: usize) -> Vec<Component> {
match self { match self {
Token::String(t, s) => vec![Component::String(s)] Token::String(t, s) => vec![Component::String(s)]
.into_iter() .into_iter()