Rename reactor -> driver, prep for lib/reactivity
This commit is contained in:
@@ -4,6 +4,9 @@
|
||||
//! - Dropping an I/O future cancels interest in the result.
|
||||
//! - The runtime issues best-effort kernel cancellation where supported.
|
||||
//! - The underlying OS operation may still complete after the future is dropped.
|
||||
//!
|
||||
//! The public surface intentionally mirrors `std::fs` where that shape makes sense, while using
|
||||
//! async methods for operations that may block the caller.
|
||||
|
||||
use std::ffi::OsStr;
|
||||
use std::io;
|
||||
@@ -21,33 +24,43 @@ struct FileInner {
|
||||
fd: OwnedFd,
|
||||
}
|
||||
|
||||
/// Async file handle.
|
||||
///
|
||||
/// `File` is cheap to clone internally and supports both cursor-based sequential I/O and
|
||||
/// offset-based positioned I/O.
|
||||
pub struct File {
|
||||
inner: Arc<FileInner>,
|
||||
}
|
||||
|
||||
/// Builder used to configure how a [`File`] is opened.
|
||||
pub struct OpenOptions {
|
||||
inner: OpOpenOptions,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
/// File metadata returned by [`metadata`] or [`File::metadata`].
|
||||
pub struct Metadata {
|
||||
inner: RawMetadata,
|
||||
}
|
||||
|
||||
/// Async directory-entry stream returned by [`read_dir`].
|
||||
pub struct ReadDir {
|
||||
inner: sys_fs::ReadDirStream,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
/// Directory entry yielded by [`ReadDir::next_entry`].
|
||||
pub struct DirEntry {
|
||||
inner: OpDirEntry,
|
||||
}
|
||||
|
||||
impl File {
|
||||
/// Opens an existing file for reading.
|
||||
pub async fn open(path: impl AsRef<Path>) -> io::Result<Self> {
|
||||
OpenOptions::new().read(true).open(path).await
|
||||
}
|
||||
|
||||
/// Opens a file for writing, creating or truncating it first.
|
||||
pub async fn create(path: impl AsRef<Path>) -> io::Result<Self> {
|
||||
OpenOptions::new()
|
||||
.write(true)
|
||||
@@ -57,10 +70,12 @@ impl File {
|
||||
.await
|
||||
}
|
||||
|
||||
/// Reads bytes from the file's current cursor position.
|
||||
pub async fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
self.read_impl(None, buf).await
|
||||
}
|
||||
|
||||
/// Reads exactly `buf.len()` bytes from the current cursor position.
|
||||
pub async fn read_exact(&mut self, mut buf: &mut [u8]) -> io::Result<()> {
|
||||
while !buf.is_empty() {
|
||||
let read = self.read(buf).await?;
|
||||
@@ -75,10 +90,12 @@ impl File {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Writes bytes at the file's current cursor position.
|
||||
pub async fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
self.write_impl(None, buf).await
|
||||
}
|
||||
|
||||
/// Writes the entire buffer at the file's current cursor position.
|
||||
pub async fn write_all(&mut self, mut buf: &[u8]) -> io::Result<()> {
|
||||
while !buf.is_empty() {
|
||||
let written = self.write(buf).await?;
|
||||
@@ -93,22 +110,30 @@ impl File {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Flushes any userspace buffering associated with this handle.
|
||||
///
|
||||
/// The current implementation does not add additional buffering beyond the kernel file
|
||||
/// description, so this is effectively a no-op.
|
||||
pub async fn flush(&mut self) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Synchronizes file contents and metadata to stable storage.
|
||||
pub async fn sync_all(&self) -> io::Result<()> {
|
||||
sys_fs::sync_all(FsOp::SyncAll { fd: self.raw_fd() }).await
|
||||
}
|
||||
|
||||
/// Synchronizes file contents to stable storage.
|
||||
pub async fn sync_data(&self) -> io::Result<()> {
|
||||
sys_fs::sync_data(FsOp::SyncData { fd: self.raw_fd() }).await
|
||||
}
|
||||
|
||||
/// Reads bytes starting at `offset` without using the shared file cursor.
|
||||
pub async fn read_at(&self, offset: u64, buf: &mut [u8]) -> io::Result<usize> {
|
||||
self.read_impl(Some(offset), buf).await
|
||||
}
|
||||
|
||||
/// Reads exactly `buf.len()` bytes starting at `offset`.
|
||||
pub async fn read_exact_at(&self, mut offset: u64, mut buf: &mut [u8]) -> io::Result<()> {
|
||||
while !buf.is_empty() {
|
||||
let read = self.read_at(offset, buf).await?;
|
||||
@@ -124,10 +149,12 @@ impl File {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Writes bytes starting at `offset` without using the shared file cursor.
|
||||
pub async fn write_at(&self, offset: u64, buf: &[u8]) -> io::Result<usize> {
|
||||
self.write_impl(Some(offset), buf).await
|
||||
}
|
||||
|
||||
/// Writes the entire buffer starting at `offset`.
|
||||
pub async fn write_all_at(&self, mut offset: u64, mut buf: &[u8]) -> io::Result<()> {
|
||||
while !buf.is_empty() {
|
||||
let written = self.write_at(offset, buf).await?;
|
||||
@@ -143,6 +170,7 @@ impl File {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Returns metadata for this file handle.
|
||||
pub async fn metadata(&self) -> io::Result<Metadata> {
|
||||
sys_fs::metadata(FsOp::Metadata {
|
||||
target: MetadataTarget::File(self.raw_fd()),
|
||||
@@ -152,6 +180,7 @@ impl File {
|
||||
.map(Metadata::from_raw)
|
||||
}
|
||||
|
||||
/// Truncates or extends the underlying file to `len` bytes.
|
||||
pub async fn set_len(&self, len: u64) -> io::Result<()> {
|
||||
sys_fs::set_len(FsOp::SetLen {
|
||||
fd: self.raw_fd(),
|
||||
@@ -160,6 +189,9 @@ impl File {
|
||||
.await
|
||||
}
|
||||
|
||||
/// Duplicates the underlying file description.
|
||||
///
|
||||
/// As with `std::fs::File::try_clone`, the cloned handle shares kernel-managed cursor state.
|
||||
pub async fn try_clone(&self) -> io::Result<Self> {
|
||||
sys_fs::try_clone(FsOp::Duplicate { fd: self.raw_fd() })
|
||||
.await
|
||||
@@ -200,42 +232,63 @@ impl File {
|
||||
}
|
||||
|
||||
impl OpenOptions {
|
||||
/// Creates a blank set of open options.
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
inner: OpOpenOptions::default(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Controls read access.
|
||||
pub fn read(&mut self, value: bool) -> &mut Self {
|
||||
self.inner.read = value;
|
||||
self
|
||||
}
|
||||
|
||||
/// Controls write access.
|
||||
pub fn write(&mut self, value: bool) -> &mut Self {
|
||||
self.inner.write = value;
|
||||
self
|
||||
}
|
||||
|
||||
/// Controls append mode.
|
||||
pub fn append(&mut self, value: bool) -> &mut Self {
|
||||
self.inner.append = value;
|
||||
self
|
||||
}
|
||||
|
||||
/// Controls whether the file is truncated after opening.
|
||||
pub fn truncate(&mut self, value: bool) -> &mut Self {
|
||||
self.inner.truncate = value;
|
||||
self
|
||||
}
|
||||
|
||||
/// Controls whether the file is created if it does not already exist.
|
||||
pub fn create(&mut self, value: bool) -> &mut Self {
|
||||
self.inner.create = value;
|
||||
self
|
||||
}
|
||||
|
||||
/// Controls whether opening must create a brand-new file.
|
||||
pub fn create_new(&mut self, value: bool) -> &mut Self {
|
||||
self.inner.create_new = value;
|
||||
self
|
||||
}
|
||||
|
||||
/// Opens a file with the configured options.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # let _ = || async {
|
||||
/// let file = ruin_runtime::fs::OpenOptions::new()
|
||||
/// .read(true)
|
||||
/// .write(true)
|
||||
/// .open("example.txt")
|
||||
/// .await;
|
||||
/// # let _ = file;
|
||||
/// # };
|
||||
/// ```
|
||||
pub async fn open(&self, path: impl AsRef<Path>) -> io::Result<File> {
|
||||
sys_fs::open(FsOp::Open {
|
||||
path: path.as_ref().to_path_buf(),
|
||||
@@ -257,32 +310,39 @@ impl Metadata {
|
||||
Self { inner }
|
||||
}
|
||||
|
||||
/// Returns the file length in bytes.
|
||||
pub fn len(&self) -> u64 {
|
||||
self.inner.len
|
||||
}
|
||||
|
||||
/// Returns `true` if the file length is zero.
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.len() == 0
|
||||
}
|
||||
|
||||
/// Returns `true` if this metadata describes a regular file.
|
||||
pub fn is_file(&self) -> bool {
|
||||
self.inner.file_type == RawFileType::File
|
||||
}
|
||||
|
||||
/// Returns `true` if this metadata describes a directory.
|
||||
pub fn is_dir(&self) -> bool {
|
||||
self.inner.file_type == RawFileType::Directory
|
||||
}
|
||||
|
||||
/// Returns `true` if this metadata describes a symbolic link.
|
||||
pub fn is_symlink(&self) -> bool {
|
||||
self.inner.file_type == RawFileType::Symlink
|
||||
}
|
||||
|
||||
/// Returns the raw POSIX mode bits reported by the platform backend.
|
||||
pub fn mode(&self) -> u16 {
|
||||
self.inner.mode
|
||||
}
|
||||
}
|
||||
|
||||
impl ReadDir {
|
||||
/// Returns the next directory entry, or `None` once the stream is exhausted.
|
||||
pub async fn next_entry(&mut self) -> io::Result<Option<DirEntry>> {
|
||||
self.inner
|
||||
.next_entry()
|
||||
@@ -292,19 +352,23 @@ impl ReadDir {
|
||||
}
|
||||
|
||||
impl DirEntry {
|
||||
/// Returns the full path to this directory entry.
|
||||
pub fn path(&self) -> PathBuf {
|
||||
self.inner.path.clone()
|
||||
}
|
||||
|
||||
/// Returns the file name portion of this directory entry.
|
||||
pub fn file_name(&self) -> &OsStr {
|
||||
self.inner.file_name.as_os_str()
|
||||
}
|
||||
|
||||
/// Resolves metadata for this entry.
|
||||
pub async fn metadata(&self) -> io::Result<Metadata> {
|
||||
metadata(self.path()).await
|
||||
}
|
||||
}
|
||||
|
||||
/// Reads the entire contents of a file into memory.
|
||||
pub async fn read(path: impl AsRef<Path>) -> io::Result<Vec<u8>> {
|
||||
let mut file = File::open(path.as_ref()).await?;
|
||||
let mut output = Vec::new();
|
||||
@@ -319,11 +383,13 @@ pub async fn read(path: impl AsRef<Path>) -> io::Result<Vec<u8>> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Reads the entire contents of a UTF-8 file into a [`String`].
|
||||
pub async fn read_to_string(path: impl AsRef<Path>) -> io::Result<String> {
|
||||
let bytes = read(path).await?;
|
||||
String::from_utf8(bytes).map_err(|error| io::Error::new(io::ErrorKind::InvalidData, error))
|
||||
}
|
||||
|
||||
/// Replaces the contents of a file with `data`, creating it if needed.
|
||||
pub async fn write(path: impl AsRef<Path>, data: impl AsRef<[u8]>) -> io::Result<()> {
|
||||
let mut file = OpenOptions::new()
|
||||
.write(true)
|
||||
@@ -334,6 +400,7 @@ pub async fn write(path: impl AsRef<Path>, data: impl AsRef<[u8]>) -> io::Result
|
||||
file.write_all(data.as_ref()).await
|
||||
}
|
||||
|
||||
/// Returns metadata for a filesystem path.
|
||||
pub async fn metadata(path: impl AsRef<Path>) -> io::Result<Metadata> {
|
||||
sys_fs::metadata(FsOp::Metadata {
|
||||
target: MetadataTarget::Path(path.as_ref().to_path_buf()),
|
||||
@@ -343,6 +410,7 @@ pub async fn metadata(path: impl AsRef<Path>) -> io::Result<Metadata> {
|
||||
.map(Metadata::from_raw)
|
||||
}
|
||||
|
||||
/// Creates a single directory.
|
||||
pub async fn create_dir(path: impl AsRef<Path>) -> io::Result<()> {
|
||||
sys_fs::create_dir(FsOp::CreateDir {
|
||||
path: path.as_ref().to_path_buf(),
|
||||
@@ -352,6 +420,7 @@ pub async fn create_dir(path: impl AsRef<Path>) -> io::Result<()> {
|
||||
.await
|
||||
}
|
||||
|
||||
/// Creates a directory and any missing parent directories.
|
||||
pub async fn create_dir_all(path: impl AsRef<Path>) -> io::Result<()> {
|
||||
let path = path.as_ref();
|
||||
let mut current = PathBuf::new();
|
||||
@@ -372,6 +441,7 @@ pub async fn create_dir_all(path: impl AsRef<Path>) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Removes a file.
|
||||
pub async fn remove_file(path: impl AsRef<Path>) -> io::Result<()> {
|
||||
sys_fs::remove_file(FsOp::RemoveFile {
|
||||
path: path.as_ref().to_path_buf(),
|
||||
@@ -379,6 +449,7 @@ pub async fn remove_file(path: impl AsRef<Path>) -> io::Result<()> {
|
||||
.await
|
||||
}
|
||||
|
||||
/// Removes an empty directory.
|
||||
pub async fn remove_dir(path: impl AsRef<Path>) -> io::Result<()> {
|
||||
sys_fs::remove_dir(FsOp::RemoveDir {
|
||||
path: path.as_ref().to_path_buf(),
|
||||
@@ -386,6 +457,7 @@ pub async fn remove_dir(path: impl AsRef<Path>) -> io::Result<()> {
|
||||
.await
|
||||
}
|
||||
|
||||
/// Renames or moves a filesystem entry.
|
||||
pub async fn rename(from: impl AsRef<Path>, to: impl AsRef<Path>) -> io::Result<()> {
|
||||
sys_fs::rename(FsOp::Rename {
|
||||
from: from.as_ref().to_path_buf(),
|
||||
@@ -394,6 +466,16 @@ pub async fn rename(from: impl AsRef<Path>, to: impl AsRef<Path>) -> io::Result<
|
||||
.await
|
||||
}
|
||||
|
||||
/// Opens an async directory-entry stream.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # let _ = || async {
|
||||
/// let mut entries = ruin_runtime::fs::read_dir(".").await.unwrap();
|
||||
/// let _ = entries.next_entry().await;
|
||||
/// # };
|
||||
/// ```
|
||||
pub async fn read_dir(path: impl AsRef<Path>) -> io::Result<ReadDir> {
|
||||
sys_fs::read_dir(FsOp::ReadDir {
|
||||
path: path.as_ref().to_path_buf(),
|
||||
|
||||
Reference in New Issue
Block a user