#![deny(unsafe_op_in_unsafe_fn)]
#![unstable(feature = "wasi_ext", issue = "71213")]
use crate::ffi::OsStr;
use crate::fs::{self, File, Metadata, OpenOptions};
use crate::io::{self, IoSlice, IoSliceMut};
use crate::path::{Path, PathBuf};
use crate::sys_common::{AsInner, AsInnerMut, FromInner};
#[allow(unused_imports)]
use io::{Read, Write};
pub trait FileExt {
fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result<usize> {
let bufs = &mut [IoSliceMut::new(buf)];
self.read_vectored_at(bufs, offset)
}
fn read_vectored_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result<usize>;
#[stable(feature = "rw_exact_all_at", since = "1.33.0")]
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::const_io_error!(io::ErrorKind::UnexpectedEof, "failed to fill whole buffer"))
} else {
Ok(())
}
}
fn write_at(&self, buf: &[u8], offset: u64) -> io::Result<usize> {
let bufs = &[IoSlice::new(buf)];
self.write_vectored_at(bufs, offset)
}
fn write_vectored_at(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result<usize>;
#[stable(feature = "rw_exact_all_at", since = "1.33.0")]
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::const_io_error!(
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(())
}
fn tell(&self) -> io::Result<u64>;
fn fdstat_set_flags(&self, flags: u16) -> io::Result<()>;
fn fdstat_set_rights(&self, rights: u64, inheriting: u64) -> io::Result<()>;
fn advise(&self, offset: u64, len: u64, advice: u8) -> io::Result<()>;
fn allocate(&self, offset: u64, len: u64) -> io::Result<()>;
fn create_directory<P: AsRef<Path>>(&self, dir: P) -> io::Result<()>;
fn read_link<P: AsRef<Path>>(&self, path: P) -> io::Result<PathBuf>;
fn metadata_at<P: AsRef<Path>>(&self, lookup_flags: u32, path: P) -> io::Result<Metadata>;
fn remove_file<P: AsRef<Path>>(&self, path: P) -> io::Result<()>;
fn remove_directory<P: AsRef<Path>>(&self, path: P) -> io::Result<()>;
}
impl FileExt for fs::File {
fn read_vectored_at(&self, bufs: &mut [IoSliceMut<'_>], offset: u64) -> io::Result<usize> {
self.as_inner().as_inner().pread(bufs, offset)
}
fn write_vectored_at(&self, bufs: &[IoSlice<'_>], offset: u64) -> io::Result<usize> {
self.as_inner().as_inner().pwrite(bufs, offset)
}
fn tell(&self) -> io::Result<u64> {
self.as_inner().as_inner().tell()
}
fn fdstat_set_flags(&self, flags: u16) -> io::Result<()> {
self.as_inner().as_inner().set_flags(flags)
}
fn fdstat_set_rights(&self, rights: u64, inheriting: u64) -> io::Result<()> {
self.as_inner().as_inner().set_rights(rights, inheriting)
}
fn advise(&self, offset: u64, len: u64, advice: u8) -> io::Result<()> {
let advice = match advice {
a if a == wasi::ADVICE_NORMAL.raw() => wasi::ADVICE_NORMAL,
a if a == wasi::ADVICE_SEQUENTIAL.raw() => wasi::ADVICE_SEQUENTIAL,
a if a == wasi::ADVICE_RANDOM.raw() => wasi::ADVICE_RANDOM,
a if a == wasi::ADVICE_WILLNEED.raw() => wasi::ADVICE_WILLNEED,
a if a == wasi::ADVICE_DONTNEED.raw() => wasi::ADVICE_DONTNEED,
a if a == wasi::ADVICE_NOREUSE.raw() => wasi::ADVICE_NOREUSE,
_ => {
return Err(io::const_io_error!(
io::ErrorKind::InvalidInput,
"invalid parameter 'advice'",
));
}
};
self.as_inner().as_inner().advise(offset, len, advice)
}
fn allocate(&self, offset: u64, len: u64) -> io::Result<()> {
self.as_inner().as_inner().allocate(offset, len)
}
fn create_directory<P: AsRef<Path>>(&self, dir: P) -> io::Result<()> {
self.as_inner().as_inner().create_directory(osstr2str(dir.as_ref().as_ref())?)
}
fn read_link<P: AsRef<Path>>(&self, path: P) -> io::Result<PathBuf> {
self.as_inner().read_link(path.as_ref())
}
fn metadata_at<P: AsRef<Path>>(&self, lookup_flags: u32, path: P) -> io::Result<Metadata> {
let m = self.as_inner().metadata_at(lookup_flags, path.as_ref())?;
Ok(FromInner::from_inner(m))
}
fn remove_file<P: AsRef<Path>>(&self, path: P) -> io::Result<()> {
self.as_inner().as_inner().unlink_file(osstr2str(path.as_ref().as_ref())?)
}
fn remove_directory<P: AsRef<Path>>(&self, path: P) -> io::Result<()> {
self.as_inner().as_inner().remove_directory(osstr2str(path.as_ref().as_ref())?)
}
}
pub trait OpenOptionsExt {
fn lookup_flags(&mut self, flags: u32) -> &mut Self;
fn directory(&mut self, dir: bool) -> &mut Self;
fn dsync(&mut self, dsync: bool) -> &mut Self;
fn nonblock(&mut self, nonblock: bool) -> &mut Self;
fn rsync(&mut self, rsync: bool) -> &mut Self;
fn sync(&mut self, sync: bool) -> &mut Self;
fn fs_rights_base(&mut self, rights: u64) -> &mut Self;
fn fs_rights_inheriting(&mut self, rights: u64) -> &mut Self;
fn open_at<P: AsRef<Path>>(&self, file: &File, path: P) -> io::Result<File>;
}
impl OpenOptionsExt for OpenOptions {
fn lookup_flags(&mut self, flags: u32) -> &mut OpenOptions {
self.as_inner_mut().lookup_flags(flags);
self
}
fn directory(&mut self, dir: bool) -> &mut OpenOptions {
self.as_inner_mut().directory(dir);
self
}
fn dsync(&mut self, enabled: bool) -> &mut OpenOptions {
self.as_inner_mut().dsync(enabled);
self
}
fn nonblock(&mut self, enabled: bool) -> &mut OpenOptions {
self.as_inner_mut().nonblock(enabled);
self
}
fn rsync(&mut self, enabled: bool) -> &mut OpenOptions {
self.as_inner_mut().rsync(enabled);
self
}
fn sync(&mut self, enabled: bool) -> &mut OpenOptions {
self.as_inner_mut().sync(enabled);
self
}
fn fs_rights_base(&mut self, rights: u64) -> &mut OpenOptions {
self.as_inner_mut().fs_rights_base(rights);
self
}
fn fs_rights_inheriting(&mut self, rights: u64) -> &mut OpenOptions {
self.as_inner_mut().fs_rights_inheriting(rights);
self
}
fn open_at<P: AsRef<Path>>(&self, file: &File, path: P) -> io::Result<File> {
let inner = file.as_inner().open_at(path.as_ref(), self.as_inner())?;
Ok(File::from_inner(inner))
}
}
pub trait MetadataExt {
fn dev(&self) -> u64;
fn ino(&self) -> u64;
fn nlink(&self) -> u64;
fn size(&self) -> u64;
fn atim(&self) -> u64;
fn mtim(&self) -> u64;
fn ctim(&self) -> u64;
}
impl MetadataExt for fs::Metadata {
fn dev(&self) -> u64 {
self.as_inner().as_wasi().dev
}
fn ino(&self) -> u64 {
self.as_inner().as_wasi().ino
}
fn nlink(&self) -> u64 {
self.as_inner().as_wasi().nlink
}
fn size(&self) -> u64 {
self.as_inner().as_wasi().size
}
fn atim(&self) -> u64 {
self.as_inner().as_wasi().atim
}
fn mtim(&self) -> u64 {
self.as_inner().as_wasi().mtim
}
fn ctim(&self) -> u64 {
self.as_inner().as_wasi().ctim
}
}
pub trait FileTypeExt {
fn is_block_device(&self) -> bool;
fn is_char_device(&self) -> bool;
fn is_socket_dgram(&self) -> bool;
fn is_socket_stream(&self) -> bool;
fn is_socket(&self) -> bool {
self.is_socket_stream() || self.is_socket_dgram()
}
}
impl FileTypeExt for fs::FileType {
fn is_block_device(&self) -> bool {
self.as_inner().bits() == wasi::FILETYPE_BLOCK_DEVICE
}
fn is_char_device(&self) -> bool {
self.as_inner().bits() == wasi::FILETYPE_CHARACTER_DEVICE
}
fn is_socket_dgram(&self) -> bool {
self.as_inner().bits() == wasi::FILETYPE_SOCKET_DGRAM
}
fn is_socket_stream(&self) -> bool {
self.as_inner().bits() == wasi::FILETYPE_SOCKET_STREAM
}
}
pub trait DirEntryExt {
fn ino(&self) -> u64;
}
impl DirEntryExt for fs::DirEntry {
fn ino(&self) -> u64 {
self.as_inner().ino()
}
}
pub fn link<P: AsRef<Path>, U: AsRef<Path>>(
old_fd: &File,
old_flags: u32,
old_path: P,
new_fd: &File,
new_path: U,
) -> io::Result<()> {
old_fd.as_inner().as_inner().link(
old_flags,
osstr2str(old_path.as_ref().as_ref())?,
new_fd.as_inner().as_inner(),
osstr2str(new_path.as_ref().as_ref())?,
)
}
pub fn rename<P: AsRef<Path>, U: AsRef<Path>>(
old_fd: &File,
old_path: P,
new_fd: &File,
new_path: U,
) -> io::Result<()> {
old_fd.as_inner().as_inner().rename(
osstr2str(old_path.as_ref().as_ref())?,
new_fd.as_inner().as_inner(),
osstr2str(new_path.as_ref().as_ref())?,
)
}
pub fn symlink<P: AsRef<Path>, U: AsRef<Path>>(
old_path: P,
fd: &File,
new_path: U,
) -> io::Result<()> {
fd.as_inner()
.as_inner()
.symlink(osstr2str(old_path.as_ref().as_ref())?, osstr2str(new_path.as_ref().as_ref())?)
}
pub fn symlink_path<P: AsRef<Path>, U: AsRef<Path>>(old_path: P, new_path: U) -> io::Result<()> {
crate::sys::fs::symlink(old_path.as_ref(), new_path.as_ref())
}
fn osstr2str(f: &OsStr) -> io::Result<&str> {
f.to_str()
.ok_or_else(|| io::const_io_error!(io::ErrorKind::Uncategorized, "input must be utf-8"))
}