use crate::cluster::ClustersReader;
use crate::entries::{ClusterAllocation, EntriesReader, EntryType, FileEntry, StreamEntry};
use crate::file::File;
use crate::image::Image;
use std::io::{Read, Seek};
use std::ops::DerefMut;
use std::sync::Arc;
use thiserror::Error;
pub struct Directory<I: Read + Seek> {
image: Arc<Image<I>>,
name: String,
stream: StreamEntry,
}
impl<I: Read + Seek> Directory<I> {
pub(crate) fn new(image: Arc<Image<I>>, name: String, stream: StreamEntry) -> Self {
Self {
image,
name,
stream,
}
}
pub fn name(&self) -> &str {
self.name.as_ref()
}
pub fn open(&self) -> Result<Vec<Item<I>>, OpenError> {
let params = self.image.params();
let fat = self.image.fat();
let mut image = self.image.reader();
let alloc = self.stream.allocation();
let mut reader = match ClustersReader::new(
params,
fat,
image.deref_mut(),
alloc.first_cluster(),
Some(alloc.data_length()),
Some(self.stream.no_fat_chain()),
) {
Ok(v) => EntriesReader::new(v),
Err(e) => return Err(OpenError::CreateClustersReaderFailed(alloc.clone(), e)),
};
let mut items: Vec<Item<I>> = Vec::new();
loop {
let entry = match reader.read() {
Ok(v) => v,
Err(e) => return Err(OpenError::ReadEntryFailed(e)),
};
let ty = entry.ty();
if !ty.is_regular() {
break;
} else if ty.type_category() != EntryType::PRIMARY {
return Err(OpenError::NotPrimaryEntry(entry.index(), entry.cluster()));
} else if ty.type_importance() != EntryType::CRITICAL || ty.type_code() != 5 {
return Err(OpenError::NotFileEntry(entry.index(), entry.cluster()));
}
let file = match FileEntry::load(entry, &mut reader) {
Ok(v) => v,
Err(e) => return Err(OpenError::LoadFileEntryFailed(e)),
};
let name = file.name;
let attrs = file.attributes;
let stream = file.stream;
let item = if attrs.is_directory() {
Item::Directory(Directory::new(self.image.clone(), name, stream))
} else {
Item::File(File::new(self.image.clone(), name, stream))
};
items.push(item);
}
Ok(items)
}
}
pub enum Item<I: Read + Seek> {
Directory(Directory<I>),
File(File<I>),
}
#[derive(Debug, Error)]
pub enum OpenError {
#[error("cannot create a clusters reader for allocation {0}")]
CreateClustersReaderFailed(ClusterAllocation, #[source] crate::cluster::NewError),
#[error("cannot read an entry")]
ReadEntryFailed(#[source] crate::entries::ReaderError),
#[error("entry #{0} on cluster #{1} is not a primary entry")]
NotPrimaryEntry(usize, usize),
#[error("entry #{0} on cluster #{1} is not a file entry")]
NotFileEntry(usize, usize),
#[error("cannot load file entry")]
LoadFileEntryFailed(#[source] crate::entries::FileEntryError),
}