1use std::collections::HashMap;
2use std::fs::File;
3use std::hash::BuildHasherDefault;
4use std::io::{BufReader, Write, BufWriter, Seek, SeekFrom, Read};
5use std::iter::Iterator;
6use std::path::{PathBuf, StripPrefixError};
7
8use crate::ReadSeek;
9
10use walkdir::{IntoIter, WalkDir};
11use failure::Fail;
12use rustc_hash::FxHasher;
13
14use zip::ZipArchive;
15use zip::result::ZipError;
16
17pub type SourceId = usize;
19
20#[derive(Debug, Copy, Clone, Eq, PartialEq)]
22pub enum TrustLevel
23{
24 UntrustedSource,
26 TrustedSource
28}
29
30pub struct SourceManager
32{
33 sources : Vec<Box<dyn Source>>
34}
35
36impl SourceManager
37{
38 pub fn new() -> SourceManager
40 {
41 SourceManager
42 {
43 sources : Vec::new()
44 }
45 }
46
47 pub fn add_source(&mut self, source : Box<dyn Source>) -> SourceId
49 {
50 self.sources.push(source);
51
52 self.sources.len()
53 }
54
55 pub fn source(&mut self, id : SourceId) -> Option<&mut Box<dyn Source>>
57 {
58 if id == 0 || id - 1 >= self.sources.len()
59 {
60 return None
61 }
62
63 Some(&mut self.sources[id - 1])
64 }
65
66 pub fn package_source_id(&self, package_name : &str) -> SourceId
70 {
71 for i in (0..self.sources.len()).rev()
72 {
73 if self.sources[i].has_package(package_name)
74 {
75 return i + 1;
76 }
77 }
78
79 0
80 }
81
82 pub fn package_source(&mut self, package_name : &str) -> Option<&mut Box<dyn Source>>
86 {
87 for source in self.sources.iter_mut().rev()
88 {
89 if source.has_package(package_name)
90 {
91 return Some(source);
92 }
93 }
94
95 None
96 }
97
98 pub fn clear(&mut self)
100 {
101 self.sources.clear();
102 }
103}
104
105#[derive(Debug, Fail)]
107pub enum PackageError
108{
109 #[fail(display = "Failed to access package source")]
112 Generic,
113 #[fail(display = "{}", _0)]
115 IoError(#[cause] std::io::Error),
116 #[fail(display = "Package item does not belong to the given prefix: {}", _0)]
118 PrefixMismatch(StripPrefixError),
119 #[fail(display = "The data object was not found")]
121 DataNotFound,
122 #[fail(display = "Operation not supported")]
124 NotSupported,
125 #[fail(display = "The package contained invalid data: {}", _0)]
127 BadData(String)
128}
129
130impl From<std::io::Error> for PackageError
131{
132 fn from(error : std::io::Error) -> PackageError
133 {
134 PackageError::IoError(error)
135 }
136}
137
138pub trait Source
141{
142 fn get_uri(&self) -> &str;
144 fn has_package(&self, package_name : &str) -> bool;
146 fn list_packages(&mut self) -> Vec<String>;
148 fn read_file<'a>(&'a mut self, package_name: &str, pathname: &str) -> Result<Box<dyn ReadSeek + 'a>, PackageError>;
150 #[allow(unused_variables)]
152 fn write_file<'a>(&'a mut self, package_name : &str, pathname : &str) -> Result<Box<dyn Write + 'a>, PackageError> { Err(PackageError::NotSupported) }
153 fn iter_entries<'a>(&'a mut self, package_name : &str, type_folder : &str) -> Box<dyn Iterator<Item = Result<String, PackageError>> + 'a>;
155 fn trust_level(&self, package_name : &str) -> TrustLevel;
158}
159
160
161struct EmptyEntryIter {}
165
166impl EmptyEntryIter
167{
168 fn new() -> EmptyEntryIter { EmptyEntryIter {} }
169}
170
171impl Iterator for EmptyEntryIter
172{
173 type Item = Result<String, PackageError>;
174
175 fn next(&mut self) -> Option<Result<String, PackageError>>
176 {
177 None
178 }
179}
180
181
182struct FilesystemIter
183{
184 basepath : PathBuf,
185 walkdir : IntoIter
186}
187
188impl FilesystemIter
189{
190 fn new<P : Into<PathBuf>>(basepath : P) -> FilesystemIter
191 {
192 let path = basepath.into();
193
194 FilesystemIter
195 {
196 basepath : path.clone(),
197 walkdir : WalkDir::new(path).into_iter(),
198 }
199 }
200}
201
202impl Iterator for FilesystemIter
203{
204 type Item = Result<String, PackageError>;
205
206 fn next(&mut self) -> Option<Result<String, PackageError>>
207 {
208 while let Some(dir_next) = self.walkdir.next()
209 {
210 let dir_entry = match dir_next
211 {
212 Ok(dir_entry) => { dir_entry }
213 Err(error) =>
214 {
215 if let Some(io_error) = error.io_error()
216 {
217 if io_error.kind() == std::io::ErrorKind::NotFound
218 {
219 return None;
220 }
221 }
222
223 return Some(Err(PackageError::IoError(error.into())));
224 }
225 };
226
227 if !dir_entry.file_type().is_file()
228 {
229 continue;
230 }
231
232 let path = dir_entry.path();
233 let filepath = match path.strip_prefix(self.basepath.clone())
234 {
235 Ok(filepath) => { filepath }
236 Err(error) =>
237 {
238 return Some(Err(PackageError::PrefixMismatch(error)));
239 }
240 };
241
242 let mut pathname = String::new();
243 let mut first_part = true;
244
245 for part in filepath.iter()
246 {
247 if !first_part
248 {
249 pathname += "/";
250 }
251 pathname += &part.to_string_lossy();
252
253 first_part = false;
254 }
255
256 return Some(Ok(pathname));
257 }
258
259 None
260 }
261}
262
263pub struct FilesystemSource
266{
267 basedir : String,
268 package_list : Option<Vec<String>>,
269 trust : TrustLevel
270}
271
272impl FilesystemSource
273{
274 pub fn new(directory : &str, trust : TrustLevel) -> FilesystemSource
276 {
277 FilesystemSource
278 {
279 basedir : directory.to_string(),
280 package_list : None,
281 trust
282 }
283 }
284}
285
286impl Source for FilesystemSource
287{
288 fn get_uri(&self) -> &str
289 {
290 &self.basedir
291 }
292
293 fn has_package(&self, package_name : &str) -> bool
294 {
295 let mut path = PathBuf::from(&self.basedir);
296 path.push(package_name);
297
298 path.exists()
299 }
300
301 fn list_packages(&mut self) -> Vec<String>
302 {
303 if let Some(package_list) = &self.package_list
304 {
305 return package_list.clone();
306 }
307
308 let mut package_list : Vec<String> = Vec::new();
309 let path = PathBuf::from(&self.basedir);
310
311 let walkdir = WalkDir::new(path).min_depth(1).max_depth(1).into_iter();
312
313 for dir_entry in walkdir
314 {
315 if let Ok(entry) = dir_entry
316 {
317 if !entry.file_type().is_dir()
318 {
319 continue;
320 }
321
322 package_list.push(entry.file_name().to_string_lossy().to_string());
323 }
324 }
325
326 package_list.sort();
327
328 self.package_list = Some(package_list.clone());
329
330 package_list
331 }
332
333 fn read_file<'a>(&'a mut self, package_name: &str, pathname: &str) -> Result<Box<dyn ReadSeek + 'a>, PackageError>
334 {
335 let mut path = PathBuf::from(&self.basedir);
336 path.push(format!("{}/{}", package_name, pathname));
337
338 let file = BufReader::new(File::open(path).map_err(
339 |error| PackageError::IoError(error))?);
340
341 Ok(Box::new(file))
342 }
343
344 fn write_file<'a>(&'a mut self, package_name : &str, pathname : &str) -> Result<Box<dyn Write + 'a>, PackageError>
345 {
346 let mut path = PathBuf::from(&self.basedir);
347 path.push(format!("{}/{}", package_name, pathname));
348 let mut folder_path = path.clone();
349
350 if !pathname.ends_with("/")
351 {
352 folder_path.pop();
353 }
354
355 std::fs::create_dir_all(folder_path).map_err(
356 |error| PackageError::IoError(error))?;
357
358 let file = BufWriter::new(File::create(path).map_err(
359 |error| PackageError::IoError(error))?);
360
361 Ok(Box::new(file))
362 }
363
364 fn iter_entries<'a>(&'a mut self, package_name : &str, type_folder : &str) -> Box<dyn Iterator<Item = Result<String, PackageError>> + 'a>
365 {
366 let mut path = PathBuf::from(&self.basedir);
367 path.push(package_name);
368 path.push(type_folder);
369
370 Box::new(FilesystemIter::new(path))
371 }
372
373 fn trust_level(&self, _package_name: &str) -> TrustLevel
374 {
375 self.trust
376 }
377}
378
379fn package_error_for_zip_error(error : ZipError) -> PackageError
380{
381 return match error
382 {
383 ZipError::Io(io_error) => { PackageError::IoError(io_error) }
384 ZipError::InvalidArchive(error_msg) => { PackageError::BadData(error_msg.to_string()) }
385 ZipError::UnsupportedArchive(error_msg) => { PackageError::BadData(error_msg.to_string()) }
386 ZipError::FileNotFound => { PackageError::DataNotFound }
387 }
388}
389
390struct FakeSeekReader<R : Read>
391{
392 reader : R
393}
394
395impl<R : Read> FakeSeekReader<R>
396{
397 fn new(reader : R) -> FakeSeekReader<R>
398 {
399 FakeSeekReader
400 {
401 reader
402 }
403 }
404}
405
406impl<R : Read> Read for FakeSeekReader<R>
407{
408 fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize>
409 {
410 self.reader.read(buf)
411 }
412}
413
414impl<R : Read> Seek for FakeSeekReader<R>
415{
416 fn seek(&mut self, _pos: SeekFrom) -> std::io::Result<u64>
417 {
418 Err(std::io::Error::new(std::io::ErrorKind::Other, "Seek not supported on ZIP archives"))
419 }
420}
421
422struct ZipFolderIter<'a, T : Iterator<Item = &'a str>>
423{
424 iter : T,
425 type_folder : String
426}
427
428impl<'a, T : Iterator<Item = &'a str>> ZipFolderIter<'a, T>
429{
430 fn new(iter : T, type_folder : &str) -> ZipFolderIter<'a, T>
431 {
432 ZipFolderIter
433 {
434 iter,
435 type_folder : type_folder.to_string()
436 }
437 }
438}
439
440impl<'a, T : Iterator<Item = &'a str>> Iterator for ZipFolderIter<'a, T>
441{
442 type Item = Result<String, PackageError>;
443
444 fn next(&mut self) -> Option<Result<String, PackageError>>
445 {
446 while let Some(item) = self.iter.next()
447 {
448 if let Some(filename) = item.strip_prefix(&format!("{}/", self.type_folder))
449 {
450 return Some(Ok(filename.to_string()));
451 }
452 }
453
454 None
455 }
456}
457
458pub struct ZipFolderSource
461{
462 basedir : String,
463 extension : String,
464 package_list : Option<Vec<String>>,
465 loaded_zips : HashMap<String, ZipArchive<BufReader<File>>, BuildHasherDefault<FxHasher>>,
466 trust : TrustLevel
467}
468
469impl ZipFolderSource
470{
471 pub fn new(directory : &str, trust : TrustLevel) -> ZipFolderSource
473 {
474 ZipFolderSource
475 {
476 basedir : directory.to_string(),
477 extension : String::from(".zip"),
478 package_list : None,
479 loaded_zips : HashMap::with_hasher(BuildHasherDefault::<FxHasher>::default()),
480 trust
481 }
482 }
483
484 pub fn with_extension(directory : &str, extension : &str, trust : TrustLevel) -> ZipFolderSource
488 {
489 let mut dot_extension = String::from(".");
490 dot_extension.push_str(extension);
491
492 ZipFolderSource
493 {
494 basedir : directory.to_string(),
495 extension : dot_extension,
496 package_list : None,
497 loaded_zips : HashMap::with_hasher(BuildHasherDefault::<FxHasher>::default()),
498 trust
499 }
500 }
501
502 pub fn zip_loaded(&self, package_name : &str) -> bool
504 {
505 self.loaded_zips.contains_key(package_name)
506 }
507
508 pub fn load_zip(&mut self, package_name : &str) -> Result<(), PackageError>
510 {
511 if self.zip_loaded(package_name)
512 {
513 return Ok(());
514 }
515
516 let mut path = PathBuf::from(&self.basedir);
517 let mut zip_name = String::from(package_name);
518 zip_name.push_str(&self.extension);
519 path.push(zip_name);
520
521 let file = BufReader::new(File::open(path).map_err(
522 |error| PackageError::IoError(error))?);
523
524 let archive = ZipArchive::new(file).map_err(
525 |error| package_error_for_zip_error(error))?;
526
527 self.loaded_zips.insert(package_name.to_string(), archive);
528
529 Ok(())
530 }
531
532 pub fn unload_zip(&mut self, package_name : &str)
534 {
535 self.loaded_zips.remove(package_name);
536 }
537}
538
539impl Source for ZipFolderSource
540{
541 fn get_uri(&self) -> &str
542 {
543 &self.basedir
544 }
545
546 fn has_package(&self, package_name: &str) -> bool
547 {
548 let mut path = PathBuf::from(&self.basedir);
549 let mut zip_name = String::from(package_name);
550 zip_name.push_str(&self.extension);
551 path.push(zip_name);
552
553 path.exists()
554 }
555
556 fn list_packages(&mut self) -> Vec<String>
557 {
558 if let Some(package_list) = &self.package_list
559 {
560 return package_list.clone();
561 }
562
563 let mut package_list: Vec<String> = Vec::new();
564 let path = PathBuf::from(&self.basedir);
565
566 let walkdir = WalkDir::new(path).min_depth(1).max_depth(1).into_iter();
567
568 for dir_entry in walkdir
569 {
570 if let Ok(entry) = dir_entry
571 {
572 if !entry.file_type().is_file()
573 {
574 continue;
575 }
576
577 if let Some(stripped) = entry.file_name().to_string_lossy()
578 .strip_suffix(&self.extension)
579 {
580 package_list.push(stripped.to_string());
581 }
582 }
583 }
584
585 package_list.sort();
586
587 self.package_list = Some(package_list.clone());
588
589 package_list
590 }
591
592 fn read_file<'a>(&'a mut self, package_name: &str, pathname: &str) -> Result<Box<dyn ReadSeek + 'a>, PackageError>
593 {
594 self.load_zip(package_name)?;
595
596 if let Some(archive) = self.loaded_zips.get_mut(package_name)
597 {
598 match archive.by_name(pathname)
599 {
600 Ok(reader) => { return Ok(Box::new(FakeSeekReader::new(reader))); }
601 Err(error) => { return Err(package_error_for_zip_error(error)); }
602 }
603 }
604
605 Err(PackageError::IoError(std::io::Error::new(std::io::ErrorKind::NotFound, "Archive not found")))
606 }
607
608 fn iter_entries<'a>(&'a mut self, package_name: &str, type_folder: &str) -> Box<dyn Iterator<Item=Result<String, PackageError>> + 'a>
609 {
610 if self.load_zip(package_name).is_err()
611 {
612 return Box::new(EmptyEntryIter::new());
613 }
614
615 let mut iter : Box<dyn Iterator<Item=Result<String, PackageError>>> = Box::new(EmptyEntryIter::new());
616
617 if let Some(archive) = self.loaded_zips.get(package_name)
618 {
619 let archive_iter = archive.file_names();
620
621 iter = Box::new(ZipFolderIter::new(archive_iter, type_folder));
622 }
623
624 iter
625 }
626
627 fn trust_level(&self, _package_name: &str) -> TrustLevel
628 {
629 self.trust
630 }
631}