5.5 KiB
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:
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:
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:
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:
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/):
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:
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:
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:
use crate::util::Apply;
let result = some_value
.apply(|v| transform(v))
.apply(|v| another_transform(v));
Container<T>
Async-safe optional container:
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:
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:
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:
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:
use crate::util::new_guid;
let guid = new_guid(); // Returns InternedString