1use crate::prelude::AnsiSeq;
2use std::io::Write;
3
4#[derive(Debug, Default)]
5pub struct Body {
7 buffer: Vec<usize>,
8}
9
10const CLEAR_TIL_EOL: AnsiSeq = AnsiSeq::ClearCursorToEndOfLine;
11const CLEAR_TIL_EOF: AnsiSeq = AnsiSeq::ClearCursorToEndOfScreen;
12const JUMP_AT_BEGINNING: AnsiSeq = AnsiSeq::AbsoluteMove { horizontal: 0 };
13
14impl Body {
15 pub fn overwrite<T, Writer>(
22 &mut self,
23 new_text: &T,
24 mut write: Writer,
25 available_width: usize,
26 ) -> std::io::Result<()>
27 where
28 Writer: Write,
29 T: ToString,
30 {
31 let new_text = new_text.to_string();
32 let mut next_buffer = if self.buffer.is_empty() {
33 vec![]
34 } else {
35 Vec::with_capacity(self.buffer.capacity())
36 };
37 let mut symbols = vec![];
38
39 if new_text.is_empty() {
40 write!(symbols, "{CLEAR_TIL_EOL}")?;
41 next_buffer.push(0);
42 } else {
43 for (line_no, line) in new_text.lines().enumerate() {
44 next_buffer.push(line.len());
45 if line_no > 0 {
46 symbols.push(b'\n');
47 }
48 symbols.write_all(line.as_bytes())?;
49 write!(symbols, "{CLEAR_TIL_EOL}")?;
50 }
51 }
52
53 match self.guess_previous_body_height(available_width) {
54 1 => {
55 write!(write, "{JUMP_AT_BEGINNING}")?;
56 }
57 lines if lines > 1 => {
58 let movement = AnsiSeq::MoveLines {
59 down: 0,
60 up: (lines - 1) as u16,
61 };
62 write!(write, "{JUMP_AT_BEGINNING}{movement}")?;
63 }
64 _ => {}
65 }
66
67 write!(symbols, "{CLEAR_TIL_EOF}")?;
68 write.write_all(&symbols)?;
69 self.buffer = next_buffer;
70 Ok(())
71 }
72
73 pub fn new() -> Self {
75 Default::default()
76 }
77
78 fn guess_previous_body_height(&self, available_width: usize) -> usize {
79 let mut lines = 0;
80 for line in &self.buffer {
81 lines += line / available_width + 1;
82 }
83 lines
84 }
85}
86
87#[cfg(test)]
88mod tests {
89 use super::*;
90 use std::error::Error;
91 use std::fmt::Write;
92
93 #[test]
94 fn initial() -> Result<(), Box<dyn Error>> {
95 let mut body = Body::default();
96 let mut buf = Vec::new();
97 body.overwrite(&"content", &mut buf, 80)?;
98
99 let string = String::from_utf8(buf.clone())?;
100 assert_eq!("content\\e[0K\\e[0J", string);
101
102 body.overwrite(&"content + content", &mut buf, 80)?;
103 let string = String::from_utf8(buf.clone())?;
104 assert_eq!(
105 "content\\e[0K\\e[0J\\e[0Gcontent + content\\e[0K\\e[0J",
106 string
107 );
108
109 body.overwrite(&"none", &mut buf, 80)?;
110 let string = String::from_utf8(buf.clone())?;
111
112 let mut expected = String::new();
113 expected.write_str("content")?; expected.write_str("\\e[0K")?; expected.write_str("\\e[0J")?; expected.write_str("\\e[0G")?; expected.write_str("content + content")?; expected.write_str("\\e[0K")?; expected.write_str("\\e[0J")?; expected.write_str("\\e[0G")?; expected.write_str("none")?; expected.write_str("\\e[0K")?; expected.write_str("\\e[0J")?; assert_eq!(expected, string);
126
127 body.overwrite(&"", &mut buf, 80)?;
128
129 expected.write_str("\\e[0G")?; expected.write_str("\\e[0K")?; expected.write_str("\\e[0J")?; let string = String::from_utf8(buf.clone())?;
134
135 assert_eq!(expected, string,);
136
137 Ok(())
138 }
139}