use std::{future::Future, io::Result, path::Path};
pub use async_trait::async_trait;
pub use photonio::io::{Read, ReadAt, Write, WriteAt};
mod stdenv;
pub use stdenv::Std;
mod photon;
pub use photon::Photon;
#[async_trait]
pub trait Env: Clone + Send + Sync + 'static {
type PositionalReader: PositionalReader;
type SequentialWriter: SequentialWriter;
type JoinHandle<T: Send>: Future<Output = T> + Send;
type Directory: Directory + Send + Sync + 'static;
async fn open_positional_reader<P>(&self, path: P) -> Result<Self::PositionalReader>
where
P: AsRef<Path> + Send;
async fn open_sequential_writer<P>(&self, path: P) -> Result<Self::SequentialWriter>
where
P: AsRef<Path> + Send;
fn spawn_background<F>(&self, f: F) -> Self::JoinHandle<F::Output>
where
F: Future + Send + 'static,
F::Output: Send;
async fn rename<P: AsRef<Path> + Send, Q: AsRef<Path> + Send>(
&self,
from: P,
to: Q,
) -> Result<()>;
async fn remove_file<P: AsRef<Path> + Send>(&self, path: P) -> Result<()>;
async fn create_dir_all<P: AsRef<Path> + Send>(&self, path: P) -> Result<()>;
async fn remove_dir_all<P: AsRef<Path> + Send>(&self, path: P) -> Result<()>;
fn read_dir<P: AsRef<Path>>(&self, path: P) -> Result<std::fs::ReadDir>;
async fn metadata<P: AsRef<Path> + Send>(&self, path: P) -> Result<Metadata>;
async fn open_dir<P: AsRef<Path> + Send>(&self, path: P) -> Result<Self::Directory>;
}
#[async_trait]
pub trait PositionalReader: Send + Sync + 'static {
type ReadAt<'a>: Future<Output = Result<usize>> + 'a + Send
where
Self: 'a;
fn read_at<'a>(&'a self, buf: &'a mut [u8], pos: u64) -> Self::ReadAt<'a>;
fn direct_io_ify(&self) -> Result<()>;
}
pub trait PositionalReaderExt {
type ReadExactAt<'a>: Future<Output = Result<()>> + 'a
where
Self: 'a;
fn read_exact_at<'a>(&'a self, buf: &'a mut [u8], pos: u64) -> Self::ReadExactAt<'a>;
}
impl<T> PositionalReaderExt for T
where
T: PositionalReader,
{
type ReadExactAt<'a> = impl Future<Output = Result<()>> + 'a where Self: 'a;
fn read_exact_at<'a>(&'a self, mut buf: &'a mut [u8], mut pos: u64) -> Self::ReadExactAt<'a> {
async move {
while !buf.is_empty() {
match self.read_at(buf, pos).await {
Ok(0) => return Err(std::io::ErrorKind::UnexpectedEof.into()),
Ok(n) => {
buf = &mut buf[n..];
pos += n as u64;
}
Err(e) if e.kind() == std::io::ErrorKind::Interrupted => {}
Err(e) => return Err(e),
}
}
Ok(())
}
}
}
#[async_trait]
pub trait SequentialWriter: Send + Sync + 'static {
type Write<'a>: Future<Output = Result<usize>> + 'a + Send
where
Self: 'a;
fn write<'a>(&'a mut self, buf: &'a [u8]) -> Self::Write<'a>;
async fn sync_data(&mut self) -> Result<()>;
async fn sync_all(&mut self) -> Result<()>;
async fn truncate(&self, len: u64) -> Result<()>;
fn direct_io_ify(&self) -> Result<()>;
}
pub trait SequentialWriterExt {
type WriteAll<'a>: Future<Output = Result<()>> + 'a
where
Self: 'a;
fn write_all<'a>(&'a mut self, buf: &'a [u8]) -> Self::WriteAll<'a>;
}
impl<T> SequentialWriterExt for T
where
T: SequentialWriter,
{
type WriteAll<'a> = impl Future<Output = Result<()>> + 'a
where
Self: 'a;
fn write_all<'a>(&'a mut self, mut buf: &'a [u8]) -> Self::WriteAll<'a> {
async move {
while !buf.is_empty() {
match self.write(buf).await {
Ok(0) => return Err(std::io::ErrorKind::WriteZero.into()),
Ok(n) => buf = &buf[n..],
Err(e) if e.kind() == std::io::ErrorKind::Interrupted => {}
Err(e) => return Err(e),
}
}
Ok(())
}
}
}
#[allow(clippy::len_without_is_empty)]
pub struct Metadata {
pub len: u64,
pub is_dir: bool,
}
#[cfg(target_os = "linux")]
pub(in crate::env) fn direct_io_ify(fd: i32) -> Result<()> {
macro_rules! syscall {
($fn: ident ( $($arg: expr),* $(,)* ) ) => {{
#[allow(unused_unsafe)]
let res = unsafe { libc::$fn($($arg, )*) };
if res == -1 {
Err(std::io::Error::last_os_error())
} else {
Ok(res)
}
}};
}
let flags = syscall!(fcntl(fd, libc::F_GETFL))?;
syscall!(fcntl(fd, libc::F_SETFL, flags | libc::O_DIRECT))?;
Ok(())
}
#[cfg(not(target_os = "linux"))]
pub(in crate::env) fn direct_io_ify(_: i32) -> Result<()> {
Err(std::io::Error::new(
std::io::ErrorKind::Unsupported,
"enable direct io fail",
))
}
#[async_trait]
pub trait Directory {
async fn sync_all(&self) -> Result<()>;
}