Skip to content

Commit

Permalink
fixup! Add object-safe traits for File, Filesystem, Storage
Browse files Browse the repository at this point in the history
Move free functions into trait object impls
  • Loading branch information
robin-nitrokey committed Dec 15, 2023
1 parent fc87937 commit 37f74fe
Showing 1 changed file with 115 additions and 139 deletions.
254 changes: 115 additions & 139 deletions src/object_safe.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,4 @@
//! Object-safe traits for [`File`][], [`Filesystem`][] and [`Storage`][].
//!
//! Methods using callbacks with arbitrary return types cannot be implemented in these traits.
//! They can be replaced with these free helper functions:
//! - [`create_file_and_then`][]
//! - [`open_file_and_then`][]
//! - [`open_file_with_options_and_then`][]
//! - [`read_dir_and_then`][]
//! - [`mount_and_then`][]
//!
//! These helper functions are available for dealing with const generics:
//! - [`read_file`][]
//! - [`read_file_chunk`][]
//! - [`read_file_to_end`][]
use generic_array::typenum::Unsigned as _;
use heapless::Vec;
Expand All @@ -37,13 +24,10 @@ pub type Predicate<'a> = &'a dyn Fn(&DirEntry) -> bool;

/// Object-safe trait for [`File`][].
///
/// The following methods have to be implemented as free functions:
/// - [`read_file_to_end`][]
///
/// The methods for opening files cannot be implemented in this trait. Use these methods instead:
/// - [`DynFilesystem::create_file_and_then`][]
/// - [`DynFilesystem::open_file_and_then`][]
/// - [`DynFilesystem::open_file_with_options_and_then`][]
/// - [`DynFilesystem::create_file_and_then`](trait.DynFilesystem.html#method.create_file_and_then)
/// - [`DynFilesystem::open_file_and_then`](trait.DynFilesystem.html#method.open_file_and_then)
/// - [`DynFilesystem::open_file_with_options_and_then`](trait.DynFilesystem.html#method.open_file_with_options_and_then)
///
/// All other methods are mirrored directly. See the documentation for [`File`][] for more information.
pub trait DynFile: Read + Seek + Write {
Expand Down Expand Up @@ -71,27 +55,34 @@ impl<S: Storage> DynFile for File<'_, '_, S> {
}
}

impl dyn DynFile + '_ {
pub fn read_to_end<const N: usize>(&self, buf: &mut Vec<u8, N>) -> Result<usize> {
let had = buf.len();
buf.resize_default(buf.capacity()).unwrap();
let read = self.read(&mut buf[had..])?;
buf.truncate(had + read);
Ok(read)
}
}

/// Object-safe trait for [`Filesystem`][].
///
/// The following methods are implemented in [`DynStorage`][] instead:
/// - [`DynStorage::format`][]
/// - [`DynStorage::is_mountable`][]
/// - [`DynStorage::mount_and_then`][]
///
/// The following methods have to be implemented as free functions:
/// - [`read_file`][]
/// - [`read_file_chunk`][]
/// - [`DynStorage::mount_and_then`](trait.DynStorage.html#method.mount_and_then)
///
/// The following methods cannot support generic return types in the callbacks:
/// - [`DynFilesystem::create_file_and_then`][]
/// - [`DynFilesystem::open_file_and_then`][]
/// - [`DynFilesystem::open_file_with_options_and_then`][]
/// - [`DynFilesystem::read_dir_and_then`][]
/// Use these free helper functions instead:
/// - [`create_file_and_then`][]
/// - [`open_file_and_then`][]
/// - [`open_file_with_options_and_then`][]
/// - [`read_dir_and_then`][]
/// - [`DynFilesystem::create_file_and_then_unit`][]
/// - [`DynFilesystem::open_file_and_then_unit`][]
/// - [`DynFilesystem::open_file_with_options_and_then_unit`][]
/// - [`DynFilesystem::read_dir_and_then_unit`][]
///
/// Use these helper functions instead:
/// - [`DynFilesystem::create_file_and_then`](#method.create_file_and_then)
/// - [`DynFilesystem::open_file_and_then`](#method.open_file_and_then)
/// - [`DynFilesystem::open_file_with_options_and_then`](#method.open_file_with_options_and_then)
/// - [`DynFilesystem::read_dir_and_then`](#method.read_dir_and_then)
///
/// All other methods are mirrored directly. See the documentation for [`Filesystem`][] for more information.
pub trait DynFilesystem {
Expand All @@ -107,9 +98,9 @@ pub trait DynFilesystem {
fn remove_dir_all_where(&self, path: &Path, predicate: Predicate<'_>) -> Result<usize>;
fn rename(&self, from: &Path, to: &Path) -> Result<()>;
fn metadata(&self, path: &Path) -> Result<Metadata>;
fn create_file_and_then(&self, path: &Path, f: FileCallback<'_>) -> Result<()>;
fn open_file_and_then(&self, path: &Path, f: FileCallback<'_>) -> Result<()>;
fn open_file_with_options_and_then(
fn create_file_and_then_unit(&self, path: &Path, f: FileCallback<'_>) -> Result<()>;
fn open_file_and_then_unit(&self, path: &Path, f: FileCallback<'_>) -> Result<()>;
fn open_file_with_options_and_then_unit(
&self,
o: OpenOptionsCallback<'_>,
path: &Path,
Expand All @@ -118,7 +109,7 @@ pub trait DynFilesystem {
fn attribute(&self, path: &Path, id: u8) -> Result<Option<Attribute>>;
fn remove_attribute(&self, path: &Path, id: u8) -> Result<()>;
fn set_attribute(&self, path: &Path, attribute: &Attribute) -> Result<()>;
fn read_dir_and_then(&self, path: &Path, f: DirEntriesCallback<'_>) -> Result<()>;
fn read_dir_and_then_unit(&self, path: &Path, f: DirEntriesCallback<'_>) -> Result<()>;
fn create_dir(&self, path: &Path) -> Result<()>;
fn create_dir_all(&self, path: &Path) -> Result<()>;
fn write(&self, path: &Path, contents: &[u8]) -> Result<()>;
Expand Down Expand Up @@ -168,15 +159,15 @@ impl<S: Storage> DynFilesystem for Filesystem<'_, S> {
Filesystem::metadata(self, path)
}

fn create_file_and_then(&self, path: &Path, f: FileCallback<'_>) -> Result<()> {
fn create_file_and_then_unit(&self, path: &Path, f: FileCallback<'_>) -> Result<()> {
Filesystem::create_file_and_then(self, path, |file| f(file))
}

fn open_file_and_then(&self, path: &Path, f: FileCallback<'_>) -> Result<()> {
fn open_file_and_then_unit(&self, path: &Path, f: FileCallback<'_>) -> Result<()> {
Filesystem::open_file_and_then(self, path, |file| f(file))
}

fn open_file_with_options_and_then(
fn open_file_with_options_and_then_unit(
&self,
o: OpenOptionsCallback<'_>,
path: &Path,
Expand All @@ -197,7 +188,7 @@ impl<S: Storage> DynFilesystem for Filesystem<'_, S> {
Filesystem::set_attribute(self, path, attribute)
}

fn read_dir_and_then(&self, path: &Path, f: DirEntriesCallback<'_>) -> Result<()> {
fn read_dir_and_then_unit(&self, path: &Path, f: DirEntriesCallback<'_>) -> Result<()> {
Filesystem::read_dir_and_then(self, path, |entries| f(entries))
}

Expand All @@ -218,17 +209,85 @@ impl<S: Storage> DynFilesystem for Filesystem<'_, S> {
}
}

impl dyn DynFilesystem + '_ {
pub fn read_file<const N: usize>(&self, path: &Path) -> Result<Vec<u8, N>> {
let mut contents = Vec::new();
self.open_file_and_then(path, &mut |file| {
file.read_to_end(&mut contents)?;
Ok(())
})?;
Ok(contents)
}

pub fn read_file_chunk<const N: usize>(
&self,
path: &Path,
pos: OpenSeekFrom,
) -> Result<(Vec<u8, N>, usize)> {
let mut contents = Vec::new();
let file_len = self.open_file_and_then(path, &mut |file| {
file.seek(pos.into())?;
let read_n = file.read(&mut contents)?;
contents.truncate(read_n);
file.len()
})?;
Ok((contents, file_len))
}

pub fn create_file_and_then<R>(&self, path: &Path, f: FileCallback<'_, R>) -> Result<R> {
let mut result = Err(Error::Io);
self.create_file_and_then_unit(path, &mut |file| {
result = Ok(f(file)?);
Ok(())
})?;
result
}

pub fn open_file_and_then<R>(&self, path: &Path, f: FileCallback<'_, R>) -> Result<R> {
let mut result = Err(Error::Io);
self.open_file_and_then_unit(path, &mut |file| {
result = Ok(f(file)?);
Ok(())
})?;
result
}

pub fn open_file_with_options_and_then<R>(
&self,
o: OpenOptionsCallback<'_>,
path: &Path,
f: FileCallback<'_, R>,
) -> Result<R> {
let mut result = Err(Error::Io);
self.open_file_with_options_and_then_unit(o, path, &mut |file| {
result = Ok(f(file)?);
Ok(())
})?;
result
}

pub fn read_dir_and_then<R>(&self, path: &Path, f: DirEntriesCallback<'_, R>) -> Result<R> {
let mut result = Err(Error::Io);
self.read_dir_and_then_unit(path, &mut |entries| {
result = Ok(f(entries)?);
Ok(())
})?;
result
}
}

/// Object-safe trait for [`Storage`][].
///
/// It contains these additional methods from [`Filesystem`][]:
/// - [`DynStorage::format`][]
/// - [`DynStorage::is_mountable`][]
/// - [`DynStorage::mount_and_then`][]
/// - [`DynStorage::mount_and_then`](#method.mount_and_then)
///
/// The following methods cannot support generic return types in the callbacks:
/// - [`DynStorage::mount_and_then`][]
/// Use these free helper functions instead:
/// - [`mount_and_then`][]
/// - [`DynStorage::mount_and_then_unit`][]
///
/// Use these helper functions instead:
/// - [`DynStorage::mount_and_then`](#method.mount_and_then)
///
/// The `read`, `write` and `erase` methods are mirrored directly. The associated constants and
/// types are transformed into methods. See the documentation for [`Storage`][] for more
Expand All @@ -246,7 +305,7 @@ pub trait DynStorage {
fn erase(&mut self, off: usize, len: usize) -> Result<usize>;
fn format(&mut self) -> Result<()>;
fn is_mountable(&mut self) -> bool;
fn mount_and_then(&mut self, f: FilesystemCallback<'_>) -> Result<()>;
fn mount_and_then_unit(&mut self, f: FilesystemCallback<'_>) -> Result<()>;
}

impl<S: Storage> DynStorage for S {
Expand Down Expand Up @@ -298,101 +357,18 @@ impl<S: Storage> DynStorage for S {
Filesystem::is_mountable(self)
}

fn mount_and_then(&mut self, f: FilesystemCallback<'_>) -> Result<()> {
fn mount_and_then_unit(&mut self, f: FilesystemCallback<'_>) -> Result<()> {
Filesystem::mount_and_then(self, |fs| f(fs))
}
}

pub fn read_file<const N: usize>(fs: &dyn DynFilesystem, path: &Path) -> Result<Vec<u8, N>> {
let mut contents = Vec::new();
fs.open_file_and_then(path, &mut |file| {
read_file_to_end(file, &mut contents)?;
Ok(())
})?;
Ok(contents)
}

pub fn read_file_chunk<const N: usize>(
fs: &dyn DynFilesystem,
path: &Path,
pos: OpenSeekFrom,
) -> Result<(Vec<u8, N>, usize)> {
let mut contents = Vec::new();
let file_len = open_file_and_then(fs, path, &mut |file| {
file.seek(pos.into())?;
let read_n = file.read(&mut contents)?;
contents.truncate(read_n);
file.len()
})?;
Ok((contents, file_len))
}

pub fn read_file_to_end<const N: usize>(file: &dyn DynFile, buf: &mut Vec<u8, N>) -> Result<usize> {
let had = buf.len();
buf.resize_default(buf.capacity()).unwrap();
let read = file.read(&mut buf[had..])?;
buf.truncate(had + read);
Ok(read)
}

pub fn create_file_and_then<R>(
fs: &dyn DynFilesystem,
path: &Path,
f: FileCallback<'_, R>,
) -> Result<R> {
let mut result = Err(Error::Io);
fs.create_file_and_then(path, &mut |file| {
result = Ok(f(file)?);
Ok(())
})?;
result
}

pub fn open_file_and_then<R>(
fs: &dyn DynFilesystem,
path: &Path,
f: FileCallback<'_, R>,
) -> Result<R> {
let mut result = Err(Error::Io);
fs.open_file_and_then(path, &mut |file| {
result = Ok(f(file)?);
Ok(())
})?;
result
}

pub fn open_file_with_options_and_then<R>(
fs: &dyn DynFilesystem,
o: OpenOptionsCallback<'_>,
path: &Path,
f: FileCallback<'_, R>,
) -> Result<R> {
let mut result = Err(Error::Io);
fs.open_file_with_options_and_then(o, path, &mut |file| {
result = Ok(f(file)?);
Ok(())
})?;
result
}

pub fn read_dir_and_then<R>(
fs: &dyn DynFilesystem,
path: &Path,
f: DirEntriesCallback<'_, R>,
) -> Result<R> {
let mut result = Err(Error::Io);
fs.read_dir_and_then(path, &mut |entries| {
result = Ok(f(entries)?);
Ok(())
})?;
result
}

pub fn mount_and_then<R>(s: &mut dyn DynStorage, f: FilesystemCallback<'_, R>) -> Result<R> {
let mut result = Err(Error::Io);
s.mount_and_then(&mut |fs| {
result = Ok(f(fs)?);
Ok(())
})?;
result
impl dyn DynStorage + '_ {
pub fn mount_and_then<R>(&mut self, f: FilesystemCallback<'_, R>) -> Result<R> {
let mut result = Err(Error::Io);
self.mount_and_then_unit(&mut |fs| {
result = Ok(f(fs)?);
Ok(())
})?;
result
}
}

0 comments on commit 37f74fe

Please sign in to comment.