heroforge_core/fs/
mod.rs

1//! # Filesystem Interface
2//!
3//! This module provides a clean, high-level filesystem abstraction for Heroforge repositories.
4//! It hides the complexity of SQLite-backed storage and provides a familiar filesystem-like API.
5//!
6//! ## Architecture Overview
7//!
8//! ```text
9//! ┌─────────────────────────────────────────────────────────────────┐
10//! │                      FsInterface (sync API)                     │
11//! │  - RwLock<StagingState>                                         │
12//! │  - Author name (set at initialization)                          │
13//! │  - All read/write operations acquire appropriate locks          │
14//! └─────────────────────────────────────────────────────────────────┘
15//!                               │
16//!           ┌───────────────────┴───────────────────┐
17//!           │                                       │
18//!           ▼                                       ▼
19//! ┌─────────────────────┐                 ┌─────────────────────────┐
20//! │   Staging Directory │                 │    Commit Thread        │
21//! │                     │                 │    (background)         │
22//! │  - All writes go    │                 │                         │
23//! │    here first       │                 │  - Runs every 1 minute  │
24//! │  - Files < 2MB      │                 │  - Acquires write lock  │
25//! │  - Frequent updates │                 │  - Blocks all I/O       │
26//! │    allowed          │                 │  - Flushes to .forge DB │
27//! │                     │                 │  - Clears staging dir   │
28//! └─────────────────────┘                 └─────────────────────────┘
29//!           │                                       │
30//!           └───────────────────┬───────────────────┘
31//!                               │
32//!                               ▼
33//!                     ┌─────────────────────┐
34//!                     │   .forge Database   │
35//!                     │      (SQLite)       │
36//!                     └─────────────────────┘
37//! ```
38//!
39//! ## Quick Start with FsInterface (Recommended)
40//!
41//! The `FsInterface` provides staging-based writes with automatic background commits:
42//!
43//! ```no_run
44//! use heroforge_core::Repository;
45//! use std::sync::Arc;
46//!
47//! fn main() -> heroforge_core::Result<()> {
48//!     let repo = Arc::new(Repository::open_rw("project.forge")?);
49//!     let fs = heroforge_core::fs::FsInterface::new(repo, "developer@example.com")?;
50//!
51//!     // Writes go to staging (fast)
52//!     fs.write_file("config.json", b"{}")?;
53//!
54//!     // Reads check staging first, then database
55//!     let content = fs.read_file("config.json")?;
56//!
57//!     // Partial updates use read-modify-write pattern
58//!     fs.write_at("data.bin", 100, b"updated")?;
59//!
60//!     // Force immediate commit (normally auto-commits every 1 minute)
61//!     fs.commit()?;
62//!
63//!     Ok(())
64//! }
65//! ```
66//!
67//! ## Legacy FileSystem API
68//!
69//! The original `FileSystem` API is still available for direct database operations:
70//!
71//! ```no_run
72//! use heroforge_core::Repository;
73//! use heroforge_core::fs::FileSystem;
74//! use std::sync::Arc;
75//!
76//! fn main() -> heroforge_core::Result<()> {
77//!     let repo = Arc::new(Repository::open_rw("project.forge")?);
78//!     let fs = FileSystem::new(repo);
79//!
80//!     // Direct database operations (no staging)
81//!     fs.write_file("config.json", b"{}", "developer", "Add config")?;
82//!
83//!     Ok(())
84//! }
85//! ```
86//!
87//! ## Staging Directory
88//!
89//! All write operations go to a **staging directory** first, not directly to SQLite:
90//!
91//! 1. **Fast writes**: Writing to filesystem is faster than SQLite transactions
92//! 2. **Frequent updates**: Files can be modified many times before commit
93//! 3. **Atomic commits**: All staged changes are committed together
94//! 4. **Crash recovery**: Uncommitted work is recoverable from staging
95//!
96//! **Current Limitations:**
97//! - Files larger than **2 MB** are not supported
98//! - Staging directory is local to the repository
99//!
100//! ## Read Path (Layered Lookup)
101//!
102//! When reading a file, the interface checks locations in this order:
103//!
104//! 1. **Staging Directory** → If file exists here, return it (most recent)
105//! 2. **`.forge` Database** → Query SQLite for committed version
106//! 3. **Return Error** → File does not exist
107//!
108//! ## Auto-Commit Behavior
109//!
110//! - Commits happen automatically every **1 minute**
111//! - During commit, an **exclusive write lock** is held
112//! - All operations are blocked during commit (typically milliseconds)
113//! - Forced commits happen on: branch change, tag creation, shutdown
114//!
115//! ## Thread Safety
116//!
117//! Uses `RwLock<StagingState>` for concurrent access:
118//!
119//! | Operation | Lock Type | Blocks |
120//! |-----------|-----------|--------|
121//! | `exists()` | Read | Nothing |
122//! | `read_file()` | Read | Nothing |
123//! | `write_file()` | Write | Other writes |
124//! | `delete_file()` | Write | Other writes |
125//! | **Commit** | **Exclusive** | **Everything** |
126//!
127//! ## Error Handling
128//!
129//! All operations return `FsResult<T>` which is `Result<T, FsError>`:
130//!
131//! ```no_run
132//! # use heroforge_core::Repository;
133//! # use std::sync::Arc;
134//! let repo = Arc::new(Repository::open_rw("repo.forge")?);
135//! let fs = heroforge_core::fs::FsInterface::new(repo, "user")?;
136//!
137//! match fs.read_file("missing.txt") {
138//!     Ok(content) => println!("Read {} bytes", content.len()),
139//!     Err(err) => eprintln!("Error: {}", err),
140//! }
141//! # Ok::<(), heroforge_core::FossilError>(())
142//! ```
143
144pub mod commit_thread;
145pub mod errors;
146pub mod find;
147pub mod fs_interface;
148pub mod interface;
149pub mod operations;
150pub mod ops;
151pub mod staging;
152pub mod transaction;
153
154pub use errors::{FsError, FsResult};
155pub use interface::{FileHandle, FileSystem, FileSystemStatus};
156pub use operations::{
157    DirectoryEntry, FileKind, FileMetadata, FilePermissions, FindResults, FsOperation,
158    OperationSummary,
159};
160pub use transaction::{SavePoint, Transaction, TransactionMode, TransactionState};
161
162// New staging-based interface
163pub use commit_thread::{CommitConfig, CommitWorker, DEFAULT_COMMIT_INTERVAL};
164pub use fs_interface::{FsInterface, FsInterfaceStatus};
165pub use staging::{MAX_FILE_SIZE, StagedFile, Staging, StagingState};
166
167// Find and high-level operations
168pub use find::{
169    FileEntry, FileType, Find, FindResult, Permissions, count, du, exists, find, is_dir,
170    list_symlinks, stat,
171};
172pub use ops::{Modify, Op, Preview};
173
174// Upload/Download (OS filesystem <-> .forge database)
175pub use ops::{
176    download, download_dir, download_from_branch, download_matching, upload, upload_dir,
177};
178
179/// Filesystem interface version
180pub const VERSION: &str = "1.0.0";
181
182#[cfg(test)]
183mod tests {
184    use super::*;
185
186    #[test]
187    fn test_version() {
188        assert_eq!(VERSION, "1.0.0");
189    }
190}