vortex-fs 0.1.0

Virtual file system with fault injection for Vortex simulation
Documentation
//! VortexFs trait — the filesystem abstraction boundary.

/// Error type for virtual filesystem operations.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum VortexFsError {
    /// File or directory not found.
    NotFound(String),
    /// Permission denied (EACCES).
    PermissionDenied(String),
    /// Disk full (ENOSPC).
    DiskFull(String),
    /// I/O error (EIO).
    IoError(String),
    /// File already exists.
    AlreadyExists(String),
    /// Not a directory.
    NotADirectory(String),
    /// Is a directory (tried to read as file).
    IsADirectory(String),
    /// Directory not empty (tried to remove).
    NotEmpty(String),
    /// Torn write — partial write succeeded then failed.
    TornWrite {
        path: String,
        bytes_written: u64,
        intended: u64,
    },
    /// Data corruption injected.
    Corrupted(String),
}

impl std::fmt::Display for VortexFsError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            VortexFsError::NotFound(p) => write!(f, "not found: {p}"),
            VortexFsError::PermissionDenied(p) => write!(f, "permission denied: {p}"),
            VortexFsError::DiskFull(p) => write!(f, "disk full: {p}"),
            VortexFsError::IoError(p) => write!(f, "I/O error: {p}"),
            VortexFsError::AlreadyExists(p) => write!(f, "already exists: {p}"),
            VortexFsError::NotADirectory(p) => write!(f, "not a directory: {p}"),
            VortexFsError::IsADirectory(p) => write!(f, "is a directory: {p}"),
            VortexFsError::NotEmpty(p) => write!(f, "not empty: {p}"),
            VortexFsError::TornWrite {
                path,
                bytes_written,
                intended,
            } => {
                write!(
                    f,
                    "torn write on {path}: wrote {bytes_written}/{intended} bytes"
                )
            }
            VortexFsError::Corrupted(p) => write!(f, "data corrupted: {p}"),
        }
    }
}

impl std::error::Error for VortexFsError {}

/// Result type alias for filesystem operations.
pub type VortexFsResult<T> = std::result::Result<T, VortexFsError>;

/// File type (regular file or directory).
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FileType {
    File,
    Directory,
}

/// Metadata about a file or directory.
#[derive(Debug, Clone)]
pub struct FileMetadata {
    /// File type.
    pub file_type: FileType,
    /// Size in bytes (0 for directories).
    pub size: u64,
}

/// The filesystem abstraction boundary.
///
/// In production: implement with [`RealFs`](crate::RealFs) (pass-through to `std::fs`).
/// In simulation: implement with [`SimFs`](crate::SimFs) (in-memory + fault injection).
pub trait VortexFs {
    /// Read the entire contents of a file.
    fn read_file(&self, path: &str) -> VortexFsResult<Vec<u8>>;

    /// Write data to a file (creates or overwrites).
    fn write_file(&mut self, path: &str, data: &[u8]) -> VortexFsResult<()>;

    /// Append data to a file (creates if not exists).
    fn append_file(&mut self, path: &str, data: &[u8]) -> VortexFsResult<()>;

    /// Remove a file.
    fn remove_file(&mut self, path: &str) -> VortexFsResult<()>;

    /// Rename a file or directory.
    fn rename(&mut self, from: &str, to: &str) -> VortexFsResult<()>;

    /// Create a directory (and all parent directories).
    fn create_dir_all(&mut self, path: &str) -> VortexFsResult<()>;

    /// Remove an empty directory.
    fn remove_dir(&mut self, path: &str) -> VortexFsResult<()>;

    /// List the entries in a directory.
    fn read_dir(&self, path: &str) -> VortexFsResult<Vec<String>>;

    /// Get metadata about a file or directory.
    fn metadata(&self, path: &str) -> VortexFsResult<FileMetadata>;

    /// Check if a path exists.
    fn exists(&self, path: &str) -> bool;

    /// Flush / fsync a file (no-op for in-memory, real sync for production).
    fn fsync(&mut self, path: &str) -> VortexFsResult<()>;
}