1extern crate phf;
2
3#[cfg(feature = "flate2")]
4extern crate flate2;
5
6use std::borrow::{Borrow, Cow};
7use std::io::{self, BufReader, Cursor, Error, ErrorKind, Read};
8use std::fs::File;
9use std::sync::atomic::{AtomicBool, Ordering};
10
11#[cfg(feature = "flate2")]
12use flate2::bufread::GzDecoder;
13
14#[derive(Debug, Clone, Copy, PartialEq, Eq)]
15pub enum Compression {
16 None,
17 #[cfg(feature = "flate2")]
18 Gzip
19}
20
21pub struct Files {
23 #[doc(hidden)]
25 pub files: phf::Map<&'static str, (Compression, &'static [u8])>,
26 #[doc(hidden)]
27 pub passthrough: AtomicBool
28}
29
30#[cfg(windows)]
31fn as_key(path: &str) -> Cow<str> {
32 Cow::Owned(path.replace("\\", "/"))
33}
34
35#[cfg(not(windows))]
36fn as_key(path: &str) -> Cow<str> {
37 Cow::Borrowed(path)
38}
39
40impl Files {
41 pub fn set_passthrough(&self, enabled: bool) {
42 self.passthrough.store(enabled, Ordering::Relaxed);
43 }
44
45 pub fn is_available(&self, path: &str) -> bool {
46 self.files.contains_key(path)
47 }
48
49 pub fn file_names(&'static self) -> FileNames {
52 FileNames { iter: self.files.keys() }
53 }
54
55 pub fn get(&self, path: &str) -> io::Result<Cow<'static, [u8]>> {
56 match self.get_raw(path) {
57 Ok((Compression::None, data)) => Ok(data),
58 #[cfg(feature = "flate2")]
59 Ok((Compression::Gzip, compressed)) => {
60 let mut r = GzDecoder::new(Cursor::new(compressed));
61 let mut v = Vec::new();
62 r.read_to_end(&mut v)?;
63 Ok(Cow::Owned(v))
64 },
65 Err(e) => Err(e)
66 }
67 }
68
69 pub fn get_raw(&self, path: &str) -> io::Result<(Compression, Cow<'static, [u8]>)> {
70 if self.passthrough.load(Ordering::Relaxed) {
71 let mut r = BufReader::new(File::open(path)?);
72 let mut v = Vec::new();
73 r.read_to_end(&mut v)?;
74 return Ok((Compression::None, Cow::Owned(v)))
75 }
76
77 let key = as_key(path);
78
79 self.files.get(&*key)
80 .map(|&(c,d)| (c, Cow::Owned(d.to_owned())))
81 .ok_or_else(|| Error::new(ErrorKind::NotFound, "Key not found"))
82 }
83
84 pub fn read(&self, path: &str) -> io::Result<Box<dyn Read>> {
85 if self.passthrough.load(Ordering::Relaxed) {
86 return Ok(Box::new(BufReader::new(File::open(path)?)))
87 }
88
89 let key = as_key(path);
90 match self.files.get(key.borrow() as &str) {
91 Some(b) => {
92 match b.0 {
93 Compression::None => Ok(Box::new(Cursor::new(b.1))),
94 #[cfg(feature = "flate2")]
95 Compression::Gzip => Ok(Box::new(GzDecoder::new(Cursor::new(b.1)))),
96 }
97 }
98 None => Err(Error::new(ErrorKind::NotFound, "Key not found")),
99 }
100 }
101}
102
103pub struct FileNames {
105 iter: phf::map::Keys<'static, &'static str, (Compression, &'static [u8])>,
108}
109
110impl Iterator for FileNames {
111 type Item = &'static str;
112
113 fn next(&mut self) -> Option<Self::Item> {
114 self.iter.next().cloned()
115 }
116}