trillium_include_dir/
dir.rs

1use crate::file::File;
2use std::fs;
3use std::io::Write;
4use std::path::Path;
5use std::time::{Duration, SystemTime, UNIX_EPOCH};
6
7/// A directory entry.
8#[derive(Debug, Copy, Clone, PartialEq)]
9pub struct Dir<'a> {
10    #[doc(hidden)]
11    pub path: &'a str,
12
13    #[doc(hidden)]
14    pub files: &'a [File<'a>],
15
16    #[doc(hidden)]
17    pub dirs: &'a [Dir<'a>],
18
19    #[doc(hidden)]
20    pub modified: Option<f64>,
21
22    #[doc(hidden)]
23    pub created: Option<f64>,
24
25    #[doc(hidden)]
26    pub accessed: Option<f64>,
27}
28
29impl<'a> Dir<'a> {
30    /// Get the directory's path.
31    pub fn path(&self) -> &'a Path {
32        Path::new(self.path)
33    }
34
35    /// Get a list of the files in this directory.
36    pub fn files(&self) -> &'a [File<'a>] {
37        self.files
38    }
39
40    /// Get a list of the sub-directories inside this directory.
41    pub fn dirs(&self) -> &'a [Dir<'a>] {
42        self.dirs
43    }
44
45    /// Does this directory contain `path`?
46    pub fn contains<S: AsRef<Path>>(&self, path: S) -> bool {
47        let path = path.as_ref();
48
49        self.get_file(path).is_some() || self.get_dir(path).is_some()
50    }
51
52    /// Fetch a sub-directory by *exactly* matching its path relative to the
53    /// directory included with `include_dir!()`.
54    pub fn get_dir<S: AsRef<Path>>(&self, path: S) -> Option<Dir<'a>> {
55        let path = path.as_ref();
56
57        for dir in self.dirs {
58            if Path::new(dir.path) == path {
59                return Some(*dir);
60            }
61
62            if let Some(d) = dir.get_dir(path) {
63                return Some(d);
64            }
65        }
66
67        None
68    }
69
70    /// Fetch a sub-directory by *exactly* matching its path relative to the
71    /// directory included with `include_dir!()`.
72    pub fn get_file<S: AsRef<Path>>(&self, path: S) -> Option<File<'a>> {
73        let path = path.as_ref();
74
75        for file in self.files {
76            if Path::new(file.path) == path {
77                return Some(*file);
78            }
79        }
80
81        for dir in self.dirs {
82            if let Some(d) = dir.get_file(path) {
83                return Some(d);
84            }
85        }
86
87        None
88    }
89
90    /// Create directories and extract all files to real filesystem.
91    /// Creates parent directories of `path` if they do not already exist.
92    /// Fails if some files already exist.
93    /// In case of error, partially extracted directory may remain on the filesystem.
94    pub fn extract<S: AsRef<Path>>(&self, path: S) -> std::io::Result<()> {
95        // Extracts the given directory entry to the given path
96        // We use this internally for recursing on subdirectories
97        fn extract_dir<S: AsRef<Path>>(dir: Dir<'_>, path: S) -> std::io::Result<()> {
98            let path = path.as_ref();
99            // Create all the subdirectories in here (but not their files yet)
100            for dir in dir.dirs() {
101                fs::create_dir_all(path.join(dir.path()))?;
102                extract_dir(*dir, path)?;
103            }
104
105            // Only write files at the root of this directory (we recurse on subdirectories)
106            for file in dir.files() {
107                let mut fsf = fs::OpenOptions::new()
108                    .write(true)
109                    .create_new(true)
110                    .open(path.join(file.path()))?;
111                fsf.write_all(file.contents())?;
112                fsf.sync_all()?;
113            }
114
115            Ok(())
116        }
117
118        extract_dir(*self, path)
119    }
120
121    /// The directory's created timestamp as of compilation time, if
122    /// available
123    pub fn created(&self) -> Option<SystemTime> {
124        self.created
125            .map(|secs| UNIX_EPOCH + Duration::from_secs_f64(secs))
126    }
127
128    /// The directory's last modified timestamp as of compilation
129    /// time, if available
130    pub fn modified(&self) -> Option<SystemTime> {
131        self.modified
132            .map(|secs| UNIX_EPOCH + Duration::from_secs_f64(secs))
133    }
134
135    /// The directory's last accessed timestamp as of compilation
136    /// time, if available
137    pub fn accessed(&self) -> Option<SystemTime> {
138        self.accessed
139            .map(|secs| UNIX_EPOCH + Duration::from_secs_f64(secs))
140    }
141}