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