use std::{fmt, str};
#[cfg(feature = "std")]
use std::{error, path::Path};
use super::{Resources, Directory, Entry, Name, DataEntry};
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum FindError {
Pe(crate::Error),
Bad8Path,
NotFound,
NoRootPath,
UnDataEntry,
UnDirectory,
}
impl FindError {
pub fn to_str(self) -> &'static str {
match self {
FindError::Pe(err) => err.to_str(),
FindError::Bad8Path => "invalid utf8 path",
FindError::NotFound => "entry not found",
FindError::NoRootPath => "missing '/' root",
FindError::UnDataEntry => "unexpected data entry",
FindError::UnDirectory => "unexpected directory",
}
}
}
impl From<crate::Error> for FindError {
fn from(err: crate::Error) -> FindError {
FindError::Pe(err)
}
}
impl From<str::Utf8Error> for FindError {
fn from(_err: str::Utf8Error) -> FindError {
FindError::Pe(crate::Error::Encoding)
}
}
impl fmt::Display for FindError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.to_str().fmt(f)
}
}
#[cfg(feature = "std")]
impl error::Error for FindError {
fn description(&self) -> &str {
self.to_str()
}
fn cause(&self) -> Option<&dyn error::Error> {
self.source()
}
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
match self {
FindError::Pe(err) => Some(err),
_ => None,
}
}
}
impl<'a> Resources<'a> {
pub fn find_resource(&self, path: &[Name<'_>; 2]) -> Result<&'a [u8], FindError> {
Ok(self.root()?.get_dir(path[0])?.get_dir(path[1])?.first_data()?.bytes()?)
}
pub fn find_resources(&self, path: &[Name<'_>; 2]) -> Result<Directory<'a>, FindError> {
self.root()?.get_dir(path[0])?.get_dir(path[1])
}
pub fn find_resource_ex(&self, path: &[Name<'_>; 3]) -> Result<&'a [u8], FindError> {
Ok(self.root()?.get_dir(path[0])?.get_dir(path[1])?.get_data(path[2])?.bytes()?)
}
pub fn version_info(&self) -> Result<super::version_info::VersionInfo<'a>, FindError> {
let bytes = self.find_resource(&[Name::VERSION, Name::Id(1)])?;
let version_info = super::version_info::VersionInfo::try_from(bytes)?;
Ok(version_info)
}
pub fn manifest(&self) -> Result<&'a str, FindError> {
let bytes = self.root()?.get_dir(Name::MANIFEST)?.first_dir()?.first_data()?.bytes()?;
let manifest = str::from_utf8(bytes)?;
Ok(manifest)
}
pub fn icons(&self) -> impl 'a + Iterator<Item = Result<(Name<'a>, super::group::GroupIcon<'a>), FindError>> + Clone {
let resources = *self;
let icons = self.root().map_err(FindError::Pe)
.and_then(|root| root.get_dir(Name::GROUP_ICON));
icons.into_iter().flat_map(move |icons| icons.entries().map(move |de| {
let name = de.name()?;
let bytes = de.entry()?.dir().ok_or(FindError::UnDataEntry)?.first_data()?.bytes()?;
let group_icon = super::group::GroupIcon::new(resources, bytes)?;
Ok((name, group_icon))
}))
}
pub fn cursors(&self) -> impl 'a + Iterator<Item = Result<(Name<'a>, super::group::GroupCursor<'a>), FindError>> + Clone {
let resources = *self;
let cursors = self.root().map_err(FindError::Pe)
.and_then(|root| root.get_dir(Name::GROUP_CURSOR));
cursors.into_iter().flat_map(move |cursors| cursors.entries().map(move |de| {
let name = de.name()?;
let bytes = de.entry()?.dir().ok_or(FindError::UnDataEntry)?.first_data()?.bytes()?;
let group_cursor = super::group::GroupCursor::new(resources, bytes)?;
Ok((name, group_cursor))
}))
}
}
impl<'a> Directory<'a> {
pub fn get(&self, name: Name<'_>) -> Result<Entry<'a>, FindError> {
self.entries().find(|de| de.name() == Ok(name)).ok_or(FindError::NotFound)?.entry().map_err(FindError::Pe)
}
pub fn get_data(&self, name: Name<'_>) -> Result<DataEntry<'a>, FindError> {
self.entries().find(|de| de.name() == Ok(name)).ok_or(FindError::NotFound)?.entry()?.data().ok_or(FindError::UnDirectory)
}
pub fn get_dir(&self, name: Name<'_>) -> Result<Directory<'a>, FindError> {
self.entries().find(|de| de.name() == Ok(name)).ok_or(FindError::NotFound)?.entry()?.dir().ok_or(FindError::UnDataEntry)
}
pub fn first(&self) -> Result<Entry<'a>, FindError> {
self.entries().next().ok_or(FindError::NotFound)?.entry().map_err(FindError::Pe)
}
pub fn first_data(&self) -> Result<DataEntry<'a>, FindError> {
self.entries().next().ok_or(FindError::NotFound)?.entry()?.data().ok_or(FindError::UnDirectory)
}
pub fn first_dir(&self) -> Result<Directory<'a>, FindError> {
self.entries().next().ok_or(FindError::NotFound)?.entry()?.dir().ok_or(FindError::UnDataEntry)
}
}
#[cfg(feature = "std")]
impl<'a> Resources<'a> {
pub fn find<P: AsRef<Path> + ?Sized>(&self, path: &P) -> Result<Entry<'a>, FindError> {
self.find_internal(path.as_ref())
}
pub fn find_data<P: AsRef<Path> + ?Sized>(&self, path: &P) -> Result<DataEntry<'a>, FindError> {
self.find(path).and_then(|e| e.data().ok_or(FindError::UnDirectory))
}
pub fn find_dir<P: AsRef<Path> + ?Sized>(&self, path: &P) -> Result<Directory<'a>, FindError> {
self.find(path).and_then(|e| e.dir().ok_or(FindError::UnDataEntry))
}
fn find_internal(&self, path: &Path) -> Result<Entry<'a>, FindError> {
let mut iter = path.iter();
if let Some(slash) = iter.next() {
if slash != "/" && slash != "\\" {
Err(FindError::NoRootPath)
}
else {
(*self).root()?.find_internal(iter.as_path())
}
}
else {
Err(FindError::NotFound)
}
}
}
#[cfg(feature = "std")]
impl<'a> Directory<'a> {
pub fn find<P: AsRef<Path> + ?Sized>(&self, path: &P) -> Result<Entry<'a>, FindError> {
self.find_internal(path.as_ref())
}
pub fn find_data<P: AsRef<Path> + ?Sized>(&self, path: &P) -> Result<DataEntry<'a>, FindError> {
self.find(path).and_then(|e| e.data().ok_or(FindError::UnDirectory))
}
pub fn find_dir<P: AsRef<Path> + ?Sized>(&self, path: &P) -> Result<Directory<'a>, FindError> {
self.find(path).and_then(|e| e.dir().ok_or(FindError::UnDataEntry))
}
fn find_internal(&self, path: &Path) -> Result<Entry<'a>, FindError> {
let mut entry = Entry::Directory(*self);
'parts: for part in path {
let name = Name::Str(part.to_str().ok_or(FindError::Bad8Path)?);
match entry {
Entry::Directory(dir) => {
for child in dir.entries() {
if child.name() == Ok(name) {
entry = child.entry()?;
continue 'parts;
}
}
return Err(FindError::NotFound);
},
Entry::DataEntry(_) => {
return Err(FindError::UnDataEntry);
},
};
}
Ok(entry)
}
}