Add structured project documentation
This commit is contained in:
204
docs/architecture.md
Normal file
204
docs/architecture.md
Normal file
@@ -0,0 +1,204 @@
|
||||
# Architecture
|
||||
|
||||
## Overview
|
||||
|
||||
VirtualFS is a virtual filesystem layer for .NET applications. It combines multiple storage backends into one unified virtual tree.
|
||||
|
||||
At a high level:
|
||||
|
||||
- every backend implements `IFileSystem`
|
||||
- a `RootFileSystem` mounts many backends under virtual paths
|
||||
- callers operate on virtual paths like `/assets/logo.png`
|
||||
- the root dispatches operations to the mounted backend that owns the target path
|
||||
|
||||
This gives the library an overlay-style design similar to a mount table.
|
||||
|
||||
## Core Types
|
||||
|
||||
### `Path`
|
||||
|
||||
VirtualFS uses its own `VirtualFS.Path` type instead of platform-native path handling.
|
||||
|
||||
Rules:
|
||||
|
||||
- all paths are rooted
|
||||
- `/` is the root
|
||||
- a trailing `/` means directory
|
||||
- no trailing `/` means file
|
||||
- separator is always `/`
|
||||
- `.` is ignored
|
||||
- `..` is normalized but cannot escape root
|
||||
|
||||
Examples:
|
||||
|
||||
```csharp
|
||||
using VirtualFS;
|
||||
|
||||
Path root = new Path(); // "/"
|
||||
Path dir = "/content/"; // directory
|
||||
Path file = "/content/app.js"; // file
|
||||
Path combined = dir + "app.js"; // "/content/app.js"
|
||||
```
|
||||
|
||||
Important:
|
||||
|
||||
- `/content` and `/content/` are different paths
|
||||
|
||||
### `Entry`, `Directory`, `File`
|
||||
|
||||
The public object model is based on three types:
|
||||
|
||||
- `Entry`
|
||||
- `Directory`
|
||||
- `File`
|
||||
|
||||
`Entry` contains:
|
||||
|
||||
- path
|
||||
- owning filesystem
|
||||
- read-only state
|
||||
- timestamps
|
||||
|
||||
`Directory` adds:
|
||||
|
||||
- enumeration helpers
|
||||
- child lookup
|
||||
- directory creation
|
||||
- file creation
|
||||
- delete operations
|
||||
|
||||
`File` adds:
|
||||
|
||||
- open for read/write
|
||||
- convenience read helpers
|
||||
- convenience write helpers
|
||||
- delete operations
|
||||
|
||||
### Local Path vs Full Path
|
||||
|
||||
For entries returned from mounted filesystems:
|
||||
|
||||
- `Path` is local to the owning backend
|
||||
- `FullPath` is the path as seen from the root
|
||||
|
||||
This distinction matters when the same backend is mounted under a non-root path.
|
||||
|
||||
## Filesystem Model
|
||||
|
||||
### `IFileSystem`
|
||||
|
||||
Represents a backend filesystem.
|
||||
|
||||
Main responsibilities:
|
||||
|
||||
- existence checks
|
||||
- metadata lookup
|
||||
- enumeration
|
||||
- create, delete, move, copy, rename
|
||||
- file stream access
|
||||
- optional mapping back to a physical path or URI-like location
|
||||
|
||||
### `IRootFileSystem`
|
||||
|
||||
Extends `IFileSystem` with mount management:
|
||||
|
||||
- `Mount`
|
||||
- `Umount`
|
||||
- `GetMounted`
|
||||
- `GetMountPath`
|
||||
- `Root`
|
||||
|
||||
### `BaseFileSystem`
|
||||
|
||||
`BaseFileSystem` provides shared behavior for backend implementations.
|
||||
|
||||
Important responsibilities:
|
||||
|
||||
- tracks read-only state
|
||||
- tracks mount priority
|
||||
- tracks open stream count through `VirtualStream`
|
||||
- provides generic copy/move behavior when a backend does not override it
|
||||
|
||||
## Root Composition
|
||||
|
||||
`RootFileSystem` is the composition layer. It is itself read-only, but mounted filesystems may be writable.
|
||||
|
||||
Typical setup:
|
||||
|
||||
```csharp
|
||||
using System.IO;
|
||||
using VirtualFS.Compressed;
|
||||
using VirtualFS.Implementation;
|
||||
using VirtualFS.Memory;
|
||||
using VirtualFS.Physical;
|
||||
|
||||
var root = new RootFileSystem();
|
||||
root.Mount(new PhysicalFileSystem(new DirectoryInfo("/srv/site")), "/");
|
||||
root.Mount(new ZipReadFileSystem(zipStream), "/assets/");
|
||||
root.Mount(new MemoryFileSystem(), "/generated/");
|
||||
```
|
||||
|
||||
After mounting:
|
||||
|
||||
- `/index.html` can resolve from the physical filesystem
|
||||
- `/assets/...` can resolve from the ZIP
|
||||
- `/generated/...` can resolve from memory
|
||||
|
||||
## Mount Priority
|
||||
|
||||
When multiple filesystems are mounted under the same virtual directory, resolution order is controlled by `FileSystemMountPriority`.
|
||||
|
||||
Values:
|
||||
|
||||
- `High`
|
||||
- `Normal`
|
||||
- `Low`
|
||||
|
||||
Current behavior:
|
||||
|
||||
- `High` mounts are checked first
|
||||
- `Low` mounts are checked last
|
||||
- `Normal` mounts are placed between them
|
||||
|
||||
This supports overlay patterns such as:
|
||||
|
||||
- immutable base content from a ZIP
|
||||
- writable overrides from disk
|
||||
- generated files from memory
|
||||
|
||||
## Local vs Rooted Operations
|
||||
|
||||
Many `Directory` and `File` instance methods accept `forceLocal`.
|
||||
|
||||
Behavior:
|
||||
|
||||
- `forceLocal: false` uses the owning root filesystem if one exists
|
||||
- `forceLocal: true` keeps the operation inside the mounted backend
|
||||
|
||||
This matters when several filesystems overlap.
|
||||
|
||||
## Copy and Move Behavior
|
||||
|
||||
The library can copy and move across filesystem boundaries.
|
||||
|
||||
Behavior to understand:
|
||||
|
||||
- same-backend operations may use optimized backend-specific implementations
|
||||
- cross-backend operations may fall back to stream-based copy logic
|
||||
- moves across backends may become copy-then-delete
|
||||
- deep remote copies can be slower than native host operations
|
||||
|
||||
## Streams and Busy State
|
||||
|
||||
Open streams are wrapped in `VirtualStream`.
|
||||
|
||||
Purpose:
|
||||
|
||||
- increment and decrement backend open counts
|
||||
- keep backend-specific transport state alive while a stream is open
|
||||
- support `IsBusy`
|
||||
|
||||
Practical implication:
|
||||
|
||||
- a busy filesystem cannot be unmounted
|
||||
- callers should dispose streams promptly
|
||||
Reference in New Issue
Block a user