tui_markup/generator/ratatui/
mod.rs

1//! Generator implementations for ratatui crate.
2
3mod span;
4mod tag;
5
6#[cfg(test)]
7mod test;
8
9use ratatui::{
10    style::Style,
11    text::{Line, Text},
12};
13
14use crate::{
15    generator::{
16        helper::{flatten, CustomTagParser, GeneratorInfallible, NoopCustomTagParser},
17        Generator,
18    },
19    parser::ItemG,
20};
21
22pub use tag::RatatuiTagConvertor;
23
24/// Generator for [ratatui crate][ratatui]'s [Text] type.
25///
26/// See [docs/ratatui-tags.ebnf] for supported tags.
27///
28/// ## Example
29///
30/// ```
31/// # use ratatui::prelude::*;
32/// use tui_markup::{compile, generator::RatatuiTextGenerator};
33///
34/// assert_eq!(
35///     compile::<RatatuiTextGenerator>("I have a <green green text>"),
36///     Ok(Text::from(vec![Line::from(vec![
37///         Span::raw("I have a "),
38///         Span::styled("green text", Style::default().fg(Color::Green)),
39///     ])])),
40/// );
41///
42/// assert_eq!(
43///     compile::<RatatuiTextGenerator>("I can set <bg:blue background>"),
44///     Ok(Text::from(vec![Line::from(vec![
45///         Span::raw("I can set "),
46///         Span::styled("background", Style::default().bg(Color::Blue)),
47///     ])])),
48/// );
49///
50/// assert_eq!(
51///     compile::<RatatuiTextGenerator>("I can add <b bold>, <d dim>, <i italic> modifiers"),
52///     Ok(Text::from(vec![Line::from(vec![
53///         Span::raw("I can add "),
54///         Span::styled("bold", Style::default().add_modifier(Modifier::BOLD)),
55///         Span::raw(", "),
56///         Span::styled("dim", Style::default().add_modifier(Modifier::DIM)),
57///         Span::raw(", "),
58///         Span::styled("italic", Style::default().add_modifier(Modifier::ITALIC)),
59///         Span::raw(" modifiers"),
60///     ])])),
61/// );
62///
63///
64/// assert_eq!(
65///     compile::<RatatuiTextGenerator>("I can <bg:blue combine <green them <b <i all>>>>"),
66///     Ok(Text::from(vec![Line::from(vec![
67///         Span::raw("I can "),
68///         Span::styled("combine ", Style::default().bg(Color::Blue)),
69///         Span::styled("them ", Style::default().bg(Color::Blue).fg(Color::Green)),
70///         Span::styled("all", Style::default()
71///             .bg(Color::Blue).fg(Color::Green).add_modifier(Modifier::BOLD | Modifier::ITALIC)),
72///     ])])),
73/// );
74///
75/// assert_eq!(
76///     compile::<RatatuiTextGenerator>("I can use <bg:66ccff custom color>"),
77///     Ok(Text::from(vec![Line::from(vec![
78///         Span::raw("I can use "),
79///         Span::styled("custom color", Style::default().bg(Color::Rgb(0x66, 0xcc, 0xff))),
80///     ])])),
81/// );
82///
83/// assert_eq!(
84///     compile::<RatatuiTextGenerator>("I can set <bg:blue,green,b,i many style> in one tag"),
85///     Ok(Text::from(vec![Line::from(vec![
86///         Span::raw("I can set "),
87///         Span::styled("many style", Style::default()
88///             .bg(Color::Blue).fg(Color::Green).add_modifier(Modifier::BOLD | Modifier::ITALIC)),
89///         Span::raw(" in one tag"),
90///     ])])),
91/// );
92/// ```
93///
94/// ### With custom tags
95///
96/// ```
97/// # use ratatui::prelude::*;
98/// use tui_markup::{compile_with, generator::RatatuiTextGenerator};
99///
100/// let gen =RatatuiTextGenerator::new(|tag: &str| match tag {
101///     "keyboard" => Some(Style::default().bg(Color::White).fg(Color::Green).add_modifier(Modifier::BOLD)),
102///     _ => None,
103/// });
104///
105/// assert_eq!(
106///     compile_with("Press <keyboard W> to move up", gen),
107///     Ok(Text::from(vec![Line::from(vec![
108///         Span::raw("Press "),
109///         Span::styled("W", Style::default().bg(Color::White).fg(Color::Green).add_modifier(Modifier::BOLD)),
110///         Span::raw(" to move up"),
111///     ])])),
112/// );
113/// ```
114///
115/// ### Show output
116///
117/// Use any widget of [ratatui] crate that supports it's [Text] type, for example: [`ratatui::widgets::Paragraph`].
118///
119/// [docs/ratatui-tags.ebnf]: https://github.com/7sDream/tui-markup/blob/master/docs/ratatui-tags.ebnf
120#[cfg_attr(docsrs, doc(cfg(feature = "ratatui")))]
121#[derive(Debug)]
122pub struct RatatuiTextGenerator<P = NoopCustomTagParser<Style>> {
123    convertor: RatatuiTagConvertor<P>,
124}
125
126impl<P> Default for RatatuiTextGenerator<P> {
127    fn default() -> Self {
128        Self {
129            convertor: RatatuiTagConvertor::<P>::default(),
130        }
131    }
132}
133
134impl<P> RatatuiTextGenerator<P> {
135    /// Create a new generator, with a custom tag parser.
136    pub fn new(p: P) -> Self {
137        RatatuiTextGenerator {
138            convertor: RatatuiTagConvertor::new(p),
139        }
140    }
141}
142
143impl<'a, P> Generator<'a> for RatatuiTextGenerator<P>
144where
145    P: CustomTagParser<Output = Style>,
146{
147    type Convertor = RatatuiTagConvertor<P>;
148
149    type Output = Text<'a>;
150
151    type Err = GeneratorInfallible;
152
153    fn convertor(&mut self) -> &mut Self::Convertor {
154        &mut self.convertor
155    }
156
157    fn generate(&mut self, items: Vec<Vec<ItemG<'a, Self>>>) -> Result<Self::Output, Self::Err> {
158        Ok(Text::from(
159            items
160                .into_iter()
161                .map(|line| Line::from(flatten(line)))
162                .collect::<Vec<_>>(),
163        ))
164    }
165}