Skip to content

Commit

Permalink
Runtime documentation
Browse files Browse the repository at this point in the history
  • Loading branch information
h33p committed Dec 7, 2023
1 parent c0be809 commit bcff8c2
Show file tree
Hide file tree
Showing 3 changed files with 188 additions and 40 deletions.
123 changes: 91 additions & 32 deletions mfio-rt/src/lib.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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 {
Expand All @@ -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 {
Expand Down Expand Up @@ -86,38 +141,11 @@ impl From<Shutdown> 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
Expand All @@ -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<Output = MfioResult<Self::FileHandle>> + 'a
Expand Down Expand Up @@ -317,7 +350,10 @@ pub trait DirHandle: Sized {
/// # });
/// ```
fn do_op<'a, P: AsRef<Path> + ?Sized>(&'a self, operation: DirOp<&'a P>) -> Self::OpFuture<'a>;
}

/// Helpers for running directory operations more ergonomically.
pub trait DirHandleExt: DirHandle {
///
/// # Examples
///
Expand Down Expand Up @@ -440,6 +476,9 @@ pub trait DirHandle: Sized {
// fn symlink_dir(&self, from: &Path, to: &Path) -> Self::OpFuture<'_>;
}

impl<T: DirHandle> 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<P: AsRef<Path>> {
Expand Down Expand Up @@ -602,6 +641,9 @@ impl<'a, P: AsRef<Path> + ?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,
Expand Down Expand Up @@ -633,6 +675,9 @@ impl From<std::fs::DirEntry> 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,
Expand All @@ -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 {}

Expand All @@ -651,6 +702,9 @@ impl From<std::fs::Permissions> 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,
Expand Down Expand Up @@ -679,12 +733,15 @@ impl Metadata {
}
}

/// Supertrait for file handles.
pub trait FileHandle: AsyncRead<u64> + AsyncWrite<u64> {}
impl<T: AsyncRead<u64> + AsyncWrite<u64>> FileHandle for T {}

/// Supertrait for stream handles.
pub trait StreamHandle: AsyncRead<NoPos> + AsyncWrite<NoPos> {}
impl<T: AsyncRead<NoPos> + AsyncWrite<NoPos>> StreamHandle for T {}

/// Describes TCP capable runtime operations.
#[cfg(feature = "std")]
pub trait Tcp: IoBackend {
type StreamHandle: TcpStreamHandle;
Expand All @@ -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<SocketAddr>;
Expand All @@ -716,6 +774,7 @@ pub trait TcpStreamHandle: StreamHandle {
//fn nodelay(&self) -> MfioResult<bool>;
}

/// Describes operations performable on a TCP listener.
#[cfg(feature = "std")]
pub trait TcpListenerHandle: Stream<Item = (Self::StreamHandle, SocketAddr)> {
type StreamHandle: TcpStreamHandle;
Expand Down
5 changes: 5 additions & 0 deletions mfio-rt/src/native/mod.rs
Original file line number Diff line number Diff line change
@@ -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};
Expand Down
100 changes: 92 additions & 8 deletions mfio-rt/src/test_suite.rs
Original file line number Diff line number Diff line change
@@ -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,
Expand Down Expand Up @@ -769,6 +775,40 @@ 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
/// use mfio_rt::*;
///
/// test_suite_base!(tests_default, |test_name, closure| {
/// let _ = ::env_logger::builder().is_test(true).try_init();
/// let mut rt = 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)
/// });
/// },
/// dirs_equal,
/// files_equal
/// );
/// ```
#[macro_export]
macro_rules! test_suite_base {
($test_ident:ident, $fs_builder:expr, $($(#[cfg($meta:meta)])* $test:ident),*) => {
Expand Down Expand Up @@ -799,6 +839,36 @@ 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
/// use mfio_rt::*;
///
/// test_suite!(tests_default, |test_name, closure| {
/// let _ = ::env_logger::builder().is_test(true).try_init();
/// let mut rt = 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) => {
Expand All @@ -819,6 +889,27 @@ 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
/// use mfio_rt::*;
///
/// net_test_suite!(net_tests_default, |closure| {
/// let _ = ::env_logger::builder().is_test(true).try_init();
/// let mut rt = NativeRt::default();
/// let rt = staticify(&mut rt);
/// rt.run(closure);
/// });
/// ```
#[cfg(feature = "std")]
#[macro_export]
macro_rules! net_test_suite {
Expand All @@ -844,13 +935,6 @@ macro_rules! net_test_suite {
unsafe { core::mem::transmute(val) }
}

async fn test_run<T: 'static, F: Future<Output = ()>>(
rt: &'static T,
closure: fn(&'static T) -> F,
) {
closure(rt).await
}

#[cfg(not(miri))]
#[test]
fn tcp_connect() {
Expand Down

0 comments on commit bcff8c2

Please sign in to comment.