Skip to main content

tui_markup/generator/
mod.rs

1//! Generator generates final output for showing.
2
3use std::fmt::{Debug, Display};
4
5use crate::{Error, error::LocatedError, parser::ItemG};
6
7pub mod helper;
8mod tag;
9
10#[cfg(feature = "ansi")]
11pub mod ansi;
12#[cfg(feature = "ansi")]
13pub use self::ansi::ANSIStringsGenerator;
14
15#[cfg(feature = "ratatui")]
16pub mod ratatui;
17#[cfg(feature = "ratatui")]
18pub use self::ratatui::RatatuiTextGenerator;
19
20#[cfg(feature = "crossterm")]
21pub mod crossterm;
22// TODO: termion generator
23pub use tag::{Tag, TagConvertor, TagG};
24
25#[cfg(feature = "crossterm")]
26pub use self::crossterm::CrosstermCommandsGenerator;
27
28/// Generator generates final output to show tui markup in some backend.
29///
30/// ## How to add support for new backend
31///
32/// Some concepts:
33///
34/// - Markup text/Source: the text you write in tui markup language.
35/// - [Parser][crate::parser::parse]: parse markup text into a series of
36///   [Item][crate::parser::Item], which usually be called as AST.
37/// - [Tag Convertor][TagConvertor]: Convert raw tag string like `green`, `bg:66ccff`, `mod:b` into
38///   [Tag].
39/// - [Generator]: generator final output from `Item<Tag>`.
40///
41/// So the whole pipeline is:
42///
43/// ```none
44/// Source --- Parser --> Item --- Tag Convertor --> Item<Tag> --- Generator --> Output --> Show it in some backend
45/// ```
46///
47/// The source, parser, Item, Tag, is already defined, so just write a [Tag Convertor][TagConvertor]
48/// and a [Generator], a new backend will be supported.
49///
50/// ## Generic implementation using flatten
51///
52/// Your tag convertor will parse color/modifiers string to some `Color`/`Modifier` type, and will
53/// support custom tag by a `Style` type, which can be converted from `Color` and `Modifier` too.
54///
55/// In this case, a [Tag] of this convertor can be convert to the `Style` type easily.
56///
57/// If this `Style` can be patched by other, then you can use the [`flatten`][helper::flatten]
58/// help method to do almost all the convert staff from AST to your final result.
59///
60/// Read document of [`flatten`][helper::flatten] to learn more, or just checkout a builtin
61/// implementation.
62pub trait Generator<'a> {
63    /// Tag convertor type.
64    type Convertor: TagConvertor<'a>;
65
66    /// Output type.
67    type Output;
68
69    /// Error type.
70    ///
71    /// If the generator can't fall, please use
72    /// [`GeneratorInfallible`][helper::GeneratorInfallible].
73    type Err: LocatedError + Display + Debug + Into<Error<'a, Self::Err>>;
74
75    /// Get the tag convertor.
76    fn convertor(&mut self) -> &mut Self::Convertor;
77
78    /// Generates final output from IR, which is output result of the Convertor.
79    ///
80    /// ## Errors
81    ///
82    /// When the generator can't process the IR. This should be documented details.
83    fn generate(&mut self, markup: Vec<Vec<ItemG<'a, Self>>>) -> Result<Self::Output, Self::Err>;
84}
85
86impl<'a, G: Generator<'a>> Generator<'a> for &mut G {
87    type Convertor = G::Convertor;
88    type Err = G::Err;
89    type Output = G::Output;
90
91    fn convertor(&mut self) -> &mut Self::Convertor {
92        <G as Generator<'a>>::convertor(self)
93    }
94
95    fn generate(&mut self, markup: Vec<Vec<ItemG<'a, G>>>) -> Result<Self::Output, Self::Err> {
96        <G as Generator<'a>>::generate(self, markup)
97    }
98}