rafts 0.1.0

Minimal experimental async block device I/O abstraction
Documentation
#![cfg_attr(not(feature = "std"), no_std)]
use crate::error::Errno;

#[cfg(not(feature = "std"))]
extern crate alloc;
#[cfg(not(feature = "std"))]
use alloc::boxed::Box;

pub trait IoBuf {
    /// Returns the length of the buffer.
    fn len(&self) -> usize {
        self.as_slice().len()
    }

    /// Returns a slice of the buffer.
    fn as_slice(&self) -> &[u8];
}

pub trait IoBufMut: IoBuf {
    /// Returns a mutable slice of the buffer.
    fn as_mut_slice(&mut self) -> &mut [u8];
}

/// Result type returned by asynchronous block I/O operations.
pub type IoResult = Result<(), Errno>;

/// Completion callback type for async block I/O operations.
pub type Completion = Box<dyn FnOnce(IoResult) + Send + 'static>;

/// A trait for asynchronous block oriented devices.
///
/// The API is callback based so it can be implemented in both `std` and
/// `no_std` (with an async executor of your choosing). For convenience, when
/// the `std` feature is enabled an extension trait (`BlockAsyncIoExt`) provides
/// `async/await` friendly wrapper methods.
///
/// # Contract
/// * Implementations MUST invoke the provided `completion` exactly once.
/// * The caller MUST guarantee that the provided buffer lives until the
///   completion callback is invoked. (Typically the caller waits on a channel
///   / future that is fulfilled inside the callback.)
/// * Implementations SHOULD treat `buf.len()` as a multiple of `block_size()`.
///   If it is not, they MAY return `Err(Errno::Unknown)` (until more granular
///   error codes are introduced).
pub trait BlockAsyncIo: Send + Sync + 'static {
    /// Returns the hardware / logical block size in bytes (e.g. 4096).
    fn block_size(&self) -> usize;

    /// Read one or more consecutive blocks starting at `block` into `buf`.
    ///
    /// `buf.len()` SHOULD be a multiple of `block_size()`.
    fn read(&self, block: u64, buf: &mut [u8], completion: Completion);

    /// Write one or more consecutive blocks starting at `block` from `buf`.
    ///
    /// `buf.len()` SHOULD be a multiple of `block_size()`.
    fn write(&self, block: u64, buf: &[u8], completion: Completion);

    /// Flush any buffered data. Default implementation is a no-op success.
    fn flush(&self, completion: Completion) {
        completion(Ok(()));
    }
}

#[cfg(feature = "tokio")]
mod ext {
    use super::{BlockAsyncIo, IoResult};
    use crate::error::Errno;
    use std::sync::Arc;

    /// Extension trait exposing `async` wrappers for the callback based API.
    pub trait BlockAsyncIoExt: BlockAsyncIo + Sized {
        /// Read blocks using async/await.
        async fn read_async(&self, block: u64, buf: &mut [u8]) -> IoResult {
            let (tx, rx) = tokio::sync::oneshot::channel();
            self.read(
                block,
                buf,
                Box::new(move |res| {
                    let _ = tx.send(res);
                }),
            );
            rx.await.unwrap_or(Err(Errno::Unknown))
        }

        /// Write blocks using async/await.
        async fn write_async(&self, block: u64, buf: &[u8]) -> IoResult {
            let (tx, rx) = tokio::sync::oneshot::channel();
            self.write(
                block,
                buf,
                Box::new(move |res| {
                    let _ = tx.send(res);
                }),
            );
            rx.await.unwrap_or(Err(Errno::Unknown))
        }

        /// Flush using async/await.
        async fn flush_async(&self) -> IoResult {
            let (tx, rx) = tokio::sync::oneshot::channel();
            self.flush(Box::new(move |res| {
                let _ = tx.send(res);
            }));
            rx.await.unwrap_or(Err(Errno::Unknown))
        }

        /// Convenience to obtain an `Arc` pointing to `self` (when already in an Arc).
        fn into_arc(self) -> Arc<Self>
        where
            Self: Sized,
        {
            Arc::new(self)
        }
    }

    impl<T: BlockAsyncIo> BlockAsyncIoExt for T {}

    pub use BlockAsyncIoExt as _; // re-export for glob import convenience
}

#[cfg(feature = "tokio")]
pub use ext::*;