use tty_interface::Style;
pub type Segment = Vec<Text>;
pub type DrawerContents = Vec<Segment>;
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct Text(String, Option<Style>);
impl Text {
pub fn new(content: String) -> Self {
Self(content, None)
}
pub fn new_styled(content: String, style: Style) -> Self {
Self(content, Some(style))
}
pub fn content(&self) -> &str {
&self.0
}
pub fn style(&self) -> Option<&Style> {
self.1.as_ref()
}
pub fn as_segment(self) -> Segment {
vec![self]
}
}
pub(crate) fn set_segment_style(segment: &mut Segment, style: Style) {
let segment_length = get_segment_length(segment);
set_segment_subset_style(segment, 0, segment_length, style);
}
pub(crate) fn set_segment_subset_style(
segment: &mut Segment,
start: usize,
end: usize,
style: Style,
) {
let mut index = 0;
let mut i = 0;
loop {
if i == segment.len() {
break;
}
let text = &segment[i];
let start_intersects = start > index && start < index + text.content().len();
let end_intersects = end > index && end < index + text.content().len();
if start_intersects {
let (first, second) = split_text(text, start - index);
segment[i] = first;
segment.insert(i + 1, second);
} else if end_intersects {
let (first, second) = split_text(text, end - index);
segment[i] = first;
segment.insert(i + 1, second);
}
index += segment[i].content().len();
i += 1;
}
index = 0;
for text in segment {
if index >= start && index < end {
text.1 = Some(style);
}
index += text.content().len();
}
}
pub(crate) fn get_segment_length(segment: &Segment) -> usize {
segment.iter().map(|text| text.content().len()).sum()
}
fn split_text(text: &Text, index: usize) -> (Text, Text) {
let (prefix, suffix) = text.0.split_at(index);
let first = Text(prefix.to_string(), text.1);
let second = Text(suffix.to_string(), text.1);
(first, second)
}
#[cfg(test)]
mod tests {
use tty_interface::Color;
use crate::text::Text;
use super::set_segment_subset_style;
macro_rules! text {
($content: expr) => {
Text::new($content.to_string())
};
}
macro_rules! text_styled {
($content: expr, $color: expr) => {
Text::new_styled($content.to_string(), $color.as_style())
};
}
#[test]
fn test_set_segment_style_entirely() {
let mut segment = vec![
text!("TEST1"),
text_styled!("TEST2", Color::Red),
text_styled!("TEST3", Color::Blue),
text!("TEST4"),
];
set_segment_subset_style(&mut segment, 0, 20, Color::Green.as_style());
assert_eq!(
vec![
text_styled!("TEST1", Color::Green),
text_styled!("TEST2", Color::Green),
text_styled!("TEST3", Color::Green),
text_styled!("TEST4", Color::Green),
],
segment
);
}
#[test]
fn test_set_segment_style_neatly() {
let mut segment = vec![
text!("TEST1"),
text_styled!("TEST2", Color::Red),
text_styled!("TEST3", Color::Blue),
text!("TEST4"),
];
set_segment_subset_style(&mut segment, 5, 15, Color::Green.as_style());
assert_eq!(
vec![
text!("TEST1"),
text_styled!("TEST2", Color::Green),
text_styled!("TEST3", Color::Green),
text!("TEST4"),
],
segment
);
}
#[test]
fn test_set_segment_style_split() {
let mut segment = vec![
text!("TEST1"),
text_styled!("TEST2", Color::Red),
text_styled!("TEST3", Color::Blue),
text!("TEST4"),
];
set_segment_subset_style(&mut segment, 3, 7, Color::Green.as_style());
set_segment_subset_style(&mut segment, 11, 14, Color::Magenta.as_style());
assert_eq!(
vec![
text!("TES"),
text_styled!("T1", Color::Green),
text_styled!("TE", Color::Green),
text_styled!("ST2", Color::Red),
text_styled!("T", Color::Blue),
text_styled!("EST", Color::Magenta),
text_styled!("3", Color::Blue),
text!("TEST4"),
],
segment
);
}
}