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(unix_file_vectored_at)]
fn read_vectored_at(&self, bufs: &mut [io::IoSliceMut<'_>], offset: u64) -> io::Result<usize> {
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(unix_file_vectored_at)]
fn write_vectored_at(&self, bufs: &[io::IoSlice<'_>], offset: u64) -> io::Result<usize> {
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(())
}
}
#[cfg(unix_file_vectored_at)]
fn default_read_vectored<F>(read: F, bufs: &mut [io::IoSliceMut<'_>]) -> io::Result<usize>
where
F: FnOnce(&mut [u8]) -> io::Result<usize>,
{
let buf = bufs
.iter_mut()
.find(|b| !b.is_empty())
.map_or(&mut [][..], |b| &mut **b);
read(buf)
}
#[cfg(unix_file_vectored_at)]
fn default_write_vectored<F>(write: F, bufs: &[io::IoSlice<'_>]) -> io::Result<usize>
where
F: FnOnce(&[u8]) -> io::Result<usize>,
{
let buf = bufs
.iter()
.find(|b| !b.is_empty())
.map_or(&[][..], |b| &**b);
write(buf)
}
/// 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 [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 [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 = &[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: &[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) -> io::Result<()>;
/// Adjust the rights associated with this file.
fn fdstat_set_rights(&self, rights: u64, inheriting: u64) -> io::Result<()>;
/// Provide file advisory information on a file descriptor.
fn advise(&self, offset: u64, len: u64, advice: u8) -> io::Result<()>;
/// Force the allocation of space in a file.
fn allocate(&self, offset: u64, len: u64) -> io::Result<()>;
/// Create a directory.
fn create_directory<P: AsRef<std::path::Path>>(&self, dir: P) -> 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>;
}