term_transcript/svg/
data.rs

1//! Data provided to Handlebars templates.
2
3use std::collections::HashMap;
4
5use serde::Serialize;
6
7use crate::{svg::TemplateOptions, write::SvgLine, UserInput};
8
9/// Root data structure sent to the Handlebars template.
10///
11/// # Examples
12///
13/// Here's example of JSON serialization of this type:
14///
15/// ```
16/// # use term_transcript::{svg::{TemplateOptions, NamedPalette}, Transcript, UserInput};
17/// let mut transcript = Transcript::new();
18/// let input = UserInput::command("rainbow");
19/// transcript.add_interaction(input, "Hello, \u{1b}[32mworld\u{1b}[0m!");
20/// let template_options = TemplateOptions {
21///     palette: NamedPalette::Dracula.into(),
22///     font_family: "Consolas, Menlo, monospace".to_owned(),
23///     ..TemplateOptions::default()
24/// };
25/// let data = template_options.render_data(&transcript).unwrap();
26///
27/// let expected_json = serde_json::json!({
28///     "creator": {
29///         "name": "term-transcript",
30///         "version": "0.4.0",
31///         "repo": "https://github.com/slowli/term-transcript",
32///     },
33///     "width": 720,
34///     "palette": {
35///         "colors": {
36///             "black": "#282936",
37///             "red": "#ea51b2",
38///             "green": "#ebff87",
39///             "yellow": "#00f769",
40///             "blue": "#62d6e8",
41///             "magenta": "#b45bcf",
42///             "cyan": "#a1efe4",
43///             "white": "#e9e9f4",
44///         },
45///         "intense_colors": {
46///             "black": "#626483",
47///             "red": "#b45bcf",
48///             "green": "#3a3c4e",
49///             "yellow": "#4d4f68",
50///             "blue": "#62d6e8",
51///             "magenta": "#f1f2f8",
52///             "cyan": "#00f769",
53///             "white": "#f7f7fb",
54///         },
55///     },
56///     "font_family": "Consolas, Menlo, monospace",
57///     "window_frame": false,
58///     "wrap": {
59///         "hard_break_at": 80,
60///     },
61///     "line_numbers": null,
62///     "has_failures": false,
63///     "interactions": [{
64///         "input": {
65///             "text": "rainbow",
66///             "prompt": "$",
67///             "hidden": false,
68///         },
69///         "output_html": "Hello, <span class=\"fg2\">world</span>!",
70/// #       "output_svg": [{
71/// #           "background": null,
72/// #           "foreground": "Hello,\u{a0}<tspan class=\"fg2\">world</tspan>!",
73/// #       }],
74/// #       // ^ Implementation detail for now
75///         "failure": false,
76///         "exit_status": null,
77///     }]
78/// });
79/// assert_eq!(serde_json::to_value(data).unwrap(), expected_json);
80/// ```
81#[derive(Debug, Serialize)]
82#[non_exhaustive]
83pub struct HandlebarsData<'r> {
84    /// Information about the rendering software.
85    pub creator: CreatorData,
86    /// Template options used for rendering. These options are flattened into the parent
87    /// during serialization.
88    #[serde(flatten)]
89    pub options: &'r TemplateOptions,
90    /// Recorded terminal interactions.
91    pub interactions: Vec<SerializedInteraction<'r>>,
92    /// Has any of terminal interactions failed?
93    pub has_failures: bool,
94}
95
96/// Information about software used for rendering (i.e., this crate).
97///
98/// It can make sense to include this info as a comment in the rendered template
99/// for debugging purposes.
100#[derive(Debug, Serialize)]
101#[non_exhaustive]
102pub struct CreatorData {
103    /// Name of software rendering the template.
104    pub name: &'static str,
105    /// Version of the rendering software.
106    pub version: &'static str,
107    /// Link to the git repository with the rendering software.
108    pub repo: &'static str,
109}
110
111impl Default for CreatorData {
112    fn default() -> Self {
113        Self {
114            name: env!("CARGO_PKG_NAME"),
115            version: env!("CARGO_PKG_VERSION"),
116            repo: env!("CARGO_PKG_REPOSITORY"),
117        }
118    }
119}
120
121/// Serializable version of [`Interaction`](crate::Interaction).
122///
123/// # HTML output
124///
125/// An interaction contains rendered HTML for the output with styles applied
126/// to the relevant segments as `<span>`s. The styles are signalled using `class`es
127/// and inline `style`s:
128///
129/// - `fg0`–`fg15` classes specify the foreground color being
130///   0th–15th [base terminal color][colors]. `fg0`–`fg7` are ordinary colors,
131///   and `fg8`–`fg15` are intense variations.
132/// - Likewise, `bg0`–`bg15` classes specify the background color as one of the base terminal
133///   colors.
134/// - Remaining indexed colors and 24-bit colors have a definite value, and thus are signalled
135///   via an inline `style` (e.g., `color: #c0ffee` or `background: #c0ffee`).
136/// - `bold`, `italic`, `underline`, `dimmed` classes correspond to the corresponding text styles.
137/// - [Hard breaks], if they are enabled, are represented by `<b class="hard-br"><br/></b>`.
138///
139/// The rendered HTML is assumed to be included into a container that preserves whitespace,
140/// i.e., has [`white-space`] CSS property set to `pre`. An example of such container is `<pre>`.
141///
142/// [colors]: https://en.wikipedia.org/wiki/ANSI_escape_code#3-bit_and_4-bit
143/// [`white-space`]: https://developer.mozilla.org/en-US/docs/Web/CSS/white-space
144/// [Hard breaks]: crate::svg::WrapOptions::HardBreakAt
145#[derive(Debug, Serialize)]
146#[non_exhaustive]
147pub struct SerializedInteraction<'a> {
148    /// User's input.
149    pub input: &'a UserInput,
150    /// Terminal output in the [HTML format](#html-output).
151    pub output_html: String,
152    /// Terminal output in the SVG format.
153    pub(crate) output_svg: Vec<SvgLine>,
154    /// Exit status of the latest executed program, or `None` if it cannot be determined.
155    pub exit_status: Option<i32>,
156    /// Was execution unsuccessful judging by the [`ExitStatus`](crate::ExitStatus)?
157    pub failure: bool,
158}
159
160#[derive(Debug, Serialize)]
161pub(super) struct CompleteHandlebarsData<'r> {
162    #[serde(flatten)]
163    pub inner: HandlebarsData<'r>,
164    #[serde(rename = "const")]
165    pub constants: &'r HashMap<&'static str, u32>,
166}