diff --git a/mfio-rt/src/lib.rs b/mfio-rt/src/lib.rs index 0129a07..7f8665d 100644 --- a/mfio-rt/src/lib.rs +++ b/mfio-rt/src/lib.rs @@ -1,3 +1,19 @@ +//! # mfio-rt +//! +//! ## mfio Backed Runtime +//! +//! This crate aims to provide building blocks for mfio backed asynchronous runtimes. The traits +//! have the option to not rely on the standard library. This makes the system great for `no_std` +//! embedded environments or kernel-side code. +//! +//! `native` feature (depends on `std`) enables native implementations of the runtime through +//! [`NativeRt`] structure. +//! +//! `virt` feature enables a virtual in-memory runtime through [`VirtRt`](virt::VirtRt) structure. +//! +//! Custom runtimes may be implemented by implementing [`IoBackend`], and any of the runtime +//! traits, such as [`Fs`] or [`Tcp`]. + #![cfg_attr(not(feature = "std"), no_std)] extern crate alloc; @@ -40,6 +56,10 @@ pub mod test_suite; #[cfg(feature = "native")] pub use native::{NativeFile, NativeRt, NativeRtBuilder}; +/// File open options. +/// +/// This type is equivalent to [`OpenOptions`](std::fs::OpenOptions) found in the standard library, +/// but omits `append` mode. #[repr(C)] #[derive(Clone, Copy, Debug, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)] pub struct OpenOptions { @@ -53,6 +73,41 @@ pub struct OpenOptions { //pub append: bool, } +impl OpenOptions { + pub const fn new() -> Self { + Self { + read: false, + write: false, + create: false, + create_new: false, + truncate: false, + } + } + + pub fn read(self, read: bool) -> Self { + Self { read, ..self } + } + + pub fn write(self, write: bool) -> Self { + Self { write, ..self } + } + + pub fn create(self, create: bool) -> Self { + Self { create, ..self } + } + + pub fn create_new(self, create_new: bool) -> Self { + Self { create_new, ..self } + } + + pub fn truncate(self, truncate: bool) -> Self { + Self { truncate, ..self } + } +} + +/// Network stream shutdown options. +/// +/// This type is equivalent to [`Shutdown`](std::net::Shutdown) in the standard library. #[repr(C)] #[derive(Clone, Copy, Debug, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)] pub enum Shutdown { @@ -86,38 +141,11 @@ impl From for net::Shutdown { } } -impl OpenOptions { - pub const fn new() -> Self { - Self { - read: false, - write: false, - create: false, - create_new: false, - truncate: false, - } - } - - pub fn read(self, read: bool) -> Self { - Self { read, ..self } - } - - pub fn write(self, write: bool) -> Self { - Self { write, ..self } - } - - pub fn create(self, create: bool) -> Self { - Self { create, ..self } - } - - pub fn create_new(self, create_new: bool) -> Self { - Self { create_new, ..self } - } - - pub fn truncate(self, truncate: bool) -> Self { - Self { truncate, ..self } - } -} - +/// Primary filesystem trait. +/// +/// This provides an entrypoint for filesystem operations. However, since operations are typically +/// performed on a directory, this trait only serves as a proxy for retrieving the current +/// directory handle. pub trait Fs: IoBackend { type DirHandle<'a>: DirHandle + 'a where @@ -140,6 +168,11 @@ pub trait Fs: IoBackend { } /// Represents a location in filesystem operations are performed from. +/// +/// Directory handles may refer to fixed directory entries throughout time, even if said entry is +/// unlinked from the filesystem. So long as the handle is held, it may be valid. However, this +/// behavior is implementation-specific, and, for instance, [`NativeRt`] does not follow it, +/// because directory handles are simply stored as paths, rather than dir FDs/handles. pub trait DirHandle: Sized { type FileHandle: FileHandle; type OpenFileFuture<'a>: Future> + 'a @@ -317,7 +350,10 @@ pub trait DirHandle: Sized { /// # }); /// ``` fn do_op<'a, P: AsRef + ?Sized>(&'a self, operation: DirOp<&'a P>) -> Self::OpFuture<'a>; +} +/// Helpers for running directory operations more ergonomically. +pub trait DirHandleExt: DirHandle { /// /// # Examples /// @@ -440,6 +476,9 @@ pub trait DirHandle: Sized { // fn symlink_dir(&self, from: &Path, to: &Path) -> Self::OpFuture<'_>; } +impl DirHandleExt for T {} + +/// List of operations that can be done on a filesystem. #[non_exhaustive] #[derive(Serialize, Deserialize, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Debug)] pub enum DirOp> { @@ -602,6 +641,9 @@ impl<'a, P: AsRef + ?Sized> DirOp<&'a P> { } } +/// Directory list entry. +/// +/// This type is equivalent to [`DirEntry`](std::fs::DirEntry) in the standard library. #[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, Ord, PartialOrd)] pub struct DirEntry { pub name: String, @@ -633,6 +675,9 @@ impl From for DirEntry { } } +/// Directory list entry type. +/// +/// This type is equivalent to [`FileType`](std::fs::FileType) in the standard library. #[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize)] pub enum FileType { Unknown, @@ -641,6 +686,12 @@ pub enum FileType { Symlink, } +/// Directory list entry permission. +/// +/// This type is equivalent to [`Permission`](std::fs::Permissions) in the standard library. +/// However, this currently contains nothing, and is effectively useless. +/// +/// TODO: make this type do something. #[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Debug, Serialize, Deserialize)] pub struct Permissions {} @@ -651,6 +702,9 @@ impl From for Permissions { } } +/// Directory list entry metadata. +/// +/// This type is equivalent to [`Metadata`](std::fs::Metadata) in the standard library. #[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize)] pub struct Metadata { pub permissions: Permissions, @@ -679,12 +733,15 @@ impl Metadata { } } +/// Supertrait for file handles. pub trait FileHandle: AsyncRead + AsyncWrite {} impl + AsyncWrite> FileHandle for T {} +/// Supertrait for stream handles. pub trait StreamHandle: AsyncRead + AsyncWrite {} impl + AsyncWrite> StreamHandle for T {} +/// Describes TCP capable runtime operations. #[cfg(feature = "std")] pub trait Tcp: IoBackend { type StreamHandle: TcpStreamHandle; @@ -703,6 +760,7 @@ pub trait Tcp: IoBackend { fn bind<'a, A: ToSocketAddrs + Send + 'a>(&'a self, addrs: A) -> Self::BindFuture<'a, A>; } +/// Describes operations performable on a TCP connection. #[cfg(feature = "std")] pub trait TcpStreamHandle: StreamHandle { fn local_addr(&self) -> MfioResult; @@ -716,6 +774,7 @@ pub trait TcpStreamHandle: StreamHandle { //fn nodelay(&self) -> MfioResult; } +/// Describes operations performable on a TCP listener. #[cfg(feature = "std")] pub trait TcpListenerHandle: Stream { type StreamHandle: TcpStreamHandle; diff --git a/mfio-rt/src/native/mod.rs b/mfio-rt/src/native/mod.rs index a164bf8..3e4293d 100644 --- a/mfio-rt/src/native/mod.rs +++ b/mfio-rt/src/native/mod.rs @@ -1,3 +1,8 @@ +//! Native async runtime +//! +//! This module provides native OS-backed runtime/backend implementations. To start, please access +//! [`NativeRt`] or [`NativeRtBuilder`] types. + use core::future::{ready, Future, Ready}; use core::pin::Pin; use core::task::{Context, Poll}; diff --git a/mfio-rt/src/test_suite.rs b/mfio-rt/src/test_suite.rs index 31286e1..91e8cfe 100644 --- a/mfio-rt/src/test_suite.rs +++ b/mfio-rt/src/test_suite.rs @@ -1,5 +1,11 @@ +//! mfio-rt test suite. +//! +//! This module contains all blocks needed for testing [`Fs`] and [`Tcp`] implementations in a +//! standard, extensive way. The main entry point for using the test suite are the +//! [`test_suite!`](crate::test_suite!) and [`net_test_suite!`](crate::net_test_suite!) macros. + use crate::util::diff_paths; -pub use crate::{DirHandle, Fs, OpenOptions, Path, Shutdown}; +pub use crate::{DirHandle, DirHandleExt, Fs, OpenOptions, Path, Shutdown}; pub use alloc::{ collections::BTreeSet, format, @@ -769,6 +775,35 @@ pub mod fs_tests { } } +/// Builds filesystem test suite. +/// +/// Unlike [`test_suite!`](crate::test_suite!), this function does not include any tests, and they +/// must be added manually. Please see the [`fs_tests`] module for a list of available tests. +/// +/// The first parameter of the macro is the name of the generated module, while the second one +/// contains a closure containing the test name and an asynchronous closure to be executed. +/// +/// The third argument an onwards is a comma separated list of tests to run. +/// +/// The closure that runs contains the entire test_suite modules. It accepts a `&'static mut T`, +/// where `T: Fs`. To get a static ref, use the `staticify` function. It is unsound, but necessary +/// to make test suite generation code ergonomic. +/// +/// # Examples +/// +/// ```no_run +/// test_suite_base!(tests_default, |test_name, closure| { +/// let _ = ::env_logger::builder().is_test(true).try_init(); +/// let mut rt = crate::NativeRt::default(); +/// let rt = staticify(&mut rt); +/// let dir = TempDir::new(test_name).unwrap(); +/// rt.set_cwd(dir.path().to_path_buf()); +/// rt.run(move |rt| { +/// let run = TestRun::new(rt, dir); +/// closure(run) +/// }); +/// }); +/// ``` #[macro_export] macro_rules! test_suite_base { ($test_ident:ident, $fs_builder:expr, $($(#[cfg($meta:meta)])* $test:ident),*) => { @@ -799,6 +834,34 @@ macro_rules! test_suite_base { }; } +/// Builds filesystem test suite. +/// +/// This includes all default tests, if you wish to not do that, please use +/// [`test_suite_base!`](crate::test_suite_base!) macro. +/// +/// The first parameter of the macro is the name of the generated module, while the second one +/// contains a closure containing the test name and an asynchronous closure to be executed. +/// +/// The closure that runs contains the entire test_suite modules. It accepts a `&'static mut T`, +/// where `T: Fs`. To get a static ref, use the `staticify` function. It is unsound, but necessary +/// to make test suite generation code ergonomic. +/// +/// +/// # Examples +/// +/// ```no_run +/// test_suite!(tests_default, |test_name, closure| { +/// let _ = ::env_logger::builder().is_test(true).try_init(); +/// let mut rt = crate::NativeRt::default(); +/// let rt = staticify(&mut rt); +/// let dir = TempDir::new(test_name).unwrap(); +/// rt.set_cwd(dir.path().to_path_buf()); +/// rt.run(move |rt| { +/// let run = TestRun::new(rt, dir); +/// closure(run) +/// }); +/// }); +/// ``` #[macro_export] macro_rules! test_suite { ($test_ident:ident, $fs_builder:expr) => { @@ -819,6 +882,25 @@ macro_rules! test_suite { }; } +/// Builds network test suite. +/// +/// The first parameter of the macro is the name of the generated module, while the second one +/// contains a closure containing the test name and an asynchronous closure to be executed. +/// +/// The closure that runs contains the entire test_suite and net modules. It accepts a `&'static +/// mut T` where `T: Tcp`. To get a static ref, use the `staticify` function. It is unsound, but +/// necessary to make test suite generation code ergonomic. +/// +/// # Examples +/// +/// ```no_run +/// net_test_suite!(net_tests_default, |closure| { +/// let _ = ::env_logger::builder().is_test(true).try_init(); +/// let mut rt = crate::NativeRt::default(); +/// let rt = staticify(&mut rt); +/// rt.run(closure); +/// }); +/// ``` #[cfg(feature = "std")] #[macro_export] macro_rules! net_test_suite { @@ -844,13 +926,6 @@ macro_rules! net_test_suite { unsafe { core::mem::transmute(val) } } - async fn test_run>( - rt: &'static T, - closure: fn(&'static T) -> F, - ) { - closure(rt).await - } - #[cfg(not(miri))] #[test] fn tcp_connect() {