1use std::io::{self, Write};
4use std::time::Instant;
5
6use crate::console::{ConsoleOptions, DynRenderable, Renderable};
7
8pub struct LiveWriter {
10 buffer: Vec<u8>,
11}
12
13impl LiveWriter {
14 pub fn new() -> Self {
15 Self { buffer: Vec::new() }
16 }
17
18 pub fn capture(&self) -> &[u8] {
19 &self.buffer
20 }
21
22 pub fn clear(&mut self) {
23 self.buffer.clear();
24 }
25}
26
27impl Write for LiveWriter {
28 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
29 self.buffer.extend_from_slice(buf);
30 Ok(buf.len())
31 }
32
33 fn flush(&mut self) -> io::Result<()> {
34 Ok(())
35 }
36}
37
38pub struct Live {
40 renderable: Option<DynRenderable>,
41 screen: bool,
42 auto_refresh: bool,
43 refresh_per_second: f64,
44 transient: bool,
45 started: Option<Instant>,
46 previous_line_count: usize,
47 redirect_stdout: bool,
48 redirect_stderr: bool,
49 writers: Vec<LiveWriter>,
50}
51
52impl std::fmt::Debug for Live {
53 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
54 f.debug_struct("Live")
55 .field("screen", &self.screen)
56 .field("started", &self.started)
57 .finish()
58 }
59}
60
61impl Live {
62 pub fn new(renderable: impl Renderable + Send + Sync + 'static) -> Self {
63 Self {
64 renderable: Some(DynRenderable::new(renderable)),
65 screen: false,
66 auto_refresh: true,
67 refresh_per_second: 4.0,
68 transient: false,
69 started: None,
70 previous_line_count: 0,
71 redirect_stdout: true,
72 redirect_stderr: true,
73 writers: Vec::new(),
74 }
75 }
76
77 pub fn screen(mut self) -> Self { self.screen = true; self }
78 pub fn no_auto_refresh(mut self) -> Self { self.auto_refresh = false; self }
79 pub fn refresh_per_second(mut self, rate: f64) -> Self { self.refresh_per_second = rate; self }
80 pub fn transient(mut self) -> Self { self.transient = true; self }
81 pub fn redirect_stdout(mut self, redirect: bool) -> Self { self.redirect_stdout = redirect; self }
82 pub fn redirect_stderr(mut self, redirect: bool) -> Self { self.redirect_stderr = redirect; self }
83
84 pub fn add_writer(&mut self, writer: LiveWriter) { self.writers.push(writer); }
86
87 pub fn create_writer() -> LiveWriter {
89 LiveWriter::new()
90 }
91
92 pub fn start(&mut self) -> io::Result<()> {
93 self.started = Some(Instant::now());
94 if self.screen {
95 write!(io::stdout(), "\x1b[?1049h")?;
96 }
97 write!(io::stdout(), "\x1b[?25l")?;
98 self.refresh()
99 }
100
101 pub fn stop(&mut self) -> io::Result<()> {
102 if self.transient {
103 for _ in 0..self.previous_line_count {
104 write!(io::stdout(), "\x1b[1A\x1b[2K")?;
105 }
106 }
107 if self.screen {
108 write!(io::stdout(), "\x1b[?1049l")?;
109 }
110 write!(io::stdout(), "\x1b[?25h")?;
111 io::stdout().flush()?;
112 self.started = None;
113 Ok(())
114 }
115
116 pub fn update(&mut self, renderable: impl Renderable + Send + Sync + 'static) -> io::Result<()> {
117 self.renderable = Some(DynRenderable::new(renderable));
118 self.refresh()
119 }
120
121 pub fn refresh(&mut self) -> io::Result<()> {
122 if let Some(ref renderable) = self.renderable {
123 let opts = ConsoleOptions::default();
124 let result = renderable.render(&opts);
125
126 if self.previous_line_count > 0 {
127 write!(io::stdout(), "\x1b[{}F", self.previous_line_count)?;
128 }
129
130 let ansi = result.to_ansi();
131 let line_count = ansi.lines().count();
132
133 write!(io::stdout(), "{ansi}")?;
134 if line_count < self.previous_line_count {
135 for _ in line_count..self.previous_line_count {
136 write!(io::stdout(), "\x1b[2K\n")?;
137 }
138 }
139
140 self.previous_line_count = line_count;
141
142 for writer in &self.writers {
144 let captured = String::from_utf8_lossy(writer.capture());
145 if !captured.is_empty() {
146 write!(io::stdout(), "{}", captured)?;
147 }
148 }
149
150 io::stdout().flush()?;
151 }
152 Ok(())
153 }
154}
155
156impl Drop for Live {
157 fn drop(&mut self) {
158 let _ = self.stop();
159 }
160}