use crate::{
file::OpenOptions,
lowlevel::{self, Extensions},
metadata::{MetaData, MetaDataBuilder, Permissions},
Auxiliary, Buffer, Error, Id, OwnedHandle, WriteEnd, WriteEndWithCachedId,
};
use std::{
borrow::Cow,
cmp::min,
convert::TryInto,
path::{Path, PathBuf},
};
use bytes::BytesMut;
mod dir;
pub use dir::{DirEntry, ReadDir};
type AwaitableStatus = lowlevel::AwaitableStatus<Buffer>;
type AwaitableAttrs = lowlevel::AwaitableAttrs<Buffer>;
type SendLinkingRequest =
fn(&mut WriteEnd, Id, Cow<'_, Path>, Cow<'_, Path>) -> Result<AwaitableStatus, Error>;
type SendRmRequest = fn(&mut WriteEnd, Id, Cow<'_, Path>) -> Result<AwaitableStatus, Error>;
type SendMetadataRequest = fn(&mut WriteEnd, Id, Cow<'_, Path>) -> Result<AwaitableAttrs, Error>;
#[derive(Debug, Clone)]
pub struct Fs {
write_end: WriteEndWithCachedId,
cwd: Box<Path>,
}
impl Fs {
pub(super) fn new(write_end: WriteEndWithCachedId, cwd: PathBuf) -> Self {
Self {
write_end,
cwd: cwd.into_boxed_path(),
}
}
fn get_auxiliary(&self) -> &Auxiliary {
self.write_end.get_auxiliary()
}
pub fn cwd(&self) -> &Path {
&self.cwd
}
pub fn set_cwd(&mut self, cwd: impl Into<PathBuf>) {
self.cwd = cwd.into().into_boxed_path();
}
fn concat_path_if_needed<'path>(&self, path: &'path Path) -> Cow<'path, Path> {
if path.is_absolute() || self.cwd.as_os_str().is_empty() {
Cow::Borrowed(path)
} else {
Cow::Owned(self.cwd.join(path))
}
}
}
impl Fs {
pub async fn open_dir(&mut self, path: impl AsRef<Path>) -> Result<Dir, Error> {
async fn inner(this: &mut Fs, path: &Path) -> Result<Dir, Error> {
let path = this.concat_path_if_needed(path);
this.write_end
.send_request(|write_end, id| Ok(write_end.send_opendir_request(id, path)?.wait()))
.await
.map(|handle| Dir(OwnedHandle::new(this.write_end.clone(), handle)))
}
inner(self, path.as_ref()).await
}
pub fn dir_builder(&mut self) -> DirBuilder<'_> {
DirBuilder {
fs: self,
metadata_builder: MetaDataBuilder::new(),
}
}
pub async fn create_dir(&mut self, path: impl AsRef<Path>) -> Result<(), Error> {
async fn inner(this: &mut Fs, path: &Path) -> Result<(), Error> {
this.dir_builder().create(path).await
}
inner(self, path.as_ref()).await
}
async fn remove_impl(&mut self, path: &Path, f: SendRmRequest) -> Result<(), Error> {
let path = self.concat_path_if_needed(path);
self.write_end
.send_request(|write_end, id| Ok(f(write_end, id, path)?.wait()))
.await
}
pub async fn remove_dir(&mut self, path: impl AsRef<Path>) -> Result<(), Error> {
self.remove_impl(path.as_ref(), WriteEnd::send_rmdir_request)
.await
}
pub async fn remove_file(&mut self, path: impl AsRef<Path>) -> Result<(), Error> {
self.remove_impl(path.as_ref(), WriteEnd::send_remove_request)
.await
}
pub async fn canonicalize(&mut self, path: impl AsRef<Path>) -> Result<PathBuf, Error> {
async fn inner(this: &mut Fs, path: &Path) -> Result<PathBuf, Error> {
let path = this.concat_path_if_needed(path);
let f = if this
.get_auxiliary()
.extensions()
.contains(Extensions::EXPAND_PATH)
{
WriteEnd::send_expand_path_request
} else {
WriteEnd::send_realpath_request
};
this.write_end
.send_request(|write_end, id| Ok(f(write_end, id, path)?.wait()))
.await
.map(Into::into)
}
inner(self, path.as_ref()).await
}
async fn linking_impl(
&mut self,
src: &Path,
dst: &Path,
f: SendLinkingRequest,
) -> Result<(), Error> {
let src = self.concat_path_if_needed(src);
let dst = self.concat_path_if_needed(dst);
self.write_end
.send_request(|write_end, id| Ok(f(write_end, id, src, dst)?.wait()))
.await
}
pub async fn hard_link(
&mut self,
src: impl AsRef<Path>,
dst: impl AsRef<Path>,
) -> Result<(), Error> {
async fn inner(this: &mut Fs, src: &Path, dst: &Path) -> Result<(), Error> {
if !this
.get_auxiliary()
.extensions()
.contains(Extensions::HARDLINK)
{
return Err(Error::UnsupportedExtension(&"hardlink"));
}
this.linking_impl(src, dst, WriteEnd::send_hardlink_request)
.await
}
inner(self, src.as_ref(), dst.as_ref()).await
}
pub async fn symlink(
&mut self,
src: impl AsRef<Path>,
dst: impl AsRef<Path>,
) -> Result<(), Error> {
self.linking_impl(src.as_ref(), dst.as_ref(), WriteEnd::send_symlink_request)
.await
}
pub async fn rename(
&mut self,
from: impl AsRef<Path>,
to: impl AsRef<Path>,
) -> Result<(), Error> {
async fn inner(this: &mut Fs, from: &Path, to: &Path) -> Result<(), Error> {
let f = if this
.get_auxiliary()
.extensions()
.contains(Extensions::POSIX_RENAME)
{
WriteEnd::send_posix_rename_request
} else {
WriteEnd::send_rename_request
};
this.linking_impl(from, to, f).await
}
inner(self, from.as_ref(), to.as_ref()).await
}
pub async fn read_link(&mut self, path: impl AsRef<Path>) -> Result<PathBuf, Error> {
async fn inner(this: &mut Fs, path: &Path) -> Result<PathBuf, Error> {
let path = this.concat_path_if_needed(path);
this.write_end
.send_request(|write_end, id| Ok(write_end.send_readlink_request(id, path)?.wait()))
.await
.map(Into::into)
}
inner(self, path.as_ref()).await
}
async fn set_metadata_impl(&mut self, path: &Path, metadata: MetaData) -> Result<(), Error> {
let path = self.concat_path_if_needed(path);
self.write_end
.send_request(|write_end, id| {
Ok(write_end
.send_setstat_request(id, path, metadata.into_inner())?
.wait())
})
.await
}
pub async fn set_metadata(
&mut self,
path: impl AsRef<Path>,
metadata: MetaData,
) -> Result<(), Error> {
self.set_metadata_impl(path.as_ref(), metadata).await
}
pub async fn set_permissions(
&mut self,
path: impl AsRef<Path>,
perm: Permissions,
) -> Result<(), Error> {
async fn inner(this: &mut Fs, path: &Path, perm: Permissions) -> Result<(), Error> {
this.set_metadata_impl(path, MetaDataBuilder::new().permissions(perm).create())
.await
}
inner(self, path.as_ref(), perm).await
}
async fn metadata_impl(
&mut self,
path: &Path,
f: SendMetadataRequest,
) -> Result<MetaData, Error> {
let path = self.concat_path_if_needed(path);
self.write_end
.send_request(|write_end, id| Ok(f(write_end, id, path)?.wait()))
.await
.map(MetaData::new)
}
pub async fn metadata(&mut self, path: impl AsRef<Path>) -> Result<MetaData, Error> {
self.metadata_impl(path.as_ref(), WriteEnd::send_stat_request)
.await
}
pub async fn symlink_metadata(&mut self, path: impl AsRef<Path>) -> Result<MetaData, Error> {
self.metadata_impl(path.as_ref(), WriteEnd::send_lstat_request)
.await
}
pub async fn read(&mut self, path: impl AsRef<Path>) -> Result<BytesMut, Error> {
async fn inner(this: &mut Fs, path: &Path) -> Result<BytesMut, Error> {
let path = this.concat_path_if_needed(path);
let mut file = OpenOptions::open_inner(
lowlevel::OpenOptions::new().read(true),
false,
false,
false,
path.as_ref(),
this.write_end.clone(),
)
.await?;
let max_read_len = file.max_read_len_impl();
let cap_to_reserve: usize = if let Some(len) = file.metadata().await?.len() {
len.saturating_add(300)
.try_into()
.unwrap_or(max_read_len as usize)
} else {
max_read_len as usize
};
let mut buffer = BytesMut::with_capacity(cap_to_reserve);
loop {
let cnt = buffer.len();
let n: u32 = if cnt <= cap_to_reserve {
(cap_to_reserve - cnt)
.saturating_add(300)
.try_into()
.map(|n| min(n, max_read_len))
.unwrap_or(max_read_len)
} else {
max_read_len
};
buffer.reserve(n.try_into().unwrap_or(usize::MAX));
if let Some(bytes) = file.read(n, buffer.split_off(cnt)).await? {
buffer.unsplit(bytes);
} else {
break Ok(buffer);
}
}
}
inner(self, path.as_ref()).await
}
pub async fn write(
&mut self,
path: impl AsRef<Path>,
content: impl AsRef<[u8]>,
) -> Result<(), Error> {
async fn inner(this: &mut Fs, path: &Path, content: &[u8]) -> Result<(), Error> {
let path = this.concat_path_if_needed(path);
OpenOptions::open_inner(
lowlevel::OpenOptions::new().write(true),
true,
true,
false,
path.as_ref(),
this.write_end.clone(),
)
.await?
.write_all(content)
.await
}
inner(self, path.as_ref(), content.as_ref()).await
}
}
#[repr(transparent)]
#[derive(Debug, Clone)]
pub struct Dir(OwnedHandle);
impl Dir {
pub fn read_dir(self) -> ReadDir {
ReadDir::new(self)
}
pub async fn close(self) -> Result<(), Error> {
self.0.close().await
}
}
#[derive(Debug)]
pub struct DirBuilder<'a> {
fs: &'a mut Fs,
metadata_builder: MetaDataBuilder,
}
impl DirBuilder<'_> {
pub fn reset(&mut self) -> &mut Self {
self.metadata_builder = MetaDataBuilder::new();
self
}
pub fn id(&mut self, (uid, gid): (u32, u32)) -> &mut Self {
self.metadata_builder.id((uid, gid));
self
}
pub fn permissions(&mut self, perm: Permissions) -> &mut Self {
self.metadata_builder.permissions(perm);
self
}
}
impl DirBuilder<'_> {
pub async fn create(&mut self, path: impl AsRef<Path>) -> Result<(), Error> {
async fn inner(this: &mut DirBuilder<'_>, path: &Path) -> Result<(), Error> {
let fs = &mut this.fs;
let path = fs.concat_path_if_needed(path);
let attrs = this.metadata_builder.create().into_inner();
fs.write_end
.send_request(|write_end, id| {
Ok(write_end.send_mkdir_request(id, path, attrs)?.wait())
})
.await
}
inner(self, path.as_ref()).await
}
}