includedir/
lib.rs

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
21/// Runtime access to the included files
22pub struct Files {
23    // Do not access these fields, they are only public to allow for code generation!
24    #[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    /// Returns an iterator over all available file names.  Does not
50    /// decompress any compressed data.
51    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
103/// Iterates over the file names available for `Files` object.
104pub struct FileNames {
105    // Our internal iterator.  We wrap this in a nice struct so our
106    // caller doesn't need to know the details.
107    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}