1use crate::*;
2
3
4pub type FileServerHandle = usize;
5
6
7pub const FILESERVER_MOCK_WRITE_FILENAME_SUFFIX: &str = "_written";
8
9
10pub trait FileServer
11{
12 fn get_handle(
13 &mut self,
14 report: &mut diagn::Report,
15 span: Option<diagn::Span>,
16 filename: &str)
17 -> Result<FileServerHandle, ()>;
18
19
20 fn get_handle_unwrap(
21 &mut self,
22 filename: &str)
23 -> FileServerHandle
24 {
25 self.get_handle(
26 &mut diagn::Report::new(),
27 None,
28 filename)
29 .unwrap()
30 }
31
32
33 fn get_filename(
34 &self,
35 file_handle: FileServerHandle)
36 -> &str;
37
38
39 fn get_bytes(
40 &self,
41 report: &mut diagn::Report,
42 span: Option<diagn::Span>,
43 file_handle: FileServerHandle)
44 -> Result<Vec<u8>, ()>;
45
46
47 fn get_bytes_unwrap(
48 &self,
49 file_handle: FileServerHandle)
50 -> Vec<u8>
51 {
52 self.get_bytes(
53 &mut diagn::Report::new(),
54 None,
55 file_handle)
56 .unwrap()
57 }
58
59
60 fn get_str(
61 &self,
62 report: &mut diagn::Report,
63 span: Option<diagn::Span>,
64 file_handle: FileServerHandle)
65 -> Result<String, ()>
66 {
67 let bytes = self.get_bytes(
68 report,
69 span,
70 file_handle)?;
71
72 let string = String::from_utf8_lossy(&bytes).to_string();
73
74 Ok(string)
75 }
76
77
78 fn get_str_unwrap(
79 &self,
80 file_handle: FileServerHandle)
81 -> String
82 {
83 self.get_str(
84 &mut diagn::Report::new(),
85 None,
86 file_handle)
87 .unwrap()
88 }
89
90
91 fn write_bytes(
92 &mut self,
93 report: &mut diagn::Report,
94 span: Option<diagn::Span>,
95 filename: &str,
96 data: &Vec<u8>)
97 -> Result<(), ()>;
98
99
100 fn get_excerpt(
101 &self,
102 span: diagn::Span)
103 -> String
104 {
105 if let Ok(chars) = self.get_str(
106 &mut diagn::Report::new(),
107 None,
108 span.file_handle)
109 {
110 let counter = util::CharCounter::new(&chars);
111 let location = span.location().unwrap();
112 counter.get_excerpt(location.0, location.1).to_string()
113 }
114 else
115 {
116 "".to_string()
117 }
118 }
119}
120
121
122pub struct FileServerMock
123{
124 handles: std::collections::HashMap<String, FileServerHandle>,
125 handles_to_filename: Vec<String>,
126 files: Vec<Vec<u8>>,
127}
128
129
130pub struct FileServerReal
131{
132 handles: std::collections::HashMap<String, FileServerHandle>,
133 handles_to_filename: Vec<String>,
134 std_files: Vec<Option<&'static str>>,
135}
136
137
138impl FileServerMock
139{
140 pub fn new() -> FileServerMock
141 {
142 FileServerMock {
143 handles: std::collections::HashMap::new(),
144 handles_to_filename: Vec::new(),
145 files: Vec::new(),
146 }
147 }
148
149
150 pub fn add_std_files(
151 &mut self,
152 entries: &[(&str, &str)])
153 {
154 for (filename, contents) in entries
155 {
156 self.add(*filename, *contents);
157 }
158 }
159
160
161 pub fn add<S, T>(
162 &mut self,
163 filename: S,
164 contents: T)
165 where S: Into<String>, T: Into<Vec<u8>>
166 {
167 let filename = filename.into();
168
169 let next_index = self.handles.len();
170
171 let handle = *self.handles
172 .entry(filename.clone())
173 .or_insert(next_index.try_into().unwrap());
174
175 while handle >= self.files.len()
176 {
177 self.handles_to_filename.push("".to_string());
178 self.files.push(Vec::new());
179 }
180
181 self.handles_to_filename[handle] = filename;
182 self.files[handle] = contents.into();
183 }
184}
185
186
187impl FileServerReal
188{
189 pub fn new() -> FileServerReal
190 {
191 FileServerReal {
192 handles: std::collections::HashMap::new(),
193 handles_to_filename: Vec::new(),
194 std_files: Vec::new(),
195 }
196 }
197
198
199 pub fn add_std_files(
200 &mut self,
201 entries: &[(&str, &'static str)])
202 {
203 for (filename, contents) in entries
204 {
205 self.add(*filename, *contents);
206 }
207 }
208
209
210 pub fn add<S>(
211 &mut self,
212 filename: S,
213 contents: &'static str)
214 where S: Into<String>
215 {
216 let filename = filename.into();
217
218 let next_index = self.handles.len();
219
220 let handle = *self.handles
221 .entry(filename.clone())
222 .or_insert(next_index.try_into().unwrap());
223
224 while handle >= self.std_files.len()
225 {
226 self.handles_to_filename.push("".to_string());
227 self.std_files.push(None);
228 }
229
230 self.handles_to_filename[handle] = filename;
231 self.std_files[handle] = Some(contents);
232 }
233}
234
235
236impl FileServer for FileServerMock
237{
238 fn get_handle(
239 &mut self,
240 report: &mut diagn::Report,
241 span: Option<diagn::Span>,
242 filename: &str)
243 -> Result<FileServerHandle, ()>
244 {
245 if self.handles.len() == FileServerHandle::MAX
246 {
247 report_error(
248 report,
249 span,
250 "exhausted number of file handles");
251
252 return Err(());
253 }
254
255 if !self.handles.contains_key(filename)
256 {
257 report_error(
258 report,
259 span,
260 format!(
261 "file not found: `{}`",
262 filename));
263
264 return Err(());
265 }
266
267 let handle = self.handles.get(filename).unwrap();
268
269 Ok(*handle)
270 }
271
272
273 fn get_filename(
274 &self,
275 file_handle: FileServerHandle)
276 -> &str
277 {
278 &self.handles_to_filename[file_handle]
279 }
280
281
282 fn get_bytes(
283 &self,
284 _report: &mut diagn::Report,
285 _span: Option<diagn::Span>,
286 file_handle: FileServerHandle)
287 -> Result<Vec<u8>, ()>
288 {
289 Ok(self.files[file_handle].clone())
290 }
291
292
293 fn write_bytes(
294 &mut self,
295 _report: &mut diagn::Report,
296 _span: Option<diagn::Span>,
297 filename: &str,
298 data: &Vec<u8>)
299 -> Result<(), ()>
300 {
301 let new_index = self.handles.len();
302
303 let mock_filename = format!(
304 "{}{}",
305 filename,
306 FILESERVER_MOCK_WRITE_FILENAME_SUFFIX);
307
308 let handle = *self.handles
309 .entry(mock_filename)
310 .or_insert(new_index.try_into().unwrap());
311
312 while handle >= self.files.len()
313 {
314 self.files.push(Vec::new());
315 }
316
317 self.files[handle] = data.clone();
318
319 Ok(())
320 }
321}
322
323
324impl FileServer for FileServerReal
325{
326 fn get_handle(
327 &mut self,
328 report: &mut diagn::Report,
329 span: Option<diagn::Span>,
330 filename: &str)
331 -> Result<FileServerHandle, ()>
332 {
333 if let Some(handle) = self.handles.get(filename)
334 {
335 return Ok(*handle);
336 }
337
338 let filename_path = std::path::PathBuf::from(filename);
339
340 if !filename_path.exists()
341 {
342 report_error(
343 report,
344 span,
345 format!(
346 "file not found: `{}`",
347 filename));
348
349 return Err(());
350 }
351
352 if self.handles.len() == FileServerHandle::MAX
353 {
354 report_error(
355 report,
356 span,
357 "exhausted number of file handles");
358
359 return Err(());
360 }
361
362 let filename_path_str = filename_path
363 .to_string_lossy()
364 .to_string();
365
366 match self.handles.get(&filename_path_str)
367 {
368 Some(handle) => Ok(*handle),
369 None =>
370 {
371 let handle: FileServerHandle =
372 self.handles.len();
373
374 self.handles.insert(
375 filename_path_str.clone(),
376 handle);
377
378 self.handles_to_filename.push(
379 filename_path_str);
380
381 Ok(handle)
382 }
383 }
384 }
385
386
387 fn get_filename(
388 &self,
389 file_handle: FileServerHandle)
390 -> &str
391 {
392 &self.handles_to_filename[file_handle]
393 }
394
395
396 fn get_bytes(
397 &self,
398 report: &mut diagn::Report,
399 span: Option<diagn::Span>,
400 file_handle: FileServerHandle)
401 -> Result<Vec<u8>, ()>
402 {
403 if let Some(Some(std_contents)) = self.std_files.get(file_handle)
404 {
405 return Ok(std_contents.as_bytes().iter().copied().collect());
406 }
407
408 let filename = &self.handles_to_filename[file_handle];
409 let filename_path = &std::path::Path::new(filename);
410
411 let mut file = {
412 match std::fs::File::open(filename_path)
413 {
414 Ok(file) => file,
415 Err(err) =>
416 {
417 report_error(
418 report,
419 span,
420 format!(
421 "could not open file `{}`: {}",
422 filename,
423 err));
424
425 return Err(());
426 }
427 }
428 };
429
430 let mut vec = Vec::new();
431
432 use std::io::Read;
433 match file.read_to_end(&mut vec)
434 {
435 Ok(_) => Ok(vec),
436 Err(err) =>
437 {
438 report_error(
439 report,
440 span,
441 format!(
442 "could not read file `{}`: {}",
443 filename,
444 err));
445
446 return Err(());
447 }
448 }
449 }
450
451
452 fn write_bytes(
453 &mut self,
454 report: &mut diagn::Report,
455 span: Option<diagn::Span>,
456 filename: &str,
457 data: &Vec<u8>)
458 -> Result<(), ()>
459 {
460 let filename_path = &std::path::Path::new(filename);
461
462 let mut file = {
463 match std::fs::File::create(filename_path)
464 {
465 Ok(file) => file,
466 Err(err) =>
467 {
468 report_error(
469 report,
470 span,
471 format!(
472 "could not create file `{}`: {}",
473 filename,
474 err));
475
476 return Err(());
477 }
478 }
479 };
480
481 use std::io::Write;
482 match file.write_all(data)
483 {
484 Ok(_) => Ok(()),
485 Err(err) =>
486 {
487 report_error(
488 report,
489 span,
490 format!("could not write to file `{}`: {}",
491 filename,
492 err));
493
494 Err(())
495 }
496 }
497 }
498}
499
500
501fn report_error<S>(
502 report: &mut diagn::Report,
503 span: Option<diagn::Span>,
504 descr: S)
505 where S: Into<String>
506{
507 if let Some(span) = span
508 {
509 report.error_span(descr, span);
510 }
511 else
512 {
513 report.error(descr);
514 }
515}