1use include_dir::{include_dir, Dir};
2use std::borrow::Cow;
3use std::fs;
4use std::io;
5use std::path::PathBuf;
6
7static ARCH_DIR: Dir<'_> = include_dir!("$CARGO_MANIFEST_DIR/arch");
8
9#[derive(Debug)]
10pub(crate) enum ResourceDir {
11 Embedded(&'static Dir<'static>),
12 Filesystem(PathBuf),
13}
14
15impl Default for ResourceDir {
16 fn default() -> Self {
17 ResourceDir::Embedded(&ARCH_DIR)
18 }
19}
20
21#[derive(Debug)]
22pub(crate) enum ResourceFile {
23 Embedded(&'static include_dir::File<'static>),
24 Filesystem { name: String, full_path: PathBuf },
25}
26
27impl ResourceFile {
28 pub fn path(&self) -> &str {
29 match self {
30 ResourceFile::Embedded(file) => file.path().to_str().unwrap(),
31 ResourceFile::Filesystem { name, .. } => name,
32 }
33 }
34
35 pub fn read(&self) -> io::Result<Cow<'static, [u8]>> {
36 match self {
37 ResourceFile::Embedded(file) => Ok(Cow::Borrowed(file.contents())),
38 ResourceFile::Filesystem { full_path, .. } => {
39 let contents = fs::read(full_path)?;
40 Ok(Cow::Owned(contents))
41 }
42 }
43 }
44}
45
46impl ResourceDir {
47 pub(crate) fn new_filesystem(path: PathBuf) -> Self {
48 ResourceDir::Filesystem(path)
49 }
50
51 pub(crate) fn get_file(&self, path: &str) -> io::Result<ResourceFile> {
52 match self {
53 ResourceDir::Embedded(dir) => {
54 let full_path = dir.path().join(path);
55 let file = ARCH_DIR
56 .get_file(full_path.to_str().unwrap())
57 .ok_or_else(|| {
58 io::Error::new(
59 io::ErrorKind::NotFound,
60 "file not found in embedded resources",
61 )
62 })?;
63 Ok(ResourceFile::Embedded(file))
64 }
65 ResourceDir::Filesystem(base_path) => {
66 let full_path = base_path.join(path);
67 if full_path.is_file() {
68 let name = path.to_string();
69 Ok(ResourceFile::Filesystem { name, full_path })
70 } else {
71 Err(io::Error::new(
72 io::ErrorKind::NotFound,
73 format!("file not found: {}", full_path.display()),
74 ))
75 }
76 }
77 }
78 }
79
80 pub(crate) fn get_dir(&self, path: &str) -> io::Result<ResourceDir> {
81 match self {
82 ResourceDir::Embedded(dir) => {
83 let full_path = dir.path().join(path);
84 let subdir = ARCH_DIR
85 .get_dir(full_path.to_str().unwrap())
86 .ok_or_else(|| {
87 io::Error::new(
88 io::ErrorKind::NotFound,
89 "directory not found in embedded resources",
90 )
91 })?;
92 Ok(ResourceDir::Embedded(subdir))
93 }
94 ResourceDir::Filesystem(base_path) => {
95 let full_path = base_path.join(path);
96 if full_path.is_dir() {
97 Ok(ResourceDir::Filesystem(full_path))
98 } else {
99 Err(io::Error::new(
100 io::ErrorKind::NotFound,
101 format!("directory not found: {}", full_path.display()),
102 ))
103 }
104 }
105 }
106 }
107
108 pub(crate) fn files(&self) -> io::Result<Vec<ResourceFile>> {
109 match self {
110 ResourceDir::Embedded(dir) => {
111 let mut files = Vec::new();
112 for file in dir.files() {
113 files.push(ResourceFile::Embedded(file));
114 }
115 Ok(files)
116 }
117 ResourceDir::Filesystem(base_path) => {
118 let mut files = Vec::new();
119 for entry in fs::read_dir(base_path)? {
120 let entry = entry?;
121 let path = entry.path();
122 if path.is_file() {
123 let name = path.file_name().unwrap().to_str().unwrap().to_string();
124 files.push(ResourceFile::Filesystem {
125 name,
126 full_path: path,
127 });
128 }
129 }
130 Ok(files)
131 }
132 }
133 }
134}
135
136#[cfg(test)]
137pub(crate) fn extract_arch_resources(path: &std::path::Path) -> Result<(), std::io::Error> {
138 ARCH_DIR.extract(path)
139}