1use std::{fmt, str};
6
7#[cfg(feature = "std")]
8use std::{error, path::Path};
9
10use super::{Resources, Directory, Entry, Name, DataEntry};
11
12#[derive(Copy, Clone, Debug, Eq, PartialEq)]
16pub enum FindError {
17 Pe(crate::Error),
21 Bad8Path,
27 NotFound,
29 NoRootPath,
31 UnDataEntry,
35 UnDirectory,
37}
38impl FindError {
39 pub fn to_str(self) -> &'static str {
41 match self {
42 FindError::Pe(err) => err.to_str(),
43 FindError::Bad8Path => "invalid utf8 path",
44 FindError::NotFound => "entry not found",
45 FindError::NoRootPath => "missing '/' root",
46 FindError::UnDataEntry => "unexpected data entry",
47 FindError::UnDirectory => "unexpected directory",
48 }
49 }
50}
51impl From<crate::Error> for FindError {
52 fn from(err: crate::Error) -> FindError {
53 FindError::Pe(err)
54 }
55}
56impl From<str::Utf8Error> for FindError {
57 fn from(_err: str::Utf8Error) -> FindError {
58 FindError::Pe(crate::Error::Encoding)
59 }
60}
61impl fmt::Display for FindError {
62 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
63 self.to_str().fmt(f)
64 }
65}
66#[cfg(feature = "std")]
67impl error::Error for FindError {
68 fn description(&self) -> &str {
69 self.to_str()
70 }
71 fn cause(&self) -> Option<&dyn error::Error> {
72 self.source()
73 }
74 fn source(&self) -> Option<&(dyn error::Error + 'static)> {
75 match self {
76 FindError::Pe(err) => Some(err),
77 _ => None,
78 }
79 }
80}
81
82impl<'a> Resources<'a> {
85 pub fn find_resource(&self, path: &[Name<'_>; 2]) -> Result<&'a [u8], FindError> {
87 Ok(self.root()?.get_dir(path[0])?.get_dir(path[1])?.first_data()?.bytes()?)
88 }
89 pub fn find_resources(&self, path: &[Name<'_>; 2]) -> Result<Directory<'a>, FindError> {
91 self.root()?.get_dir(path[0])?.get_dir(path[1])
92 }
93 pub fn find_resource_ex(&self, path: &[Name<'_>; 3]) -> Result<&'a [u8], FindError> {
95 Ok(self.root()?.get_dir(path[0])?.get_dir(path[1])?.get_data(path[2])?.bytes()?)
96 }
97 pub fn version_info(&self) -> Result<super::version_info::VersionInfo<'a>, FindError> {
99 let bytes = self.find_resource(&[Name::VERSION, Name::Id(1)])?;
100 let version_info = super::version_info::VersionInfo::try_from(bytes)?;
101 Ok(version_info)
102 }
103 pub fn manifest(&self) -> Result<&'a str, FindError> {
105 let bytes = self.root()?.get_dir(Name::MANIFEST)?.first_dir()?.first_data()?.bytes()?;
107 let manifest = str::from_utf8(bytes)?;
108 Ok(manifest)
109 }
110 pub fn icons(&self) -> impl 'a + Iterator<Item = Result<(Name<'a>, super::group::GroupIcon<'a>), FindError>> + Clone {
112 let resources = *self;
113 let icons = self.root().map_err(FindError::Pe)
114 .and_then(|root| root.get_dir(Name::GROUP_ICON));
115
116 icons.into_iter().flat_map(move |icons| icons.entries().map(move |de| {
117 let name = de.name()?;
118 let bytes = de.entry()?.dir().ok_or(FindError::UnDataEntry)?.first_data()?.bytes()?;
120 let group_icon = super::group::GroupIcon::new(resources, bytes)?;
121 Ok((name, group_icon))
122 }))
123 }
124 pub fn cursors(&self) -> impl 'a + Iterator<Item = Result<(Name<'a>, super::group::GroupCursor<'a>), FindError>> + Clone {
126 let resources = *self;
127 let cursors = self.root().map_err(FindError::Pe)
128 .and_then(|root| root.get_dir(Name::GROUP_CURSOR));
129
130 cursors.into_iter().flat_map(move |cursors| cursors.entries().map(move |de| {
131 let name = de.name()?;
132 let bytes = de.entry()?.dir().ok_or(FindError::UnDataEntry)?.first_data()?.bytes()?;
134 let group_cursor = super::group::GroupCursor::new(resources, bytes)?;
135 Ok((name, group_cursor))
136 }))
137 }
138}
139impl<'a> Directory<'a> {
140 pub fn get(&self, name: Name<'_>) -> Result<Entry<'a>, FindError> {
142 self.entries().find(|de| de.name() == Ok(name)).ok_or(FindError::NotFound)?.entry().map_err(FindError::Pe)
143 }
144 pub fn get_data(&self, name: Name<'_>) -> Result<DataEntry<'a>, FindError> {
146 self.entries().find(|de| de.name() == Ok(name)).ok_or(FindError::NotFound)?.entry()?.data().ok_or(FindError::UnDirectory)
147 }
148 pub fn get_dir(&self, name: Name<'_>) -> Result<Directory<'a>, FindError> {
150 self.entries().find(|de| de.name() == Ok(name)).ok_or(FindError::NotFound)?.entry()?.dir().ok_or(FindError::UnDataEntry)
151 }
152 pub fn first(&self) -> Result<Entry<'a>, FindError> {
154 self.entries().next().ok_or(FindError::NotFound)?.entry().map_err(FindError::Pe)
155 }
156 pub fn first_data(&self) -> Result<DataEntry<'a>, FindError> {
158 self.entries().next().ok_or(FindError::NotFound)?.entry()?.data().ok_or(FindError::UnDirectory)
159 }
160 pub fn first_dir(&self) -> Result<Directory<'a>, FindError> {
162 self.entries().next().ok_or(FindError::NotFound)?.entry()?.dir().ok_or(FindError::UnDataEntry)
163 }
164}
165
166#[cfg(feature = "std")]
169impl<'a> Resources<'a> {
170 pub fn find<P: AsRef<Path> + ?Sized>(&self, path: &P) -> Result<Entry<'a>, FindError> {
172 self.find_internal(path.as_ref())
173 }
174 pub fn find_data<P: AsRef<Path> + ?Sized>(&self, path: &P) -> Result<DataEntry<'a>, FindError> {
176 self.find(path).and_then(|e| e.data().ok_or(FindError::UnDirectory))
177 }
178 pub fn find_dir<P: AsRef<Path> + ?Sized>(&self, path: &P) -> Result<Directory<'a>, FindError> {
180 self.find(path).and_then(|e| e.dir().ok_or(FindError::UnDataEntry))
181 }
182 fn find_internal(&self, path: &Path) -> Result<Entry<'a>, FindError> {
183 let mut iter = path.iter();
184 if let Some(slash) = iter.next() {
185 if slash != "/" && slash != "\\" {
187 Err(FindError::NoRootPath)
188 }
189 else {
191 (*self).root()?.find_internal(iter.as_path())
192 }
193 }
194 else {
195 Err(FindError::NotFound)
197 }
198 }
199}
200#[cfg(feature = "std")]
201impl<'a> Directory<'a> {
202 pub fn find<P: AsRef<Path> + ?Sized>(&self, path: &P) -> Result<Entry<'a>, FindError> {
204 self.find_internal(path.as_ref())
205 }
206 pub fn find_data<P: AsRef<Path> + ?Sized>(&self, path: &P) -> Result<DataEntry<'a>, FindError> {
208 self.find(path).and_then(|e| e.data().ok_or(FindError::UnDirectory))
209 }
210 pub fn find_dir<P: AsRef<Path> + ?Sized>(&self, path: &P) -> Result<Directory<'a>, FindError> {
212 self.find(path).and_then(|e| e.dir().ok_or(FindError::UnDataEntry))
213 }
214 fn find_internal(&self, path: &Path) -> Result<Entry<'a>, FindError> {
215 let mut entry = Entry::Directory(*self);
216 'parts: for part in path {
217 let name = Name::Str(part.to_str().ok_or(FindError::Bad8Path)?);
219 match entry {
220 Entry::Directory(dir) => {
221 for child in dir.entries() {
223 if child.name() == Ok(name) {
224 entry = child.entry()?;
225 continue 'parts;
226 }
227 }
228 return Err(FindError::NotFound);
229 },
230 Entry::DataEntry(_) => {
231 return Err(FindError::UnDataEntry);
232 },
233 };
234 }
235 Ok(entry)
236 }
237}