customasm/util/
fileserver.rs

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}