1use std::borrow::Cow;
6use std::ops::Range;
7use std::sync::{Arc, Mutex, RwLock};
8
9use thiserror::Error;
10
11use crate::event::{Event, EventSender};
12use crate::file::{FileIndex, FileInfo};
13
14#[derive(Debug, Error)]
16pub enum ControlledFileError {
17 #[error("line number {index} out of range (0..{length})")]
19 LineOutOfRange {
20 index: usize,
22 length: usize,
24 },
25
26 #[error(transparent)]
28 Error(#[from] crate::error::Error),
29}
30
31pub type Result<T> = std::result::Result<T, ControlledFileError>;
33
34#[derive(Clone)]
40pub struct Controller {
41 data: Arc<RwLock<FileData>>,
42 notify: Arc<Mutex<Vec<(EventSender, FileIndex)>>>,
43}
44
45impl Controller {
46 pub fn new(title: impl Into<String>) -> Controller {
48 Controller {
49 data: Arc::new(RwLock::new(FileData::new(title))),
50 notify: Arc::new(Mutex::new(Vec::new())),
51 }
52 }
53
54 pub fn title(&self) -> String {
56 let data = self.data.read().unwrap();
57 data.title.clone()
58 }
59
60 pub fn info(&self) -> String {
62 let data = self.data.read().unwrap();
63 data.info.clone()
64 }
65
66 pub fn apply_changes(&self, changes: impl IntoIterator<Item = Change>) -> Result<()> {
68 let mut data = self.data.write().unwrap();
69 for change in changes {
70 data.apply_change(change)?;
71 }
72 let notify = self.notify.lock().unwrap();
75 for (event_sender, index) in notify.iter() {
76 event_sender.send(Event::Reloading(*index))?;
77 }
78 Ok(())
79 }
80}
81
82pub enum Change {
84 SetTitle {
86 title: String,
88 },
89
90 SetInfo {
92 info: String,
94 },
95
96 AppendLine {
98 content: Vec<u8>,
100 },
101
102 InsertLine {
104 before_index: usize,
106 content: Vec<u8>,
108 },
109
110 ReplaceLine {
112 index: usize,
114 content: Vec<u8>,
116 },
117
118 DeleteLine {
120 index: usize,
122 },
123
124 AppendLines {
126 contents: Vec<Vec<u8>>,
128 },
129
130 InsertLines {
132 before_index: usize,
134 contents: Vec<Vec<u8>>,
136 },
137
138 ReplaceLines {
141 range: Range<usize>,
143 contents: Vec<Vec<u8>>,
145 },
146
147 DeleteLines {
149 range: Range<usize>,
151 },
152
153 ReplaceAll {
155 contents: Vec<Vec<u8>>,
157 },
158}
159
160#[derive(Clone)]
162pub struct ControlledFile {
163 index: FileIndex,
164 data: Arc<RwLock<FileData>>,
165}
166
167impl ControlledFile {
168 pub(crate) fn new(
169 controller: &Controller,
170 index: FileIndex,
171 event_sender: EventSender,
172 ) -> ControlledFile {
173 let mut notify = controller.notify.lock().unwrap();
174 notify.push((event_sender, index));
175 ControlledFile {
176 index,
177 data: controller.data.clone(),
178 }
179 }
180}
181
182impl FileInfo for ControlledFile {
183 fn index(&self) -> FileIndex {
185 self.index
186 }
187
188 fn title(&self) -> Cow<'_, str> {
190 let data = self.data.read().unwrap();
191 Cow::Owned(data.title.clone())
192 }
193
194 fn info(&self) -> Cow<'_, str> {
196 let data = self.data.read().unwrap();
197 Cow::Owned(data.info.clone())
198 }
199
200 fn loaded(&self) -> bool {
202 true
203 }
204
205 fn lines(&self) -> usize {
207 self.data.read().unwrap().lines.len()
208 }
209
210 fn with_line<T, F>(&self, index: usize, mut call: F) -> Option<T>
214 where
215 F: FnMut(Cow<'_, [u8]>) -> T,
216 {
217 let data = self.data.read().unwrap();
218 data.lines.get(index).map(|line| call(Cow::Borrowed(line.content.as_slice())))
219 }
220
221 fn set_needed_lines(&self, _lines: usize) {}
227
228 fn paused(&self) -> bool {
230 false
231 }
232}
233
234struct FileData {
235 title: String,
236 info: String,
237 lines: Vec<LineData>,
238}
239
240impl FileData {
241 fn new(title: impl Into<String>) -> FileData {
242 FileData {
243 title: title.into(),
244 info: String::new(),
245 lines: Vec::new(),
246 }
247 }
248
249 fn line_mut(&mut self, index: usize) -> Result<&mut LineData> {
250 let length = self.lines.len();
251 if let Some(line) = self.lines.get_mut(index) {
252 return Ok(line);
253 }
254 Err(ControlledFileError::LineOutOfRange { index, length })
255 }
256
257 fn apply_change(&mut self, change: Change) -> Result<()> {
258 match change {
259 Change::SetTitle { title } => {
260 self.title = title;
261 }
262 Change::SetInfo { info } => {
263 self.info = info;
264 }
265 Change::AppendLine { content } => {
266 self.lines.push(LineData::with_content(content));
267 }
268 Change::InsertLine {
269 before_index,
270 content,
271 } => {
272 self.lines
273 .insert(before_index, LineData::with_content(content));
274 }
275 Change::ReplaceLine { index, content } => {
276 self.line_mut(index)?.content = content;
277 }
278 Change::DeleteLine { index } => {
279 self.lines.remove(index);
280 }
281 Change::AppendLines { contents } => {
282 let new_lines = contents.into_iter().map(LineData::with_content);
283 self.lines.extend(new_lines);
284 }
285 Change::InsertLines {
286 before_index,
287 contents,
288 } => {
289 let new_lines = contents.into_iter().map(LineData::with_content);
290 self.lines.splice(before_index..before_index, new_lines);
291 }
292 Change::ReplaceLines { range, contents } => {
293 let new_lines = contents.into_iter().map(LineData::with_content);
294 self.lines.splice(range, new_lines);
295 }
296 Change::DeleteLines { range } => {
297 self.lines.splice(range, std::iter::empty());
298 }
299 Change::ReplaceAll { contents } => {
300 let new_lines = contents.into_iter().map(LineData::with_content);
301 self.lines = new_lines.collect();
302 }
303 }
304 Ok(())
305 }
306}
307
308struct LineData {
309 content: Vec<u8>,
310}
311
312impl LineData {
313 fn with_content(content: Vec<u8>) -> LineData {
314 LineData { content }
315 }
316}