mirror of
https://github.com/Start9Labs/start-os.git
synced 2026-03-26 02:11:53 +00:00
250 lines
5.5 KiB
Markdown
250 lines
5.5 KiB
Markdown
# Utilities & Patterns
|
|
|
|
This document covers common utilities and patterns used throughout the StartOS codebase.
|
|
|
|
## Util Module (`core/src/util/`)
|
|
|
|
The `util` module contains reusable utilities. Key submodules:
|
|
|
|
| Module | Purpose |
|
|
|--------|---------|
|
|
| `actor/` | Actor pattern implementation for concurrent state management |
|
|
| `collections/` | Custom collection types |
|
|
| `crypto.rs` | Cryptographic utilities (encryption, hashing) |
|
|
| `future.rs` | Future/async utilities |
|
|
| `io.rs` | File I/O helpers (create_file, canonicalize, etc.) |
|
|
| `iter.rs` | Iterator extensions |
|
|
| `net.rs` | Network utilities |
|
|
| `rpc.rs` | RPC helpers |
|
|
| `rpc_client.rs` | RPC client utilities |
|
|
| `serde.rs` | Serialization helpers (Base64, display/fromstr, etc.) |
|
|
| `sync.rs` | Synchronization primitives (SyncMutex, etc.) |
|
|
|
|
## Command Invocation (`Invoke` trait)
|
|
|
|
The `Invoke` trait provides a clean way to run external commands with error handling:
|
|
|
|
```rust
|
|
use crate::util::Invoke;
|
|
|
|
// Simple invocation
|
|
tokio::process::Command::new("ls")
|
|
.arg("-la")
|
|
.invoke(ErrorKind::Filesystem)
|
|
.await?;
|
|
|
|
// With timeout
|
|
tokio::process::Command::new("slow-command")
|
|
.timeout(Some(Duration::from_secs(30)))
|
|
.invoke(ErrorKind::Timeout)
|
|
.await?;
|
|
|
|
// With input
|
|
let mut input = Cursor::new(b"input data");
|
|
tokio::process::Command::new("cat")
|
|
.input(Some(&mut input))
|
|
.invoke(ErrorKind::Filesystem)
|
|
.await?;
|
|
|
|
// Piped commands
|
|
tokio::process::Command::new("cat")
|
|
.arg("file.txt")
|
|
.pipe(&mut tokio::process::Command::new("grep").arg("pattern"))
|
|
.invoke(ErrorKind::Filesystem)
|
|
.await?;
|
|
```
|
|
|
|
## Guard Pattern
|
|
|
|
Guards ensure cleanup happens when they go out of scope.
|
|
|
|
### `GeneralGuard` / `GeneralBoxedGuard`
|
|
|
|
For arbitrary cleanup actions:
|
|
|
|
```rust
|
|
use crate::util::GeneralGuard;
|
|
|
|
let guard = GeneralGuard::new(|| {
|
|
println!("Cleanup runs on drop");
|
|
});
|
|
|
|
// Do work...
|
|
|
|
// Explicit drop with action
|
|
guard.drop();
|
|
|
|
// Or skip the action
|
|
// guard.drop_without_action();
|
|
```
|
|
|
|
### `FileLock`
|
|
|
|
File-based locking with automatic unlock:
|
|
|
|
```rust
|
|
use crate::util::FileLock;
|
|
|
|
let lock = FileLock::new("/path/to/lockfile", true).await?; // blocking=true
|
|
// Lock held until dropped or explicitly unlocked
|
|
lock.unlock().await?;
|
|
```
|
|
|
|
## Mount Guard Pattern (`core/src/disk/mount/guard.rs`)
|
|
|
|
RAII guards for filesystem mounts. Ensures filesystems are unmounted when guards are dropped.
|
|
|
|
### `MountGuard`
|
|
|
|
Basic mount guard:
|
|
|
|
```rust
|
|
use crate::disk::mount::guard::MountGuard;
|
|
use crate::disk::mount::filesystem::{MountType, ReadOnly};
|
|
|
|
let guard = MountGuard::mount(&filesystem, "/mnt/target", ReadOnly).await?;
|
|
|
|
// Use the mounted filesystem at guard.path()
|
|
do_something(guard.path()).await?;
|
|
|
|
// Explicit unmount (or auto-unmounts on drop)
|
|
guard.unmount(false).await?; // false = don't delete mountpoint
|
|
```
|
|
|
|
### `TmpMountGuard`
|
|
|
|
Reference-counted temporary mount (mounts to `/media/startos/tmp/`):
|
|
|
|
```rust
|
|
use crate::disk::mount::guard::TmpMountGuard;
|
|
use crate::disk::mount::filesystem::ReadOnly;
|
|
|
|
// Multiple clones share the same mount
|
|
let guard1 = TmpMountGuard::mount(&filesystem, ReadOnly).await?;
|
|
let guard2 = guard1.clone();
|
|
|
|
// Mount stays alive while any guard exists
|
|
// Auto-unmounts when last guard is dropped
|
|
```
|
|
|
|
### `GenericMountGuard` trait
|
|
|
|
All mount guards implement this trait:
|
|
|
|
```rust
|
|
pub trait GenericMountGuard: std::fmt::Debug + Send + Sync + 'static {
|
|
fn path(&self) -> &Path;
|
|
fn unmount(self) -> impl Future<Output = Result<(), Error>> + Send;
|
|
}
|
|
```
|
|
|
|
### `SubPath`
|
|
|
|
Wraps a mount guard to point to a subdirectory:
|
|
|
|
```rust
|
|
use crate::disk::mount::guard::SubPath;
|
|
|
|
let mount = TmpMountGuard::mount(&filesystem, ReadOnly).await?;
|
|
let subdir = SubPath::new(mount, "data/subdir");
|
|
|
|
// subdir.path() returns the full path including subdirectory
|
|
```
|
|
|
|
## FileSystem Implementations (`core/src/disk/mount/filesystem/`)
|
|
|
|
Various filesystem types that can be mounted:
|
|
|
|
| Type | Description |
|
|
|------|-------------|
|
|
| `bind.rs` | Bind mounts |
|
|
| `block_dev.rs` | Block device mounts |
|
|
| `cifs.rs` | CIFS/SMB network shares |
|
|
| `ecryptfs.rs` | Encrypted filesystem |
|
|
| `efivarfs.rs` | EFI variables |
|
|
| `httpdirfs.rs` | HTTP directory as filesystem |
|
|
| `idmapped.rs` | ID-mapped mounts |
|
|
| `label.rs` | Mount by label |
|
|
| `loop_dev.rs` | Loop device mounts |
|
|
| `overlayfs.rs` | Overlay filesystem |
|
|
|
|
## Other Useful Utilities
|
|
|
|
### `Apply` / `ApplyRef` traits
|
|
|
|
Fluent method chaining:
|
|
|
|
```rust
|
|
use crate::util::Apply;
|
|
|
|
let result = some_value
|
|
.apply(|v| transform(v))
|
|
.apply(|v| another_transform(v));
|
|
```
|
|
|
|
### `Container<T>`
|
|
|
|
Async-safe optional container:
|
|
|
|
```rust
|
|
use crate::util::Container;
|
|
|
|
let container = Container::new(None);
|
|
container.set(value).await;
|
|
let taken = container.take().await;
|
|
```
|
|
|
|
### `HashWriter<H, W>`
|
|
|
|
Write data while computing hash:
|
|
|
|
```rust
|
|
use crate::util::HashWriter;
|
|
use sha2::Sha256;
|
|
|
|
let writer = HashWriter::new(Sha256::new(), file);
|
|
// Write data...
|
|
let (hasher, file) = writer.finish();
|
|
let hash = hasher.finalize();
|
|
```
|
|
|
|
### `Never` type
|
|
|
|
Uninhabited type for impossible cases:
|
|
|
|
```rust
|
|
use crate::util::Never;
|
|
|
|
fn impossible() -> Never {
|
|
// This function can never return
|
|
}
|
|
|
|
let never: Never = impossible();
|
|
never.absurd::<String>() // Can convert to any type
|
|
```
|
|
|
|
### `MaybeOwned<'a, T>`
|
|
|
|
Either borrowed or owned data:
|
|
|
|
```rust
|
|
use crate::util::MaybeOwned;
|
|
|
|
fn accept_either(data: MaybeOwned<'_, String>) {
|
|
// Use &*data to access the value
|
|
}
|
|
|
|
accept_either(MaybeOwned::from(&existing_string));
|
|
accept_either(MaybeOwned::from(owned_string));
|
|
```
|
|
|
|
### `new_guid()`
|
|
|
|
Generate a random GUID:
|
|
|
|
```rust
|
|
use crate::util::new_guid;
|
|
|
|
let guid = new_guid(); // Returns InternedString
|
|
```
|