1use std::io::{self, Write};
2
3use console::Term;
4
5pub fn gen_prefix(prefix: &str, prefix_len: u16) -> String {
7 if console::measure_text_width(prefix) > (prefix_len - 1).into() {
8 panic!("Line prefix \"{prefix}\" too long!");
9 }
10
11 let left_padding_size = (prefix_len as usize) - 1 - console::measure_text_width(prefix);
13 let mut real_prefix: String = " ".repeat(left_padding_size);
14 real_prefix.push_str(prefix);
15 real_prefix.push(' ');
16 real_prefix
17}
18
19pub trait Writeln {
20 fn writeln(&self, prefix: &str, msg: &str) -> io::Result<()>;
21}
22
23impl Writeln for Writer {
24 fn writeln(&self, prefix: &str, msg: &str) -> io::Result<()> {
25 let max_len = self.get_max_len();
26
27 let mut res = Ok(());
28
29 writeln_inner(msg, prefix, max_len as usize, self.prefix_len, |t, s| {
30 match t {
31 MessageType::Msg => res = self.term.write_str(s),
32 MessageType::Prefix => res = self.write_prefix(s),
33 };
34 });
35
36 res
37 }
38}
39
40impl Default for Writer {
41 fn default() -> Self {
42 Writer {
43 term: Term::stderr(),
44 prefix_len: 10,
45 limit_max_len: Some(80),
46 }
47 }
48}
49
50pub struct Writer {
51 term: Term,
52 pub prefix_len: u16,
53 limit_max_len: Option<u16>,
54}
55
56impl Writer {
57 pub fn new(prefix_len: u16) -> Self {
58 Self {
59 prefix_len,
60 ..Default::default()
61 }
62 }
63
64 pub fn new_no_limit_length(prefix_len: u16) -> Self {
65 Self {
66 prefix_len,
67 limit_max_len: None,
68 ..Default::default()
69 }
70 }
71
72 pub fn new_stdout() -> Self {
73 Self {
74 term: Term::stdout(),
75 ..Default::default()
76 }
77 }
78
79 pub fn is_terminal(&self) -> bool {
81 self.term.is_term()
82 }
83
84 pub fn show_cursor(&self) -> io::Result<()> {
86 self.term.show_cursor()?;
87 Ok(())
88 }
89
90 pub fn get_max_len(&self) -> u16 {
92 let len = self.get_length();
93
94 if let Some(limit) = self.limit_max_len {
95 if len < limit { len } else { limit }
96 } else {
97 len
98 }
99 }
100
101 pub fn get_height(&self) -> u16 {
103 self.term.size_checked().unwrap_or((25, 80)).0
104 }
105
106 pub fn get_length(&self) -> u16 {
108 self.term.size_checked().unwrap_or((25, 80)).1
109 }
110
111 pub fn get_writer(&self) -> Box<dyn Write> {
113 Box::new(self.term.clone())
114 }
115
116 pub fn write_prefix(&self, prefix: &str) -> io::Result<()> {
118 self.term.write_str(&gen_prefix(prefix, self.prefix_len))?;
119
120 Ok(())
121 }
122
123 pub fn get_prefix_len(&self) -> u16 {
124 self.prefix_len
125 }
126
127 pub fn write_chunks<S: AsRef<str>>(
128 &self,
129 prefix: &str,
130 chunks: &[S],
131 prefix_len: u16,
132 ) -> io::Result<()> {
133 if chunks.is_empty() {
134 return Ok(());
135 }
136
137 let max_len: usize = (self.get_max_len() - prefix_len).into();
138 self.write_prefix(prefix)?;
140 let mut cur_line_len: usize = prefix_len.into();
141 for chunk in chunks {
142 let chunk = chunk.as_ref();
143 let chunk_len = console::measure_text_width(chunk);
144 if cur_line_len + chunk_len + 1 > max_len {
147 self.term.write_str("\n")?;
148 self.write_prefix("")?;
149 cur_line_len = 0;
150 }
151 self.term.write_str(chunk)?;
152 self.term.write_str(" ")?;
153 cur_line_len += chunk_len + 1;
154 }
155 self.term.write_str("\n")?;
157
158 Ok(())
159 }
160}
161
162pub enum MessageType {
163 Msg,
164 Prefix,
165}
166
167pub fn writeln_inner(
168 msg: &str,
169 prefix: &str,
170 max_len: usize,
171 prefix_len: u16,
172 mut callback: impl FnMut(MessageType, &str),
173) {
174 let len = max_len - prefix_len as usize;
175 let mut first_run = true;
176
177 for i in textwrap::wrap(msg, len) {
178 if first_run {
179 callback(MessageType::Prefix, prefix);
180 first_run = false;
181 } else {
182 callback(MessageType::Prefix, "");
183 }
184
185 callback(MessageType::Msg, &format!("{i}\n"));
186 }
187}