mod tag;
#[cfg(test)]
mod test;
use tui::{
style::Style,
text::{Span, Spans, Text},
};
use super::{
helper::{unescape, CustomTagParser, GeneratorInfallible, NoopCustomTagParser},
Generator,
};
use crate::generator::{Tag, TagG};
use crate::parser::{Item, ItemG};
use tag::TuiTagParser;
#[cfg_attr(docsrs, doc(cfg(feature = "tui")))]
#[derive(Debug)]
pub struct TuiTextGenerator<P = NoopCustomTagParser<Style>> {
tag_parser: TuiTagParser<P>,
}
impl<P> Default for TuiTextGenerator<P> {
fn default() -> Self {
Self {
tag_parser: Default::default(),
}
}
}
impl<P> TuiTextGenerator<P> {
pub fn new(p: P) -> Self {
TuiTextGenerator {
tag_parser: TuiTagParser::new(p),
}
}
}
impl<'a, P> TuiTextGenerator<P>
where
P: CustomTagParser<Output = Style>,
{
fn tag_to_style(tag: TagG<'a, Self>) -> Style {
match tag {
Tag::Fg(color) => Style::default().fg(color),
Tag::Bg(color) => Style::default().bg(color),
Tag::Modifier(m) => Style::default().add_modifier(m),
Tag::Custom(style) => style,
}
}
fn patch_style(style: Option<Style>, tag: Vec<TagG<'a, Self>>) -> Style {
tag.into_iter()
.map(Self::tag_to_style)
.fold(style.unwrap_or_default(), Style::patch)
}
fn plain_text(&self, escaped: &'a str, style: Option<Style>) -> Vec<Span<'a>> {
unescape(escaped)
.map(|s| match style {
Some(style) => Span::styled(s, style),
None => Span::raw(s),
})
.collect()
}
fn element(
&mut self, tags: Vec<TagG<'a, Self>>, children: Vec<ItemG<'a, Self>>, style: Option<Style>,
) -> Vec<Span<'a>> {
self.items(children, Some(Self::patch_style(style, tags)))
}
fn item(&mut self, item: Item<'a, TagG<'a, Self>>, style: Option<Style>) -> Vec<Span<'a>> {
match item {
Item::PlainText(t) => self.plain_text(t.fragment(), style),
Item::Element(tags, children) => self.element(tags, children, style),
}
}
fn items(&mut self, items: Vec<ItemG<'a, Self>>, style: Option<Style>) -> Vec<Span<'a>> {
items
.into_iter()
.flat_map(|item| self.item(item, style).into_iter())
.collect()
}
}
impl<'a, P> Generator<'a> for TuiTextGenerator<P>
where
P: CustomTagParser<Output = Style>,
{
type Convertor = TuiTagParser<P>;
type Output = Text<'a>;
type Err = GeneratorInfallible;
fn convertor(&mut self) -> &mut Self::Convertor {
&mut self.tag_parser
}
fn generate(&mut self, items: Vec<Vec<Item<'a, TagG<'a, Self>>>>) -> Result<Self::Output, Self::Err> {
Ok(Text::from(
items
.into_iter()
.map(|line| Spans::from(self.items(line, None)))
.collect::<Vec<_>>(),
))
}
}