nitro_shared/util/
print.rs1use std::fmt::Debug;
2use std::io::Write;
3use std::sync::Arc;
4use std::sync::atomic::{AtomicUsize, Ordering};
5
6pub const INDENT_STR: &str = " ";
8
9#[derive(Debug, Clone)]
11pub struct ReplPrinter {
12 chars_written: Arc<AtomicUsize>,
13 finished: bool,
14 options: PrintOptions,
15}
16
17impl ReplPrinter {
18 pub fn new(verbose: bool) -> Self {
21 Self::from_options(PrintOptions::new(verbose, 0))
22 }
23
24 pub fn from_options(options: PrintOptions) -> Self {
26 Self {
27 chars_written: Arc::new(AtomicUsize::new(0)),
28 finished: false,
29 options,
30 }
31 }
32
33 pub fn indent(&mut self, indent: usize) {
35 self.options.indent = indent;
36 self.options.indent_str = make_indent(self.options.indent);
37 }
38
39 pub fn clearline(&self) {
41 if !self.options.verbose {
42 return;
43 }
44
45 let mut lock = std::io::stdout().lock();
46
47 let chars_written = self.chars_written.load(Ordering::Relaxed);
48 if chars_written == 0 {
49 return;
50 }
51
52 let _ = write!(&mut lock, "\r");
53 for _ in 0..chars_written {
54 let _ = write!(&mut lock, " ");
55 }
56 self.chars_written.store(0, Ordering::Relaxed);
57 let _ = lock.flush();
58 }
59
60 pub fn print(&self, text: &str) {
62 if !self.options.verbose {
63 return;
64 }
65 let mut lock = std::io::stdout().lock();
66
67 let _ = write!(&mut lock, "\r{}{text}", self.options.indent_str);
69
70 let written = get_terminal_width(text) + self.options.indent_str.chars().count();
72
73 let chars_written = self.chars_written.load(Ordering::Relaxed);
75 if written < chars_written {
76 let _ = write!(&mut lock, "{}", " ".repeat(chars_written - written));
77 }
78
79 self.chars_written.store(written, Ordering::Relaxed);
80 let _ = lock.flush();
81 }
82
83 pub fn println(&self, text: &str) {
85 self.newline();
86 self.print(text);
87 }
88
89 pub fn finish(&mut self) {
91 if self.finished {
92 return;
93 }
94 if self.chars_written.load(Ordering::Relaxed) != 0 {
95 self.newline();
96 }
97 self.finished = true;
98 }
99
100 pub fn force_finished(&mut self) {
102 self.finished = true;
103 }
104
105 pub fn newline(&self) {
107 let mut lock = std::io::stdout().lock();
108
109 self.chars_written.store(0, Ordering::Relaxed);
110
111 let _ = writeln!(&mut lock);
112 std::mem::drop(lock);
113 }
114}
115
116impl Drop for ReplPrinter {
117 fn drop(&mut self) {
118 self.finish();
119 }
120}
121
122pub fn make_indent(indent: usize) -> String {
124 INDENT_STR.repeat(indent)
125}
126
127#[derive(Debug, Clone)]
129pub struct PrintOptions {
130 pub verbose: bool,
132 pub indent: usize,
134 pub indent_str: String,
136}
137
138impl PrintOptions {
139 pub fn new(verbose: bool, indent: usize) -> Self {
141 Self {
142 verbose,
143 indent,
144 indent_str: make_indent(indent),
145 }
146 }
147
148 pub fn increase_indent(opt: &Self) -> Self {
150 let mut out = opt.clone();
151 out.indent += 1;
152 out.indent_str = make_indent(out.indent);
153 out
154 }
155}
156
157pub fn get_terminal_width(text: &str) -> usize {
160 let esc = 0o33 as char;
161 let mut out = 0;
162 let mut in_escape = false;
163 for c in text.chars() {
164 if c == esc {
165 in_escape = true;
166 }
167
168 if !in_escape {
169 out += 1;
170 }
171
172 if c == 'm' {
173 in_escape = false;
174 }
175 }
176 out
177}
178
179#[cfg(test)]
180mod tests {
181 use super::*;
182
183 #[test]
184 fn test_terminal_width() {
185 assert_eq!(get_terminal_width("\u{001b}[16mHello"), 5);
186 }
187}