1use std::{
2 collections::VecDeque,
3 error::Error,
4 fs::File,
5 io::{Read, Write},
6};
7
8pub struct Window {
27 elements: VecDeque<Vec<u8>>,
28 size: u16,
29 chunk_size: u16,
30 file: File,
31}
32
33impl Window {
34 pub fn new(size: u16, chunk_size: u16, file: File) -> Window {
36 Window {
37 elements: VecDeque::new(),
38 size,
39 chunk_size,
40 file,
41 }
42 }
43
44 pub fn fill(&mut self) -> Result<bool, Box<dyn Error>> {
47 for _ in self.len()..self.size {
48 let mut chunk = vec![0; self.chunk_size as usize];
49 let size = self.file.read(&mut chunk)?;
50
51 if size != self.chunk_size as usize {
52 chunk.truncate(size);
53 self.elements.push_back(chunk);
54 return Ok(false);
55 }
56
57 self.elements.push_back(chunk);
58 }
59
60 Ok(true)
61 }
62
63 pub fn empty(&mut self) -> Result<(), Box<dyn Error>> {
65 for data in &self.elements {
66 self.file.write_all(data)?;
67 }
68
69 self.elements.clear();
70
71 Ok(())
72 }
73
74 pub fn remove(&mut self, amount: u16) -> Result<(), &'static str> {
76 if amount > self.len() {
77 return Err("amount cannot be larger than length of window");
78 }
79
80 drop(self.elements.drain(0..amount as usize));
81
82 Ok(())
83 }
84
85 pub fn add(&mut self, data: Vec<u8>) -> Result<(), &'static str> {
87 if self.len() == self.size {
88 return Err("cannot add to a full window");
89 }
90
91 self.elements.push_back(data);
92
93 Ok(())
94 }
95
96 pub fn get_elements(&self) -> &VecDeque<Vec<u8>> {
98 &self.elements
99 }
100
101 pub fn len(&self) -> u16 {
103 self.elements.len() as u16
104 }
105
106 pub fn is_empty(&self) -> bool {
108 self.elements.is_empty()
109 }
110
111 pub fn is_full(&self) -> bool {
113 self.elements.len() as u16 == self.size
114 }
115
116 pub fn file_len(&self) -> Result<u64, Box<dyn Error>> {
118 Ok(self.file.metadata()?.len())
119 }
120}
121
122#[cfg(test)]
123mod tests {
124 use super::*;
125 use std::{
126 fs::{self, OpenOptions},
127 io::Write,
128 };
129
130 const DIR_NAME: &str = "target/test";
131
132 #[test]
133 fn fills_and_removes_from_window() {
134 const FILENAME: &str = "fills_and_removes_from_window.txt";
135
136 let mut file = initialize(FILENAME);
137 file.write_all(b"Hello, world!").unwrap();
138 file.flush().unwrap();
139 drop(file);
140
141 file = open(FILENAME);
142
143 let mut window = Window::new(2, 5, file);
144 window.fill().unwrap();
145 assert_eq!(window.elements.len(), 2);
146 assert_eq!(window.elements[0], b"Hello"[..]);
147 assert_eq!(window.elements[1], b", wor"[..]);
148
149 window.remove(1).unwrap();
150 assert_eq!(window.elements.len(), 1);
151 assert_eq!(window.elements[0], b", wor"[..]);
152
153 window.fill().unwrap();
154 assert_eq!(window.elements.len(), 2);
155 assert_eq!(window.elements[0], b", wor"[..]);
156 assert_eq!(window.elements[1], b"ld!"[..]);
157
158 clean(FILENAME);
159 }
160
161 #[test]
162 fn adds_to_and_empties_window() {
163 const FILENAME: &str = "adds_to_and_empties_window.txt";
164
165 let file = initialize(FILENAME);
166
167 let mut window = Window::new(3, 5, file);
168 window.add(b"Hello".to_vec()).unwrap();
169 assert_eq!(window.elements.len(), 1);
170 assert_eq!(window.elements[0], b"Hello"[..]);
171
172 window.add(b", wor".to_vec()).unwrap();
173 assert_eq!(window.elements.len(), 2);
174 assert_eq!(window.elements[0], b"Hello"[..]);
175 assert_eq!(window.elements[1], b", wor"[..]);
176
177 window.add(b"ld!".to_vec()).unwrap();
178 assert_eq!(window.elements.len(), 3);
179 assert_eq!(window.elements[0], b"Hello"[..]);
180 assert_eq!(window.elements[1], b", wor"[..]);
181 assert_eq!(window.elements[2], b"ld!"[..]);
182
183 window.empty().unwrap();
184 assert_eq!(window.elements.len(), 0);
185
186 let mut contents = Default::default();
187 File::read_to_string(
188 &mut File::open(DIR_NAME.to_string() + "/" + FILENAME).unwrap(),
189 &mut contents,
190 )
191 .unwrap();
192 assert_eq!(contents, "Hello, world!");
193
194 clean(FILENAME);
195 }
196
197 fn initialize(filename: &str) -> File {
198 let filename = DIR_NAME.to_string() + "/" + filename;
199
200 let _ = fs::create_dir_all(DIR_NAME);
201
202 if File::open(&filename).is_ok() {
203 fs::remove_file(&filename).unwrap();
204 }
205
206 OpenOptions::new()
207 .read(true)
208 .append(true)
209 .create(true)
210 .open(&filename)
211 .unwrap()
212 }
213
214 fn open(filename: &str) -> File {
215 let filename = DIR_NAME.to_string() + "/" + filename;
216
217 OpenOptions::new()
218 .read(true)
219 .append(true)
220 .create(true)
221 .open(filename)
222 .unwrap()
223 }
224
225 fn clean(filename: &str) {
226 let filename = DIR_NAME.to_string() + "/" + filename;
227 fs::remove_file(filename).unwrap();
228 if fs::remove_dir(DIR_NAME).is_err() {
229 }
232 }
233}