Skip to content

Commit

Permalink
Use more custom traits. (#346)
Browse files Browse the repository at this point in the history
Use custom traits instead of libstd's `OpenOptionsExt`, `FileExt`,
and `DirBuilderExt`.
  • Loading branch information
sunfishcode authored Jan 11, 2024
1 parent e3f9875 commit 6a264e2
Show file tree
Hide file tree
Showing 33 changed files with 375 additions and 92 deletions.
4 changes: 2 additions & 2 deletions cap-async-std/src/fs/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,9 @@ pub use cap_primitives::fs::{DirBuilder, FileType, Metadata, OpenOptions, Permis
// Re-export conditional types from `cap_primitives`.
#[cfg(any(unix, target_os = "vxworks", all(windows, windows_file_type_ext)))]
pub use cap_primitives::fs::FileTypeExt;
pub use cap_primitives::fs::MetadataExt;
pub use cap_primitives::fs::{FileExt, OpenOptionsExt, MetadataExt};
#[cfg(unix)]
pub use cap_primitives::fs::PermissionsExt;
pub use cap_primitives::fs::{DirBuilderExt, PermissionsExt};

// Re-export things from `async_std` that we can use as-is.
#[cfg(target_os = "wasi")]
Expand Down
4 changes: 2 additions & 2 deletions cap-async-std/src/fs_utf8/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@ pub use crate::fs::{DirBuilder, FileType, Metadata, OpenOptions, Permissions};
// Re-export conditional types from `cap_primitives`.
#[cfg(any(unix, target_os = "vxworks", all(windows, windows_file_type_ext)))]
pub use cap_primitives::fs::FileTypeExt;
pub use cap_primitives::fs::MetadataExt;
pub use cap_primitives::fs::{FileExt, OpenOptionsExt, MetadataExt};
#[cfg(unix)]
pub use cap_primitives::fs::PermissionsExt;
pub use cap_primitives::fs::{DirBuilderExt, PermissionsExt};

// Re-export `camino` to make it easy for users to depend on the same
// version we do, because we use its types in our public API.
Expand Down
6 changes: 2 additions & 4 deletions cap-fs-ext/src/dir_ext.rs
Original file line number Diff line number Diff line change
Expand Up @@ -595,8 +595,7 @@ impl DirExt for cap_std::fs::Dir {
fn remove_file_or_symlink<P: AsRef<Path>>(&self, path: P) -> io::Result<()> {
use crate::OpenOptionsFollowExt;
use cap_primitives::fs::_WindowsByHandle;
use cap_std::fs::OpenOptions;
use std::os::windows::fs::OpenOptionsExt;
use cap_std::fs::{OpenOptions, OpenOptionsExt};
use windows_sys::Win32::Storage::FileSystem::{
DELETE, FILE_ATTRIBUTE_DIRECTORY, FILE_FLAG_BACKUP_SEMANTICS,
FILE_FLAG_OPEN_REPARSE_POINT,
Expand Down Expand Up @@ -1092,8 +1091,7 @@ impl DirExtUtf8 for cap_std::fs_utf8::Dir {
fn remove_file_or_symlink<P: AsRef<Utf8Path>>(&self, path: P) -> io::Result<()> {
use crate::OpenOptionsFollowExt;
use cap_primitives::fs::_WindowsByHandle;
use cap_std::fs::OpenOptions;
use std::os::windows::fs::OpenOptionsExt;
use cap_std::fs::{OpenOptions, OpenOptionsExt};
use windows_sys::Win32::Storage::FileSystem::{
DELETE, FILE_ATTRIBUTE_DIRECTORY, FILE_FLAG_BACKUP_SEMANTICS,
FILE_FLAG_OPEN_REPARSE_POINT,
Expand Down
1 change: 1 addition & 0 deletions cap-primitives/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use std::env::var;
use std::io::Write;

fn main() {
use_feature_or_nothing("unix_file_vectored_at");
use_feature_or_nothing("windows_by_handle"); // https://github.com/rust-lang/rust/issues/63010
// https://doc.rust-lang.org/unstable-book/library-features/windows-file-type-ext.html
use_feature_or_nothing("windows_file_type_ext");
Expand Down
10 changes: 9 additions & 1 deletion cap-primitives/src/fs/dir_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,16 @@ impl DirBuilder {
}
}

/// Unix-specific extensions to [`fs::DirBuilder`].
#[cfg(unix)]
impl std::os::unix::fs::DirBuilderExt for DirBuilder {
pub trait DirBuilderExt {
/// Sets the mode to create new directories with. This option defaults to
/// 0o777.
fn mode(&mut self, mode: u32) -> &mut Self;
}

#[cfg(unix)]
impl DirBuilderExt for DirBuilder {
#[inline]
fn mode(&mut self, mode: u32) -> &mut Self {
self.options.mode(mode);
Expand Down
6 changes: 3 additions & 3 deletions cap-primitives/src/fs/dir_options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ impl DirOptions {
}

#[cfg(unix)]
impl std::os::unix::fs::DirBuilderExt for DirOptions {
impl crate::fs::DirBuilderExt for DirOptions {
#[inline]
fn mode(&mut self, mode: u32) -> &mut Self {
self.ext.mode(mode);
Expand All @@ -34,7 +34,7 @@ impl std::os::unix::fs::DirBuilderExt for DirOptions {
}

#[cfg(target_os = "vxworks")]
impl std::os::vxworks::fs::DirBuilderExt for DirOptions {
impl crate::fs::DirBuilderExt for DirOptions {
#[inline]
fn mode(&mut self, mode: u32) -> &mut Self {
self.ext.mode(mode);
Expand All @@ -46,7 +46,7 @@ impl std::os::vxworks::fs::DirBuilderExt for DirOptions {
impl arbitrary::Arbitrary<'_> for DirOptions {
fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
#[cfg(any(unix, target_os = "vxworks"))]
use std::os::unix::fs::DirBuilderExt;
use crate::fs::DirBuilderExt;

#[allow(unused_mut)]
let mut dir_options = Self::new();
Expand Down
187 changes: 187 additions & 0 deletions cap-primitives/src/fs/file.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
use std::io;

/// Unix-specific extensions to [`fs::File`].
#[cfg(unix)]
pub trait FileExt {
/// Reads a number of bytes starting from a given offset.
fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize>;

/// Like `read_at`, except that it reads into a slice of buffers.
#[cfg(feature = "unix_file_vectored_at")]
fn read_vectored_at(
&self,
bufs: &mut [std::io::IoSliceMut<'_>],
offset: u64,
) -> io::Result<usize> {
io::default_read_vectored(|b| self.read_at(b, offset), bufs)
}

/// Reads the exact number of bytes required to fill `buf` from the given offset.
fn read_exact_at(&self, mut buf: &mut [u8], mut offset: u64) -> io::Result<()> {
while !buf.is_empty() {
match self.read_at(buf, offset) {
Ok(0) => break,
Ok(n) => {
let tmp = buf;
buf = &mut tmp[n..];
offset += n as u64;
}
Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {}
Err(e) => return Err(e),
}
}
if !buf.is_empty() {
Err(io::Error::new(
io::ErrorKind::UnexpectedEof,
"failed to fill whole buffer",
))
} else {
Ok(())
}
}

/// Writes a number of bytes starting from a given offset.
fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize>;

/// Like `write_at`, except that it writes from a slice of buffers.
#[cfg(feature = "unix_file_vectored_at")]
fn write_vectored_at(&self, bufs: &[std::io::IoSlice<'_>], offset: u64) -> io::Result<usize> {
io::default_write_vectored(|b| self.write_at(b, offset), bufs)
}

/// Attempts to write an entire buffer starting from a given offset.
fn write_all_at(&self, mut buf: &[u8], mut offset: u64) -> io::Result<()> {
while !buf.is_empty() {
match self.write_at(buf, offset) {
Ok(0) => {
return Err(io::Error::new(
io::ErrorKind::WriteZero,
"failed to write whole buffer",
));
}
Ok(n) => {
buf = &buf[n..];
offset += n as u64
}
Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {}
Err(e) => return Err(e),
}
}
Ok(())
}
}

/// WASI-specific extensions to [`fs::File`].
#[cfg(target_os = "wasi")]
pub trait FileExt {
/// Reads a number of bytes starting from a given offset.
fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> {
let bufs = &mut [std::io::IoSliceMut::new(buf)];
self.read_vectored_at(bufs, offset)
}

/// Reads a number of bytes starting from a given offset.
fn read_vectored_at(
&self,
bufs: &mut [std::io::IoSliceMut<'_>],
offset: u64,
) -> io::Result<usize>;

/// Reads the exact number of byte required to fill `buf` from the given offset.
fn read_exact_at(&self, mut buf: &mut [u8], mut offset: u64) -> io::Result<()> {
while !buf.is_empty() {
match self.read_at(buf, offset) {
Ok(0) => break,
Ok(n) => {
let tmp = buf;
buf = &mut tmp[n..];
offset += n as u64;
}
Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {}
Err(e) => return Err(e),
}
}
if !buf.is_empty() {
Err(io::Error::new(
io::ErrorKind::UnexpectedEof,
"failed to fill whole buffer",
))
} else {
Ok(())
}
}

/// Writes a number of bytes starting from a given offset.
fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> {
let bufs = &[std::io::IoSlice::new(buf)];
self.write_vectored_at(bufs, offset)
}

/// Writes a number of bytes starting from a given offset.
fn write_vectored_at(&self, bufs: &[std::io::IoSlice<'_>], offset: u64) -> io::Result<usize>;

/// Attempts to write an entire buffer starting from a given offset.
fn write_all_at(&self, mut buf: &[u8], mut offset: u64) -> io::Result<()> {
while !buf.is_empty() {
match self.write_at(buf, offset) {
Ok(0) => {
return Err(io::Error::new(
io::ErrorKind::WriteZero,
"failed to write whole buffer",
));
}
Ok(n) => {
buf = &buf[n..];
offset += n as u64
}
Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {}
Err(e) => return Err(e),
}
}
Ok(())
}

/// Returns the current position within the file.
fn tell(&self) -> io::Result<u64>;

/// Adjust the flags associated with this file.
fn fdstat_set_flags(&self, flags: u16) -> std::io::Result<()>;

/// Adjust the rights associated with this file.
fn fdstat_set_rights(&self, rights: u64, inheriting: u64) -> std::io::Result<()>;

/// Provide file advisory information on a file descriptor.
fn advise(&self, offset: u64, len: u64, advice: u8) -> std::io::Result<()>;

/// Force the allocation of space in a file.
fn allocate(&self, offset: u64, len: u64) -> std::io::Result<()>;

/// Create a directory.
fn create_directory<P: AsRef<std::path::Path>>(&self, dir: P) -> std::io::Result<()>;

/// Read the contents of a symbolic link.
fn read_link<P: AsRef<std::path::Path>>(&self, path: P) -> io::Result<std::path::PathBuf>;

/// Return the attributes of a file or directory.
fn metadata_at<P: AsRef<std::path::Path>>(
&self,
lookup_flags: u32,
path: P,
) -> io::Result<std::fs::Metadata>;

/// Unlink a file.
fn remove_file<P: AsRef<std::path::Path>>(&self, path: P) -> io::Result<()>;

/// Remove a directory.
fn remove_directory<P: AsRef<std::path::Path>>(&self, path: P) -> io::Result<()>;
}

/// Windows-specific extensions to [`fs::File`].
#[cfg(windows)]
pub trait FileExt {
/// Seeks to a given position and reads a number of bytes.
fn seek_read(&self, buf: &mut [u8], offset: u64) -> io::Result<usize>;

/// Seeks to a given position and writes a number of bytes.
fn seek_write(&self, buf: &[u8], offset: u64) -> io::Result<usize>;
}
6 changes: 4 additions & 2 deletions cap-primitives/src/fs/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ mod create_dir;
mod dir_builder;
mod dir_entry;
mod dir_options;
mod file;
#[cfg(not(any(target_os = "android", target_os = "linux", windows)))]
mod file_path_by_searching;
mod file_type;
Expand Down Expand Up @@ -62,11 +63,12 @@ pub use access::{access, AccessModes, AccessType};
pub use canonicalize::canonicalize;
pub use copy::copy;
pub use create_dir::create_dir;
pub use dir_builder::DirBuilder;
pub use dir_builder::*;
pub use dir_entry::DirEntry;
#[cfg(windows)]
pub use dir_entry::_WindowsDirEntryExt;
pub use dir_options::DirOptions;
pub use file::FileExt;
pub use file_type::FileType;
#[cfg(any(unix, target_os = "vxworks", all(windows, windows_file_type_ext)))]
pub use file_type::FileTypeExt;
Expand All @@ -81,7 +83,7 @@ pub use metadata::{Metadata, MetadataExt};
pub use open::open;
pub use open_ambient::open_ambient;
pub use open_dir::*;
pub use open_options::OpenOptions;
pub use open_options::*;
pub use permissions::Permissions;
#[cfg(unix)]
pub use permissions::PermissionsExt;
Expand Down
Loading

0 comments on commit 6a264e2

Please sign in to comment.