DirWriterLister now requires an ExactSizeIterator.

`DirWriter::write` now doesn't collect all entries initially.
This commit is contained in:
Filipe Rodrigues 2021-05-27 22:57:53 +01:00
parent 136af25359
commit 252330391b
7 changed files with 61 additions and 35 deletions

View File

@ -1,6 +1,6 @@
#![doc(include = "lib.md")]
// Features
#![feature(external_doc, seek_stream_len, try_blocks)]
#![feature(external_doc, seek_stream_len, try_blocks, associated_type_bounds)]
// Lints
#![warn(clippy::restriction, clippy::pedantic, clippy::nursery)]
// We'll disable the ones we don't need

View File

@ -10,7 +10,9 @@ use std::{
};
/// A directory lister
pub trait DirWriterLister: Sized + IntoIterator<Item = Result<DirEntryWriter<Self>, Self::Error>> {
pub trait DirWriterLister:
Sized + IntoIterator<Item = Result<DirEntryWriter<Self>, Self::Error>, IntoIter: ExactSizeIterator>
{
/// File type
type FileReader: io::Read;
@ -69,11 +71,7 @@ impl<L: DirWriterLister> DirWriter<L> {
// Note: We on the directory after this directory.
// Note: `+1` for the null entry.
// Note: `+2047` is to pad this directory to the next sector, if not empty.
let entries: Vec<DirEntryWriter<L>> = self
.entries
.into_iter()
.collect::<Result<_, _>>()
.map_err(WriteDirError::GetEntry)?;
let entries = self.entries.into_iter();
let entries_len: u32 = entries
.len()
.try_into()
@ -82,8 +80,10 @@ impl<L: DirWriterLister> DirWriter<L> {
// Write each entry and map it so we can write it later
let entries: Vec<_> = entries
.into_iter()
.map(|entry| {
// Get the entry
let entry = entry.map_err(WriteDirError::GetEntry)?;
// Seek to the entry
writer
.seek(SeekFrom::Start(u64::from(cur_sector_pos) * 2048))

View File

@ -12,6 +12,8 @@ dcb-drv = { path = "../../dcb-drv" }
# Util
filetime = "0.2.14"
chrono = "0.4.19"
size_format = "1.0.2"
itertools = "0.10.0"
# Cmd
clap = "2.33.3"

View File

@ -54,12 +54,9 @@ impl CliData {
let output_file = match matches.value_of("OUTPUT") {
Some(output) => PathBuf::from(output),
None => {
let extension = match input_dir.extension() {
Some(extension) => format!("{}.drv", extension.to_string_lossy()),
None => "drv".to_string(),
};
input_dir.with_extension(extension)
let mut path = input_dir.clone().into_os_string();
path.push(".drv");
PathBuf::from(path)
},
};

View File

@ -3,15 +3,17 @@
// Modules
pub mod error;
use dcb_drv::{DirEntryWriter, DirEntryWriterKind, DirWriter, DirWriterLister};
// Exports
pub use error::{NewError, NextError, ReadEntryError};
// Imports
use dcb_drv::{DirEntryWriter, DirEntryWriterKind, DirWriter, DirWriterLister};
use itertools::{Itertools, Position};
use std::{
cmp::Ordering,
convert::{TryFrom, TryInto},
fs,
io::Seek,
path::{Path, PathBuf},
time::SystemTime,
};
@ -21,6 +23,9 @@ use std::{
pub struct DirLister {
/// All entries
entries: Vec<DirEntry>,
/// Depth
depth: usize,
}
/// Directory entry
@ -35,7 +40,7 @@ pub struct DirEntry {
impl DirLister {
/// Creates a new iterator from a path
pub fn new(path: &Path) -> Result<Self, NewError> {
pub fn new(path: &Path, depth: usize) -> Result<Self, NewError> {
// Read the directory entries
let mut entries = fs::read_dir(path)
.map_err(|err| NewError::ReadDir(path.to_path_buf(), err))?
@ -49,11 +54,6 @@ impl DirLister {
.collect::<Result<Vec<_>, _>>()
.map_err(|err| NewError::ReadEntries(path.to_path_buf(), err))?;
// If there are too many entries, return Err
if u32::try_from(entries.len()).is_err() {
return Err(NewError::TooManyEntries);
}
// Then sort them by type and then name
entries.sort_by(|lhs, rhs| {
// Get if they're a directory
@ -71,7 +71,7 @@ impl DirLister {
lhs.path.file_name().cmp(&rhs.path.file_name())
});
Ok(Self { entries })
Ok(Self { entries, depth })
}
}
@ -83,10 +83,18 @@ impl DirWriterLister for DirLister {
impl IntoIterator for DirLister {
type Item = Result<DirEntryWriter<Self>, <Self as DirWriterLister>::Error>;
type IntoIter = impl Iterator<Item = Self::Item>;
type IntoIter = impl Iterator<Item = Self::Item> + ExactSizeIterator;
fn into_iter(self) -> Self::IntoIter {
self.entries.into_iter().map(|entry| {
let depth = self.depth;
self.entries.into_iter().with_position().map(move |entry| {
let (entry, is_last) = {
match entry {
Position::First(entry) | Position::Middle(entry) => (entry, false),
Position::Last(entry) | Position::Only(entry) => (entry, true),
}
};
// Read the entry and it's metadata
let name = entry
.path
@ -109,20 +117,43 @@ impl IntoIterator for DirLister {
// Check if it's a directory or file
let kind = match entry.metadata.is_dir() {
false => {
let reader = fs::File::open(&entry.path).map_err(NextError::OpenFile)?;
let mut reader = fs::File::open(&entry.path).map_err(NextError::OpenFile)?;
let extension = entry
.path
.extension()
.ok_or(NextError::NoFileExtension)?
.try_into()
.map_err(NextError::InvalidFileExtension)?;
let size = reader.stream_len().ok();
println!("{}", entry.path.display());
let prefix = dcb_util::DisplayWrapper::new(|f| {
match depth {
0 => (),
_ => {
for _ in 0..(depth - 1) {
write!(f, "")?;
}
match is_last {
true => write!(f, "└──")?,
false => write!(f, "├──")?,
};
},
}
Ok(())
});
let size = dcb_util::DisplayWrapper::new(|f| match size {
Some(size) => write!(f, "{}B", size_format::SizeFormatterSI::new(size)),
None => write!(f, "Unknown Size"),
});
println!("{}{} ({})", prefix, name, size,);
DirEntryWriterKind::File { extension, reader }
},
true => {
let entries = Self::new(&entry.path).map_err(NextError::OpenDir)?;
let entries = Self::new(&entry.path, depth + 1).map_err(NextError::OpenDir)?;
println!("{} ({} entries)", entry.path.display(), entries.entries.len());

View File

@ -13,10 +13,6 @@ pub enum NewError {
/// Unable to read entry
#[error("Unable to read entry in {}", _0.display())]
ReadEntries(PathBuf, #[source] ReadEntryError),
/// Too many entries in directory
#[error("Too many entries in directory")]
TooManyEntries,
}
/// Error for [`DirList::new`](super::DirLister::new)'s entry reading

View File

@ -26,19 +26,19 @@ fn main() -> Result<(), anyhow::Error> {
let cli::CliData { input_dir, output_file } = cli::CliData::new();
// Try to pack the filesystem
self::pack_filesystem(&input_dir, &output_file).context("Unable to pack `drv` file")?;
self::write_fs(&input_dir, &output_file).context("Unable to pack `drv` file")?;
Ok(())
}
/// Extracts a `.drv` file to `output_dir`.
fn pack_filesystem(input_dir: &Path, output_file: &Path) -> Result<(), anyhow::Error> {
/// Writes a `.drv` filesystem to `output_file`.
pub fn write_fs(input_dir: &Path, output_file: &Path) -> Result<(), anyhow::Error> {
// Create the output file
let mut output_file = fs::File::create(output_file).context("Unable to create output file")?;
// Create the filesystem writer
let root_entries =
dir_lister::DirLister::new(input_dir).context("Unable to create new dir lister for root directory")?;
dir_lister::DirLister::new(input_dir, 0).context("Unable to create new dir lister for root directory")?;
DirWriter::new(root_entries)
.write(DirPtr::root(), &mut output_file)
.context("Unable to write filesystem")?;