gsh/libgsh/src/registry.rs
Tyler J King 919d8accde refactor: extract libgsh from monolith
Phase 2 of the WSL2 jumphost build.

Workspace: gsh/ (binary) + libgsh/ (library).

libgsh modules:
  ac.rs       — AC validation (R-22 single-use, R-23 corpus match, expiry)
  cr.rs       — CR construction + broker posting + inline AC request
  corpus.rs   — Corpus directory gate (killswitch)
  config.rs   — GshConfig from environment
  registry.rs — Filesystem-based consumed AC registry

gsh/src/main.rs: CLI only (~170 lines).
  Clap args, mode detection, calls libgsh, formats output.

11 unit tests in libgsh:
  ac: valid AC, expired, corpus mismatch, replay, missing context_id
  cr: broker URL formatting
  corpus: ungoverned skip, missing dir, command name extraction
  registry: consume and check
  config: default corpus_cid

Zero behavior change. Same JSON output, same exit codes,
same flags, same env vars, same broker interaction.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 09:31:50 -04:00

48 lines
1.2 KiB
Rust

//! Single-use AC context registry (R-22).
//! Filesystem-based: ~/.gsh/consumed/{context_id}
use std::path::PathBuf;
/// Tracks consumed AC context IDs on the filesystem.
pub struct ConsumedRegistry {
dir: PathBuf,
}
impl ConsumedRegistry {
pub fn new(dir: PathBuf) -> Self {
Self { dir }
}
/// Default registry location: ~/.gsh/consumed/
pub fn default_location() -> Self {
let dir = dirs::home_dir()
.map(|h| h.join(".gsh").join("consumed"))
.unwrap_or_else(|| PathBuf::from("/tmp/.gsh/consumed"));
Self { dir }
}
pub fn is_consumed(&self, context_id: &str) -> bool {
self.dir.join(context_id).exists()
}
pub fn mark_consumed(&mut self, context_id: &str) {
let _ = std::fs::create_dir_all(&self.dir);
let _ = std::fs::write(self.dir.join(context_id), "");
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_consume_and_check() {
let dir = tempfile::tempdir().unwrap();
let mut reg = ConsumedRegistry::new(dir.path().to_path_buf());
assert!(!reg.is_consumed("ac-123"));
reg.mark_consumed("ac-123");
assert!(reg.is_consumed("ac-123"));
assert!(!reg.is_consumed("ac-456"));
}
}