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 if let Some(line) = data.lines.get(index) {
219 Some(call(Cow::Borrowed(line.content.as_slice())))
220 } else {
221 None
222 }
223 }
224
225 fn set_needed_lines(&self, _lines: usize) {}
231
232 fn paused(&self) -> bool {
234 false
235 }
236}
237
238struct FileData {
239 title: String,
240 info: String,
241 lines: Vec<LineData>,
242}
243
244impl FileData {
245 fn new(title: impl Into<String>) -> FileData {
246 FileData {
247 title: title.into(),
248 info: String::new(),
249 lines: Vec::new(),
250 }
251 }
252
253 fn line_mut(&mut self, index: usize) -> Result<&mut LineData> {
254 let length = self.lines.len();
255 if let Some(line) = self.lines.get_mut(index) {
256 return Ok(line);
257 }
258 Err(ControlledFileError::LineOutOfRange { index, length })
259 }
260
261 fn apply_change(&mut self, change: Change) -> Result<()> {
262 match change {
263 Change::SetTitle { title } => {
264 self.title = title;
265 }
266 Change::SetInfo { info } => {
267 self.info = info;
268 }
269 Change::AppendLine { content } => {
270 self.lines.push(LineData::with_content(content));
271 }
272 Change::InsertLine {
273 before_index,
274 content,
275 } => {
276 self.lines
277 .insert(before_index, LineData::with_content(content));
278 }
279 Change::ReplaceLine { index, content } => {
280 self.line_mut(index)?.content = content;
281 }
282 Change::DeleteLine { index } => {
283 self.lines.remove(index);
284 }
285 Change::AppendLines { contents } => {
286 let new_lines = contents.into_iter().map(LineData::with_content);
287 self.lines.extend(new_lines);
288 }
289 Change::InsertLines {
290 before_index,
291 contents,
292 } => {
293 let new_lines = contents.into_iter().map(LineData::with_content);
294 self.lines.splice(before_index..before_index, new_lines);
295 }
296 Change::ReplaceLines { range, contents } => {
297 let new_lines = contents.into_iter().map(LineData::with_content);
298 self.lines.splice(range, new_lines);
299 }
300 Change::DeleteLines { range } => {
301 self.lines.splice(range, std::iter::empty());
302 }
303 Change::ReplaceAll { contents } => {
304 let new_lines = contents.into_iter().map(LineData::with_content);
305 self.lines = new_lines.collect();
306 }
307 }
308 Ok(())
309 }
310}
311
312struct LineData {
313 content: Vec<u8>,
314}
315
316impl LineData {
317 fn with_content(content: Vec<u8>) -> LineData {
318 LineData { content }
319 }
320}