include_dir/
dir.rs

1use crate::{file::File, DirEntry};
2use std::fs;
3use std::path::Path;
4
5/// A directory.
6#[derive(Debug, Clone, PartialEq)]
7pub struct Dir<'a> {
8    path: &'a str,
9    entries: &'a [DirEntry<'a>],
10}
11
12impl<'a> Dir<'a> {
13    /// Create a new [`Dir`].
14    pub const fn new(path: &'a str, entries: &'a [DirEntry<'a>]) -> Self {
15        Dir { path, entries }
16    }
17
18    /// The full path for this [`Dir`], relative to the directory passed to
19    /// [`crate::include_dir!()`].
20    pub fn path(&self) -> &'a Path {
21        Path::new(self.path)
22    }
23
24    /// The entries within this [`Dir`].
25    pub const fn entries(&self) -> &'a [DirEntry<'a>] {
26        self.entries
27    }
28
29    /// Get a list of the files in this directory.
30    pub fn files(&self) -> impl Iterator<Item = &'a File<'a>> + 'a {
31        self.entries().iter().filter_map(DirEntry::as_file)
32    }
33
34    /// Get a list of the sub-directories inside this directory.
35    pub fn dirs(&self) -> impl Iterator<Item = &'a Dir<'a>> + 'a {
36        self.entries().iter().filter_map(DirEntry::as_dir)
37    }
38
39    /// Recursively search for a [`DirEntry`] with a particular path.
40    pub fn get_entry<S: AsRef<Path>>(&self, path: S) -> Option<&'a DirEntry<'a>> {
41        let path = path.as_ref();
42
43        for entry in self.entries() {
44            if entry.path() == path {
45                return Some(entry);
46            }
47
48            if let DirEntry::Dir(d) = entry {
49                if let Some(nested) = d.get_entry(path) {
50                    return Some(nested);
51                }
52            }
53        }
54
55        None
56    }
57
58    /// Look up a file by name.
59    pub fn get_file<S: AsRef<Path>>(&self, path: S) -> Option<&'a File<'a>> {
60        self.get_entry(path).and_then(DirEntry::as_file)
61    }
62
63    /// Look up a dir by name.
64    pub fn get_dir<S: AsRef<Path>>(&self, path: S) -> Option<&'a Dir<'a>> {
65        self.get_entry(path).and_then(DirEntry::as_dir)
66    }
67
68    /// Does this directory contain `path`?
69    pub fn contains<S: AsRef<Path>>(&self, path: S) -> bool {
70        self.get_entry(path).is_some()
71    }
72
73    /// Create directories and extract all files to real filesystem.
74    /// Creates parent directories of `path` if they do not already exist.
75    /// Fails if some files already exist.
76    /// In case of error, partially extracted directory may remain on the filesystem.
77    pub fn extract<S: AsRef<Path>>(&self, base_path: S) -> std::io::Result<()> {
78        let base_path = base_path.as_ref();
79
80        for entry in self.entries() {
81            let path = base_path.join(entry.path());
82
83            match entry {
84                DirEntry::Dir(d) => {
85                    fs::create_dir_all(&path)?;
86                    d.extract(base_path)?;
87                }
88                DirEntry::File(f) => {
89                    fs::write(path, f.contents())?;
90                }
91            }
92        }
93
94        Ok(())
95    }
96}