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