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}