Made DirWriterLister now require the number of entries.

Refactored `drv-packer`.
This commit is contained in:
Filipe Rodrigues 2021-01-25 05:35:25 +00:00
parent 1b3ca80cb3
commit 5fc311ff49
5 changed files with 202 additions and 178 deletions

View File

@ -31,10 +31,10 @@ pub struct DrvFsWriter;
impl DrvFsWriter {
/// Creates a `.DRV` filesystem
pub fn write_fs<W: io::Write + io::Seek, L: DirWriterLister>(
writer: &mut W, root_entries: L, root_entries_len: u32,
writer: &mut W, root_entries: L,
) -> Result<(), WriteFsError<L::Error>> {
// Get the root and write it
let root = DirWriter::new(root_entries, root_entries_len);
let root = DirWriter::new(root_entries);
root.write_entries(writer).map_err(WriteFsError::RootDir)?;
Ok(())

View File

@ -26,6 +26,9 @@ where
/// Error type for each entry
type Error: std::error::Error + 'static;
/// Returns the number of entries in this lister
fn entries_len(&self) -> u32;
}
/// Directory writer
@ -33,20 +36,17 @@ where
pub struct DirWriter<L: DirWriterLister> {
/// Writer list
entries: L,
/// Number of entries
entries_len: u32,
}
impl<L: DirWriterLister> DirWriter<L> {
/// Creates a new directory writer
pub fn new(entries: L, entries_len: u32) -> Self {
Self { entries, entries_len }
pub fn new(entries: L) -> Self {
Self { entries }
}
/// Returns the number of entries
pub fn entries_len(&self) -> u32 {
self.entries_len
self.entries.entries_len()
}
/// Returns this directory's size

View File

@ -0,0 +1,116 @@
//! Directory lister
// Modules
pub mod error;
// Exports
pub use error::{DirListNewError, NextError};
// Imports
use dcb_io::drv::{dir::entry::DirEntryWriterKind, DirEntryWriter, DirWriter, DirWriterLister, FileWriter};
use std::{
convert::{TryFrom, TryInto},
fs,
io::Seek,
path::Path,
time::SystemTime,
};
/// Directory list
#[derive(Debug)]
pub struct DirLister {
/// Directory read
dir: fs::ReadDir,
/// Number of entries
entries_len: u32,
}
impl DirLister {
/// Creates a new iterator from a path
pub fn new(path: &Path) -> Result<Self, DirListNewError> {
// Get the length
let len = fs::read_dir(path)
.map_err(|err| DirListNewError::ReadDir(path.to_path_buf(), err))?
.count();
let entries_len = u32::try_from(len).map_err(|_err| DirListNewError::TooManyEntries)?;
// And read the directory
let dir = fs::read_dir(path).map_err(|err| DirListNewError::ReadDir(path.to_path_buf(), err))?;
Ok(Self { dir, entries_len })
}
}
impl DirWriterLister for DirLister {
type DirList = Self;
type Error = NextError;
type FileReader = fs::File;
fn entries_len(&self) -> u32 {
self.entries_len
}
}
impl Iterator for DirLister {
type Item = Result<DirEntryWriter<<Self as DirWriterLister>::DirList>, <Self as DirWriterLister>::Error>;
fn next(&mut self) -> Option<Self::Item> {
// Get the next entry
let entry = self.dir.next()?;
// Then read it
let res = try {
// Read the entry and it's metadata
let entry = entry.map_err(NextError::ReadEntry)?;
let metadata = entry.metadata().map_err(NextError::ReadMetadata)?;
let path = entry.path();
let name = path
.file_stem()
.ok_or(NextError::NoEntryName)?
.try_into()
.map_err(NextError::InvalidEntryName)?;
let secs_since_epoch = metadata
.modified()
.map_err(NextError::EntryDate)?
.duration_since(SystemTime::UNIX_EPOCH)
.map_err(NextError::EntryDateSinceEpoch)?
.as_secs();
let date = chrono::NaiveDateTime::from_timestamp(i64::try_from(secs_since_epoch).map_err(|_err| NextError::EntryDateI64Secs)?, 0);
// Check if it's a directory or file
let kind = match metadata.is_file() {
true => {
let mut file = fs::File::open(&path).map_err(NextError::OpenFile)?;
let size = file
.stream_len()
.map_err(NextError::FileSize)?
.try_into()
.map_err(|_err| NextError::FileTooBig)?;
let extension = path
.extension()
.ok_or(NextError::NoFileExtension)?
.try_into()
.map_err(NextError::InvalidFileExtension)?;
log::info!("{} ({} bytes)", path.display(), size);
let file = FileWriter::new(extension, file, size);
DirEntryWriterKind::File(file)
},
false => {
let entries = Self::new(&path).map_err(NextError::OpenDir)?;
log::info!("{} ({} entries)", path.display(), entries.entries_len);
let dir = DirWriter::new(entries);
DirEntryWriterKind::Dir(dir)
},
};
DirEntryWriter::new(name, date, kind)
};
Some(res)
}
}

View File

@ -0,0 +1,73 @@
//! Errors
// Imports
use std::{io, path::PathBuf};
/// Error for [`DirList::new`]
// TODO: Rename
#[derive(Debug, thiserror::Error)]
pub enum DirListNewError {
/// Unable to read directory
#[error("Unable to read directory {}", _0.display())]
ReadDir(PathBuf, #[source] io::Error),
/// Too many entries in directory
#[error("Too many entries in directory")]
TooManyEntries,
}
/// Error for [`Iterator::Item`]
#[derive(Debug, thiserror::Error)]
pub enum NextError {
/// Unable to read entry
#[error("Unable to read entry")]
ReadEntry(#[source] io::Error),
/// Unable to read entry metadata
#[error("Unable to read entry metadata")]
ReadMetadata(#[source] io::Error),
/// Entry had no name
#[error("Entry had no name")]
NoEntryName,
/// Invalid file name
#[error("Invalid file name")]
InvalidEntryName(#[source] dcb_util::ascii_str_arr::FromBytesError<0x10>),
/// File had no file name
#[error("file had no file name")]
NoFileExtension,
/// Invalid extension
#[error("Invalid extension")]
InvalidFileExtension(#[source] dcb_util::ascii_str_arr::FromBytesError<0x3>),
/// Unable to get entry date
#[error("Unable to get entry date")]
EntryDate(#[source] io::Error),
/// Unable to get entry date as time since epoch
#[error("Unable to get entry date as time since epoch")]
EntryDateSinceEpoch(#[source] std::time::SystemTimeError),
/// Unable to get entry date as `i64` seconds since epoch
#[error("Unable to get entry date as `i64` seconds since epoch")]
EntryDateI64Secs,
/// Unable to open file
#[error("Unable to open file")]
OpenFile(#[source] io::Error),
/// Unable to get file size
#[error("Unable to get file size")]
FileSize(#[source] io::Error),
/// File was too big
#[error("File was too big")]
FileTooBig,
/// Unable to open directory
#[error("Unable to open directory")]
OpenDir(#[source] self::DirListNewError),
}

View File

@ -5,18 +5,13 @@
// Modules
mod cli;
mod dir_lister;
mod logger;
// Imports
use anyhow::Context;
use dcb_io::drv::{dir::entry::DirEntryWriterKind, DirEntryWriter, DirWriter, DirWriterLister, DrvFsWriter, FileWriter};
use std::{
convert::{TryFrom, TryInto},
fs,
io::{self, Seek},
path::{Path, PathBuf},
time::SystemTime,
};
use dcb_io::drv::DrvFsWriter;
use std::{fs, path::Path};
fn main() -> Result<(), anyhow::Error> {
@ -38,166 +33,6 @@ fn pack_filesystem(input_dir: &Path, output_file: &Path) -> Result<(), anyhow::E
let mut output_file = fs::File::create(output_file).context("Unable to create output file")?;
// Create the filesystem writer
let (root_entries, root_entries_len) = DirLister::new(input_dir).context("Unable to read root directory")?;
DrvFsWriter::write_fs(&mut output_file, root_entries, root_entries_len).context("Unable to write filesystem")
}
/// Directory list
#[derive(Debug)]
struct DirLister {
/// Directory read
dir: fs::ReadDir,
}
impl DirLister {
/// Creates a new iterator from a path
fn new(path: &Path) -> Result<(Self, u32), DirListNewError> {
// Get the length
let len = fs::read_dir(path)
.map_err(|err| DirListNewError::ReadDir(path.to_path_buf(), err))?
.count();
let len = u32::try_from(len).map_err(|_err| DirListNewError::TooManyEntries)?;
// And read the directory
let dir = fs::read_dir(path).map_err(|err| DirListNewError::ReadDir(path.to_path_buf(), err))?;
Ok((Self { dir }, len))
}
}
/// Error for [`DirList::new`]
#[derive(Debug, thiserror::Error)]
enum DirListNewError {
/// Unable to read directory
#[error("Unable to read directory {}", _0.display())]
ReadDir(PathBuf, #[source] io::Error),
/// Too many entries in directory
#[error("Too many entries in directory")]
TooManyEntries,
}
/// Error for [`Iterator::Item`]
#[derive(Debug, thiserror::Error)]
enum NextError {
/// Unable to read entry
#[error("Unable to read entry")]
ReadEntry(#[source] io::Error),
/// Unable to read entry metadata
#[error("Unable to read entry metadata")]
ReadMetadata(#[source] io::Error),
/// Entry had no name
#[error("Entry had no name")]
NoEntryName,
/// Invalid file name
#[error("Invalid file name")]
InvalidEntryName(#[source] dcb_util::ascii_str_arr::FromBytesError<0x10>),
/// File had no file name
#[error("file had no file name")]
NoFileExtension,
/// Invalid extension
#[error("Invalid extension")]
InvalidFileExtension(#[source] dcb_util::ascii_str_arr::FromBytesError<0x3>),
/// Unable to get entry date
#[error("Unable to get entry date")]
EntryDate(#[source] io::Error),
/// Unable to get entry date as time since epoch
#[error("Unable to get entry date as time since epoch")]
EntryDateSinceEpoch(#[source] std::time::SystemTimeError),
/// Unable to get entry date as `i64` seconds since epoch
#[error("Unable to get entry date as `i64` seconds since epoch")]
EntryDateI64Secs,
/// Unable to open file
#[error("Unable to open file")]
OpenFile(#[source] io::Error),
/// Unable to get file size
#[error("Unable to get file size")]
FileSize(#[source] io::Error),
/// File was too big
#[error("File was too big")]
FileTooBig,
/// Unable to open directory
#[error("Unable to open directory")]
OpenDir(#[source] DirListNewError),
}
impl DirWriterLister for DirLister {
type DirList = Self;
type Error = NextError;
type FileReader = std::fs::File;
}
impl Iterator for DirLister {
type Item = Result<DirEntryWriter<<Self as DirWriterLister>::DirList>, <Self as DirWriterLister>::Error>;
fn next(&mut self) -> Option<Self::Item> {
// Get the next entry
let entry = self.dir.next()?;
// Then read it
let res = try {
// Read the entry and it's metadata
let entry = entry.map_err(NextError::ReadEntry)?;
let metadata = entry.metadata().map_err(NextError::ReadMetadata)?;
let path = entry.path();
let name = path
.file_stem()
.ok_or(NextError::NoEntryName)?
.try_into()
.map_err(NextError::InvalidEntryName)?;
let secs_since_epoch = metadata
.modified()
.map_err(NextError::EntryDate)?
.duration_since(SystemTime::UNIX_EPOCH)
.map_err(NextError::EntryDateSinceEpoch)?
.as_secs();
let date = chrono::NaiveDateTime::from_timestamp(i64::try_from(secs_since_epoch).map_err(|_err| NextError::EntryDateI64Secs)?, 0);
// Check if it's a directory or file
let kind = match metadata.is_file() {
true => {
let mut file = std::fs::File::open(&path).map_err(NextError::OpenFile)?;
let size = file
.stream_len()
.map_err(NextError::FileSize)?
.try_into()
.map_err(|_err| NextError::FileTooBig)?;
let extension = path
.extension()
.ok_or(NextError::NoFileExtension)?
.try_into()
.map_err(NextError::InvalidFileExtension)?;
log::info!("{} ({} bytes)", path.display(), size);
let file = FileWriter::new(extension, file, size);
DirEntryWriterKind::File(file)
},
false => {
let (entries, entries_len) = Self::new(&path).map_err(NextError::OpenDir)?;
log::info!("{} ({} entries)", path.display(), entries_len);
let dir = DirWriter::new(entries, entries_len);
DirEntryWriterKind::Dir(dir)
},
};
DirEntryWriter::new(name, date, kind)
};
Some(res)
}
let root_entries = dir_lister::DirLister::new(input_dir).context("Unable to create new dir lister for root directory")?;
DrvFsWriter::write_fs(&mut output_file, root_entries).context("Unable to write filesystem")
}