#[doc(hidden)]
#[macro_export]
macro_rules! __vfsi_file_methods {
($file:ty) => {
fn path(&self) -> &Path {
<$file>::path(self)
}
fn try_close(&mut self) -> Result<()> {
<$file>::try_close(self)
}
fn is_closed(&self) -> bool {
<$file>::is_closed(self)
}
fn close(self) -> Result<()> {
<$file>::close(self)
}
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! __vfsi_client_methods {
($client:ty, $receiver:path, $vread_native:expr, $read_receiver:path,
$vwrite_native:expr, $vwrite_all_native:expr, $metadata:expr,
$write_receiver:path, $page_capacity:expr, $open_native:expr, $stream_native:expr) => {
$crate::__vfsi_client_methods!(
$client, $receiver, $vread_native, $read_receiver, $vwrite_native,
$vwrite_all_native, $metadata, $write_receiver, $page_capacity,
$open_native, $stream_native;
vrename, vsymlink, vreadlink, vhardlink, vstatfs, vsetattrs,
limits, vopen, vclose, vmkdir, vcopy, capabilities
);
};
($client:ty, $receiver:path, $vread_native:expr, $read_receiver:path,
$vwrite_native:expr, $vwrite_all_native:expr, $metadata:expr,
$write_receiver:path, $page_capacity:expr, $open_native:expr, $stream_native:expr;
$rename:ident, $symlink:ident, $readlink:ident, $hardlink:ident,
$statfs:ident, $setattrs:ident, $limits:ident, $open:ident,
$close:ident, $mkdir:ident, $copy:ident, $capabilities:ident) => {
fn vrename<P: AsRef<Path>, Q: AsRef<Path>>(
&self,
pairs: &[(P, Q)],
options: vfsi_core::api::RenameMode,
) -> Result<()> {
<$client>::$rename($receiver(self), pairs, options)
}
fn vlistdirs<P: AsRef<Path>>(
&self,
paths: &[P],
options: vfsi_core::api::ListDirOptions,
callback: impl FnMut(usize, DirectoryListing) -> Result<std::ops::ControlFlow<()>>,
) -> Result<Vec<$crate::TraversalCompletion>> {
$crate::application::visit_directory_pages(
paths,
options,
self.limits(),
|paths| ($page_capacity)($receiver(self), paths),
|path| {
let metadata = self.vgetattrs(
&[path],
vfsi_core::api::AttrsOptions::new()
.with_fields(vfsi_core::api::Attributes::MODE)
.with_follow_final_symlink(false),
)?;
if metadata.len() != 1 {
return Err(vfsi_core::api::Error::transport(
None,
"invalid directory root metadata count",
));
}
if !metadata[0].is_dir() {
return Err(vfsi_core::api::Error::client(0, libc::ENOTDIR as u32)
.with_context("visit_dirs", path));
}
Ok(())
},
|paths, cursors, page_size, max_entries| {
<$client>::read_dir_pages_with_fields(
$receiver(self),
paths,
options.fields(),
cursors,
page_size,
max_entries,
options.allow_symlink_components(),
)
},
callback,
)
}
fn vstream<P: AsRef<Path>>(
&self,
paths: &[P],
options: $crate::StreamOptions,
mut callback: impl FnMut(usize, u64, &[u8]) -> Result<std::ops::ControlFlow<()>>,
) -> Result<Vec<$crate::StreamCompletion>> {
let mut output = Vec::new();
for (index, path) in paths.iter().enumerate() {
let completion =
($stream_native)($receiver(self), path, options, |offset, data| {
callback(index, offset, data)
})
.map_err(|error| $crate::application::vector_index(error, index))?;
output.push(completion);
if matches!(completion, $crate::StreamCompletion::Stopped { .. }) {
break;
}
}
Ok(output)
}
fn capabilities(&self) -> Result<vfsi_core::Capabilities> {
<$client>::$capabilities($receiver(self))
}
fn vsymlink<P: AsRef<Path>, Q: AsRef<Path>>(&self, pairs: &[(P, Q)]) -> Result<()> {
<$client>::$symlink($receiver(self), pairs)
}
fn vreadlink<P: AsRef<Path>>(&self, paths: &[P]) -> Result<Vec<std::path::PathBuf>> {
<$client>::$readlink($receiver(self), paths)
}
fn vhardlink<P: AsRef<Path>, Q: AsRef<Path>>(&self, pairs: &[(P, Q)]) -> Result<()> {
<$client>::$hardlink($receiver(self), pairs)
}
fn vstatfs<P: vfsi_core::AsTarget<Self::File>>(
&self,
targets: &[P],
) -> Result<Vec<vfsi_core::FilesystemStats>> {
<$client>::$statfs($receiver(self), targets)
}
fn vsetattrs<P: vfsi_core::AsTarget<Self::File>>(
&self,
updates: &[vfsi_core::SetAttrsOp<P>],
) -> Result<()> {
<$client>::$setattrs($receiver(self), updates)
}
fn vfsync(&self, files: &[&Self::File], mode: vfsi_core::api::SyncMode) -> Result<()> {
<$client>::vfsync_impl($receiver(self), files, mode)
}
fn vgetattrs<P: vfsi_core::AsTarget<Self::File>>(
&self,
paths: &[P],
options: vfsi_core::api::AttrsOptions,
) -> Result<Vec<Attrs>> {
($metadata)($receiver(self), paths, options)
}
fn limits(&self) -> ResourceLimits {
<$client>::$limits($receiver(self))
}
fn vopen_dirs<P: AsRef<Path>>(&self, paths: &[P]) -> Result<Vec<Self::Dir>> {
<$client>::vopen_dirs_impl($receiver(self), paths)
}
fn vremove_dir_contents(&self, dirs: &[&Self::Dir], options: $crate::RemoveOptions) -> Result<()> {
<$client>::vremove_dir_contents_impl($receiver(self), dirs, options)
}
fn vopen(&self, requests: &[OpenOp]) -> Result<Vec<Self::File>> {
if requests.len() == 1 {
return ($open_native)($receiver(self), requests[0].clone()).map(|file| vec![file]);
}
<$client>::$open($receiver(self), requests)
}
fn vread<'a>(
&self,
ops: impl IntoIterator<Item = vfsi_core::api::ReadOp<'a, Self::File>>,
options: vfsi_core::api::ReadOptions,
) -> Result<Vec<vfsi_core::api::ReadResult>> {
($vread_native)($read_receiver(self), ops, options)
}
fn vwrite<'a>(
&self,
requests: &[vfsi_core::api::WriteOp<'a, Self::File>],
options: vfsi_core::api::WriteOptions,
) -> Result<Vec<WriteResult>> {
let result = if options.write_all() {
($vwrite_all_native)($write_receiver(self), requests)
} else {
($vwrite_native)($write_receiver(self), requests)
};
result.map_err($crate::application::public_write_error)
}
fn vclose(&self, files: &mut [Self::File]) -> Result<()> {
<$client>::$close($receiver(self), files)
}
fn vmkdir<P: AsRef<Path>>(&self, paths: &[vfsi_core::MkDirOp<P>]) -> Result<()> {
<$client>::$mkdir($receiver(self), paths)
}
fn vcopy<P: AsRef<Path>, Q: AsRef<Path>>(
&self,
pairs: &[(P, Q)],
options: vfsi_core::api::CopyOptions,
) -> Result<()> {
<$client>::$copy($receiver(self), pairs, options)
}
fn vremove<P: AsRef<Path>>(
&self,
paths: &[P],
mode: vfsi_core::api::RemoveMode,
options: $crate::RemoveOptions,
) -> Result<()> {
match mode {
vfsi_core::api::RemoveMode::Entry | vfsi_core::api::RemoveMode::Tree => {
<$client>::vremove_impl(
$receiver(self),
paths,
mode == vfsi_core::api::RemoveMode::Tree,
options,
)
}
vfsi_core::api::RemoveMode::Contents => {
let mut first_error = None;
for (index, path) in paths.iter().enumerate() {
if let Err(error) = <$client>::remove_dir_contents_impl(
$receiver(self),
path,
options,
) {
let error = $crate::application::vector_index(error, index);
if error.is_transport() || !options.continue_on_error() {
return Err(error);
}
first_error.get_or_insert(error);
}
}
first_error.map_or(Ok(()), Err)
}
}
}
};
}