1use std::io::{self, Write};
50use std::time::Instant;
51
52use crate::console::{ConsoleOptions, DynRenderable, Renderable};
53
54pub struct LiveWriter {
56 buffer: Vec<u8>,
57}
58
59impl LiveWriter {
60 pub fn new() -> Self {
62 Self { buffer: Vec::new() }
63 }
64
65 pub fn capture(&self) -> &[u8] {
67 &self.buffer
68 }
69
70 pub fn clear(&mut self) {
72 self.buffer.clear();
73 }
74}
75
76impl Write for LiveWriter {
77 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
78 self.buffer.extend_from_slice(buf);
79 Ok(buf.len())
80 }
81
82 fn flush(&mut self) -> io::Result<()> {
83 Ok(())
84 }
85}
86
87pub struct Live {
89 renderable: Option<DynRenderable>,
90 screen: bool,
91 auto_refresh: bool,
92 refresh_per_second: f64,
93 transient: bool,
94 started: Option<Instant>,
95 previous_line_count: usize,
96 redirect_stdout: bool,
97 redirect_stderr: bool,
98 writers: Vec<LiveWriter>,
99}
100
101impl std::fmt::Debug for Live {
102 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
103 f.debug_struct("Live")
104 .field("screen", &self.screen)
105 .field("started", &self.started)
106 .finish()
107 }
108}
109
110impl Live {
111 pub fn new(renderable: impl Renderable + Send + Sync + 'static) -> Self {
113 Self {
114 renderable: Some(DynRenderable::new(renderable)),
115 screen: false,
116 auto_refresh: true,
117 refresh_per_second: 4.0,
118 transient: false,
119 started: None,
120 previous_line_count: 0,
121 redirect_stdout: true,
122 redirect_stderr: true,
123 writers: Vec::new(),
124 }
125 }
126
127 pub fn screen(mut self) -> Self { self.screen = true; self }
129 pub fn no_auto_refresh(mut self) -> Self { self.auto_refresh = false; self }
131 pub fn refresh_per_second(mut self, rate: f64) -> Self { self.refresh_per_second = rate; self }
133 pub fn transient(mut self) -> Self { self.transient = true; self }
135 pub fn redirect_stdout(mut self, redirect: bool) -> Self { self.redirect_stdout = redirect; self }
137 pub fn redirect_stderr(mut self, redirect: bool) -> Self { self.redirect_stderr = redirect; self }
139
140 pub fn add_writer(&mut self, writer: LiveWriter) { self.writers.push(writer); }
142
143 pub fn create_writer() -> LiveWriter {
145 LiveWriter::new()
146 }
147
148 pub fn start(&mut self) -> io::Result<()> {
150 self.started = Some(Instant::now());
151 if self.screen {
152 write!(io::stdout(), "\x1b[?1049h")?;
153 }
154 write!(io::stdout(), "\x1b[?25l")?;
155 self.refresh()
156 }
157
158 pub fn stop(&mut self) -> io::Result<()> {
160 if self.transient {
161 for _ in 0..self.previous_line_count {
162 write!(io::stdout(), "\x1b[1A\x1b[2K")?;
163 }
164 }
165 if self.screen {
166 write!(io::stdout(), "\x1b[?1049l")?;
167 }
168 write!(io::stdout(), "\x1b[?25h")?;
169 io::stdout().flush()?;
170 self.started = None;
171 Ok(())
172 }
173
174 pub fn update(&mut self, renderable: impl Renderable + Send + Sync + 'static) -> io::Result<()> {
176 self.renderable = Some(DynRenderable::new(renderable));
177 self.refresh()
178 }
179
180 pub fn refresh(&mut self) -> io::Result<()> {
182 if let Some(ref renderable) = self.renderable {
183 let opts = ConsoleOptions::default();
184 let result = renderable.render(&opts);
185
186 if self.previous_line_count > 0 {
187 write!(io::stdout(), "\x1b[{}F", self.previous_line_count)?;
188 }
189
190 let ansi = result.to_ansi();
191 let line_count = ansi.lines().count();
192
193 write!(io::stdout(), "{ansi}")?;
194 if line_count < self.previous_line_count {
195 for _ in line_count..self.previous_line_count {
196 write!(io::stdout(), "\x1b[2K\n")?;
197 }
198 }
199
200 self.previous_line_count = line_count;
201
202 for writer in &self.writers {
204 let captured = String::from_utf8_lossy(writer.capture());
205 if !captured.is_empty() {
206 write!(io::stdout(), "{}", captured)?;
207 }
208 }
209
210 io::stdout().flush()?;
211 }
212 Ok(())
213 }
214}
215
216impl Drop for Live {
217 fn drop(&mut self) {
218 let _ = self.stop();
219 }
220}