Skip to main content

vfsi_core/api/
mod.rs

1//! Portable synchronous vectorized filesystem API.
2//!
3//! Implement [`Vfsi`] for native vector execution; [`VfsiExt`] is blanket
4//! implemented and composes scalar conveniences and workflows from those vectors.
5//! This module has no backend, native RPC, or runtime dependencies.
6//!
7//! Application read/write operations live here rather than sharing the legacy
8//! backend operation types at the crate root.
9
10pub type Result<T> = crate::VfResult<T>;
11pub type Error = crate::VfError;
12pub use crate::{
13    AttrMask as Attributes, Attrs, Capabilities, CopyOption, DirEntry, ErrorDomain,
14    FilesystemStats, OpenFlags, OpenOp, Permissions, RemoveOptions, StatusCode, TransportKind,
15    VfType as FileType,
16};
17/// Per-pair rename semantics requested by [`Vfsi::vrename`].
18#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
19pub enum RenameOptions {
20    /// Replace a destination according to ordinary filesystem rename semantics.
21    #[default]
22    Replace,
23    /// Fail atomically if the destination already exists.
24    NoReplace,
25    /// Atomically exchange the source and destination names.
26    /// Both paths must exist. This is available only on backends that provide
27    /// native exchange semantics; it is never emulated as multiple operations.
28    Exchange,
29}
30pub use std::io::ErrorKind;
31pub use std::ops::ControlFlow;
32mod io;
33pub use io::SyncMode;
34mod builders;
35pub use builders::OpenOptions;
36mod traits;
37pub use traits::{DirHandle, FileHandle, Vfsi, VfsiExt};
38mod metadata;
39pub use metadata::{AttrsOptions, SetAttrsOp};
40mod mkdir;
41pub use mkdir::MkDirOp;
42mod read;
43pub use read::{ReadOp, ReadOptions, ReadResult};
44mod write;
45pub use write::{WriteOp, WriteOptions};
46mod visit;
47pub use visit::ListDirOptions;
48mod support;
49pub use support::{
50    DEFAULT_DIRECTORY_MAX_ENTRIES, DEFAULT_DIRECTORY_MAX_PATH_BYTES,
51    DEFAULT_READ_ALLV_MAX_TOTAL_BYTES, DEFAULT_READ_MAX_BYTES, DEFAULT_READ_STREAM_CHUNK_BYTES,
52    DEFAULT_READV_MAX_TOTAL_BYTES, DEFAULT_WALK_MAX_DEPTH, DepthLimit, DirectoryListing,
53    ResourceLimits, StreamCompletion, StreamOptions, TraversalCompletion,
54};
55pub use support::{ReadIntoResult, WriteResult};
56mod listdir;
57mod traversal;
58#[doc(hidden)]
59pub use traversal::walk_events;
60pub use traversal::{WalkControl, WalkEvent, WalkEventKind};
61/// What each input to [`Vfsi::vremove`] removes.
62#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
63pub enum RemoveMode {
64    /// Remove a file, symlink, or empty directory; do not traverse directories.
65    #[default]
66    Entry,
67    /// Remove the entry and, for directories, its descendants.
68    Tree,
69    /// Remove descendants while retaining the directory root. Final symlinks
70    /// are not followed; execution uses the backend's anchored removal path.
71    Contents,
72}
73
74/// Unstable dispatch projections for workspace backend implementations.
75#[doc(hidden)]
76pub mod internal {
77    pub use super::read::{ReadRequest, consume_ops, read_batch};
78    pub use super::support::FsReadResult as OwnedReadResult;
79}
80
81/// Common imports for backend-independent application code.
82pub mod prelude {
83    pub use super::{
84        AsTarget, Attributes, AttrsOptions, ControlFlow, CopyOption, DirHandle, FileHandle,
85        ListDirOptions, MkDirOp, OpenFlags, OpenOp, ReadOp, ReadOptions, ReadResult, RemoveMode,
86        RemoveOptions, RenameOptions, ResourceLimits, SetAttrsOp, StreamOptions, SyncMode, Target,
87        Vfsi, VfsiExt, WriteOp, WriteOptions,
88    };
89}
90
91/// Borrowed filesystem operand: a path to resolve or an already-open object.
92/// Used by attribute updates, filesystem statistics, and scalar conveniences.
93/// Handle targets retain object identity across rename/unlink; path symlink
94/// policies do not change that identity. A target never owns or closes a handle.
95#[derive(Debug)]
96pub enum Target<'a, F> {
97    Path(&'a std::path::Path),
98    File(&'a F),
99}
100impl<F> Copy for Target<'_, F> {}
101impl<F> Clone for Target<'_, F> {
102    fn clone(&self) -> Self {
103        *self
104    }
105}
106impl<F> Target<'_, F> {
107    pub fn path(path: &impl AsRef<std::path::Path>) -> Target<'_, F> {
108        Target::Path(path.as_ref())
109    }
110    pub fn file(file: &F) -> Target<'_, F> {
111        Target::File(file)
112    }
113}
114/// Borrow a target for metadata queries, attribute updates or filesystem statistics.
115/// Paths can be passed directly; use [`Target`] for handles or mixed vectors.
116/// Conversion performs no I/O. Clients convert each operand once at preflight
117/// and retain that target for routing, execution, and error context.
118pub trait AsTarget<F> {
119    fn as_target(&self) -> Target<'_, F>;
120}
121impl<F, P: AsRef<std::path::Path>> AsTarget<F> for P {
122    fn as_target(&self) -> Target<'_, F> {
123        Target::Path(self.as_ref())
124    }
125}
126impl<F> AsTarget<F> for Target<'_, F> {
127    fn as_target(&self) -> Target<'_, F> {
128        match self {
129            Self::Path(path) => Target::Path(path),
130            Self::File(file) => Target::File(file),
131        }
132    }
133}
134
135#[cfg(test)]
136mod target_tests {
137    use super::*;
138    #[test]
139    fn borrowed_targets_and_handle_operations_do_not_require_cloning_handles() {
140        struct NonClone;
141        fn copy<T: Copy>(value: T) -> (T, T) {
142            (value, value)
143        }
144        let file = NonClone;
145        let (first, second) = copy(Target::file(&file));
146        for target in [first, second.as_target()] {
147            assert!(matches!(target, Target::File(actual) if std::ptr::eq(actual, &file)));
148        }
149        let (first, second) = copy(SetAttrsOp::file(&file).len(0).follow_symlinks(false));
150        for op in [first, second] {
151            assert_eq!(op.requested_len(), Some(0));
152            assert!(!op.follows_symlinks());
153            assert!(matches!(op.target(), Target::File(actual) if std::ptr::eq(*actual, &file)));
154        }
155        let path = std::path::Path::new("/file");
156        let direct: Target<'_, NonClone> = path.as_target();
157        let explicit: Target<'_, NonClone> = Target::path(&path);
158        for target in [direct, explicit] {
159            assert!(matches!(target, Target::Path(actual) if actual == path));
160        }
161    }
162}