Skip to main content

dodot_lib/fs/
mod.rs

1mod os;
2
3pub use os::OsFs;
4
5use std::path::{Path, PathBuf};
6
7use crate::Result;
8
9/// Metadata about a filesystem entry.
10#[derive(Debug, Clone)]
11pub struct FsMetadata {
12    pub is_file: bool,
13    pub is_dir: bool,
14    pub is_symlink: bool,
15    pub len: u64,
16    /// Unix permission mode (e.g. `0o755`).
17    pub mode: u32,
18}
19
20/// A single directory entry returned by [`Fs::read_dir`].
21#[derive(Debug, Clone)]
22pub struct DirEntry {
23    pub path: PathBuf,
24    pub name: String,
25    pub is_dir: bool,
26    pub is_file: bool,
27    pub is_symlink: bool,
28}
29
30/// Filesystem abstraction.
31///
32/// All dodot code accesses the filesystem through this trait so that:
33/// - Tests can use isolated temp directories with a real implementation
34/// - Every `io::Error` is wrapped with the path that caused it
35///
36/// Use `&dyn Fs` (trait objects) throughout the codebase. The operations
37/// are I/O-bound so dynamic dispatch costs nothing meaningful, and generics
38/// would infect every type signature.
39pub trait Fs: Send + Sync {
40    /// Returns metadata for the path, following symlinks.
41    fn stat(&self, path: &Path) -> Result<FsMetadata>;
42
43    /// Returns metadata for the path without following symlinks.
44    fn lstat(&self, path: &Path) -> Result<FsMetadata>;
45
46    /// Opens the file for reading in a streaming fashion.
47    ///
48    /// Errors that occur while opening the file are returned through this
49    /// method's [`Result`] and include path context. Once opened, the
50    /// returned reader is a raw [`std::io::Read`], so any later `read()`
51    /// errors are reported as plain [`std::io::Error`] values and are not
52    /// automatically wrapped with the path.
53    fn open_read(&self, path: &Path) -> Result<Box<dyn std::io::Read + Send + Sync>>;
54
55    /// Reads the entire file into bytes.
56    fn read_file(&self, path: &Path) -> Result<Vec<u8>>;
57
58    /// Reads the entire file as a UTF-8 string.
59    fn read_to_string(&self, path: &Path) -> Result<String>;
60
61    /// Writes `contents` to `path`, creating or truncating the file.
62    fn write_file(&self, path: &Path, contents: &[u8]) -> Result<()>;
63
64    /// Creates `path` and all parent directories.
65    fn mkdir_all(&self, path: &Path) -> Result<()>;
66
67    /// Creates a symbolic link at `link` pointing to `original`.
68    fn symlink(&self, original: &Path, link: &Path) -> Result<()>;
69
70    /// Reads the target of a symbolic link.
71    fn readlink(&self, path: &Path) -> Result<PathBuf>;
72
73    /// Removes a file or symlink (not a directory).
74    fn remove_file(&self, path: &Path) -> Result<()>;
75
76    /// Removes a directory and all of its contents.
77    fn remove_dir_all(&self, path: &Path) -> Result<()>;
78
79    /// Returns `true` if `path` exists (follows symlinks).
80    fn exists(&self, path: &Path) -> bool;
81
82    /// Returns `true` if `path` is a symlink (does not follow).
83    fn is_symlink(&self, path: &Path) -> bool;
84
85    /// Returns `true` if `path` is a directory (follows symlinks).
86    fn is_dir(&self, path: &Path) -> bool;
87
88    /// Lists entries in a directory, sorted by name.
89    fn read_dir(&self, path: &Path) -> Result<Vec<DirEntry>>;
90
91    /// Renames (moves) `from` to `to`.
92    fn rename(&self, from: &Path, to: &Path) -> Result<()>;
93
94    /// Copies a file from `from` to `to`.
95    fn copy_file(&self, from: &Path, to: &Path) -> Result<()>;
96
97    /// Sets file permissions (Unix mode).
98    fn set_permissions(&self, path: &Path, mode: u32) -> Result<()>;
99}