use crate::console::Console;
use crate::segment::Segment;
use crate::terminal_theme::{TerminalTheme, SVG_EXPORT_THEME};
pub struct Recording {
pub(crate) segments: Vec<Segment>,
pub(crate) color_system: Option<crate::color::ColorSystem>,
pub(crate) no_color: bool,
pub(crate) width: usize,
}
impl Recording {
pub fn to_text(&self) -> String {
let mut out = String::new();
for seg in &self.segments {
if !seg.is_control() {
out.push_str(&seg.text);
}
}
out
}
pub fn to_html(&self) -> String {
self.to_html_with_theme(None, false)
}
pub fn to_html_with_theme(
&self,
terminal_theme: Option<&TerminalTheme>,
inline_styles: bool,
) -> String {
let mut tmp = build_tmp_console(self);
tmp.export_html(terminal_theme, false, inline_styles)
}
pub fn to_svg(&self, title: &str) -> String {
self.to_svg_with_theme(title, None)
}
pub fn to_svg_with_theme(&self, title: &str, terminal_theme: Option<&TerminalTheme>) -> String {
let theme = terminal_theme.unwrap_or(&SVG_EXPORT_THEME);
let mut tmp = build_tmp_console(self);
tmp.export_svg(title, Some(theme), false, None, 0.61)
}
}
impl Console {
pub fn scoped_record<F>(&mut self, f: F) -> Recording
where
F: FnOnce(&mut Console),
{
let prior_record = self.record;
let prior_record_buffer = std::mem::take(&mut self.record_buffer);
self.record = true;
f(self);
let captured_segments = std::mem::take(&mut self.record_buffer);
self.record = prior_record;
self.record_buffer = prior_record_buffer;
Recording {
segments: captured_segments,
color_system: self.color_system,
no_color: self.no_color,
width: self.width(),
}
}
}
fn build_tmp_console(rec: &Recording) -> Console {
use crate::console::ConsoleBuilder;
let mut tmp = ConsoleBuilder::default()
.width(rec.width)
.record(true)
.no_color(rec.no_color)
.build();
if let Some(cs) = rec.color_system {
tmp.color_system = Some(cs);
}
tmp.record_buffer = rec.segments.clone();
tmp
}
#[cfg(test)]
mod tests {
use super::*;
fn recording_console() -> Console {
Console::builder()
.width(80)
.no_color(true)
.markup(false)
.build()
}
#[test]
fn scoped_record_basic_text() {
let mut c = recording_console();
let rec = c.scoped_record(|c| {
c.print_text("hello recording");
});
assert!(
rec.to_text().contains("hello recording"),
"to_text should contain printed text"
);
}
#[test]
fn scoped_record_html_contains_text() {
let mut c = recording_console();
let rec = c.scoped_record(|c| {
c.print_text("html test");
});
let html = rec.to_html();
assert!(html.contains("html test"), "to_html should contain text");
assert!(
html.contains("<!DOCTYPE html>"),
"to_html should be a full HTML document"
);
}
#[test]
fn scoped_record_svg_contains_text() {
let mut c = Console::builder()
.width(40)
.no_color(true)
.markup(false)
.build();
let rec = c.scoped_record(|c| {
c.print_text("svg content");
});
let svg = rec.to_svg("Title");
assert!(svg.contains("<svg"), "to_svg should produce SVG");
assert!(svg.contains("svg content"), "SVG should contain text");
}
#[test]
fn scoped_record_restores_record_false() {
let mut c = recording_console();
assert!(!c.record, "precondition: record should be false");
let _ = c.scoped_record(|_| {});
assert!(!c.record, "record should be restored to false");
}
#[test]
fn scoped_record_restores_record_true() {
let mut c = Console::builder()
.width(80)
.no_color(true)
.markup(false)
.record(true)
.build();
assert!(c.record, "precondition: record should be true");
let _ = c.scoped_record(|_| {});
assert!(c.record, "record should be restored to true");
}
#[test]
fn scoped_record_does_not_mix_with_outer_buffer() {
let mut c = Console::builder()
.width(80)
.no_color(true)
.markup(false)
.record(true)
.build();
c.print_text("before");
let _ = c.scoped_record(|c| {
c.print_text("inside");
});
c.print_text("after");
let exported = c.export_text(false, false);
assert!(exported.contains("before"), "outer buffer: before");
assert!(exported.contains("after"), "outer buffer: after");
assert!(
!exported.contains("inside"),
"outer buffer should NOT have inside; got {:?}",
exported
);
}
}