stout-state
Lightweight local state management for package installations — tracking installed packages, pins, links, and configuration with a simple file-based API.
Keywords: state-management, package-manager, configuration, tracking, json, rust, homebrew, local-state
Why stout-state?
Every package manager needs to remember what's installed, what version, and where. stout-state provides a clean, typed API for managing this local state without a database dependency. It stores everything as readable JSON files, making it easy to inspect, version control, or migrate.
This crate powers the stout list, stout config, and installation tracking in stout, but it's designed as a general-purpose state library for any Rust project that needs to persist structured local data.
Features
- Installation Tracking — Track installed formulas, casks, versions, and paths
- Pin Management — Pin packages to prevent accidental upgrades
- Link State — Track which packages are linked into the prefix
- Configuration Files — Typed TOML configuration with defaults and validation
- Lockfile Support — Generate and restore reproducible package environments
- Human-Readable Storage — Plain JSON files, easy to inspect and version control
- Atomic Writes — All updates are atomic (write to temp, then rename)
- Cross-Platform — Works on macOS, Linux, and Windows
Installation
Or in your Cargo.toml:
[]
= "0.2"
Quick Start
use State;
// Load or initialize state in the default location
let state = load_default?;
// Track an installation
state.track_formula?;
// Query installed packages
let installed = state.installed_formulas;
for pkg in installed
// Check if a package is pinned
if state.is_pinned?
API Overview
Loading State
use State;
// Default location (~/.stout/state.json)
let state = load_default?;
// Custom state directory
let state = load?;
// In-memory state (useful for testing)
let state = in_memory;
Tracking Installations
// Track a formula installation
state.track_formula?;
// Track a cask installation
state.track_cask?;
// Update tracking after an upgrade
state.update_version?;
// Untrack (uninstall)
state.untrack_formula?;
state.untrack_cask?;
Querying State
// List all installed formulas
let formulas = state.installed_formulas;
for f in formulas
// List installed casks
let casks = state.installed_casks;
// Check if a formula is installed
let is_installed = state.has_formula?;
// Get version of installed package
let version = state.formula_version?; // Option<String>
// Get installation path
let path = state.formula_path?; // Option<PathBuf>
// Count installed packages
println!;
Pin Management
// Pin a package (prevent upgrades)
state.pin?;
// Unpin
state.unpin?;
// Check pin status
let is_pinned = state.is_pinned?;
// List all pinned packages
let pinned = state.pinned_formulas;
for p in pinned
Link State
// Mark a formula as linked
state.mark_linked?;
// Mark as unlinked
state.mark_unlinked?;
// Check link status
let is_linked = state.is_linked?;
// List linked packages
let linked = state.linked_formulas;
Configuration Management
use Config;
// Load configuration
let config = state.config?;
println!;
println!;
// Update configuration
let mut config = state.config?;
config.install.parallel_downloads = 8;
config.cache.max_size = "4GB".to_string;
state.save_config?;
// Access typed config values
let auto_update: bool = state.config_value?;
let cellar: PathBuf = state.config_value?;
Lockfiles
use Lockfile;
// Generate a lockfile from current state
let lockfile = from_state?;
lockfile.save?;
// Restore from lockfile
let lockfile = load?;
for entry in lockfile.entries
// Check if current state matches lockfile
let is_satisfied = lockfile.is_satisfied_by?;
if !is_satisfied
State Inspection
// Get disk usage summary
let usage = state.disk_usage?;
println!;
println!;
println!;
// Export state as JSON
let json = state.to_json?;
println!;
// Import state from JSON
state.from_json?;
Storage Format
State is stored as human-readable JSON files:
~/.stout/
├── state.json — Main state file
├── config.toml — User configuration
├── receipts/ — Per-package installation receipts
│ ├── jq.json
│ └── node.json
└── lockfiles/ — Generated lockfiles
└── Brewfile.lock
Example state.json:
Integration with the Stout Ecosystem
stout-state is the bookkeeping layer of stout:
- stout-install updates state after every install/uninstall/upgrade
- stout-resolve reads state to skip already-installed dependencies
- stout-cask tracks cask installations separately from formulas
- stout-bundle generates lockfiles from state and restores from them
You can use stout-state standalone for any project that needs simple, persistent, typed local state without pulling in a database.
License
MIT License — see the repository root for details.