git-ents.gitmain
⌘K
foforge
commit b4b8b84
refactor: route the meta-ref set documents through `Row`

Signer, Check, and RunOutcome implement git_store::Row, so their load and store run through load_rows/store_rows instead of a hand-written map and collect at each site. Each module’s single-variant Error wrapper is gone; the functions return git_store::Error, which every caller already stringifies.

refactor: implement git_store::Row for Signer, Check, and RunOutcome refactor: load and store the signer and check sets via load_rows/store_rows refactor: return git_store::Error from signers/checks/config/issues directly Assisted-by: Claude:claude-opus-4-8

Joseph D. Carpinelli · 1 month ago

Reviews

No reviews of this commit yet — record a verdict below.

Start a review

verdict

crates/git-ents/src/checks.rs @@ -13,7 +13,7 @@ use std::path::Path; use facet::Facet; -use git_store::MapDoc as _; +use git_store::{MapDoc as _, Row as _}; /// The ref whose tree holds the configured check set. pub const CHECKS_REF: &str = "refs/meta/checks"; @@ -44,12 +44,17 @@ pub command: String, } -/// A failure reading or writing the check set. -#[derive(Debug, thiserror::Error)] -pub enum Error { - /// The check set could not be read from or written to its ref. - #[error(transparent)] - Store(#[from] git_store::Error), +impl git_store::Row for Check { + fn from_pair(name: String, command: String) -> Self { + Self { + name, + command: command.trim_end().to_owned(), + } + } + + fn into_pair(self) -> (String, String) { + (self.name, self.command) + } } /// Load the configured checks recorded at [`CHECKS_REF`] in `repo`. @@ -57,26 +62,18 @@ /// An absent ref yields an empty set, as on a server whose check set has not /// been pushed yet. A present but unreadable ref is an error so callers can /// distinguish corruption from "no checks configured". -pub fn load(repo: &Path) -> Result<Vec<Check>, Error> { - Ok(git_store::Store::open(repo)? - .load_entries::<Checks>(CHECKS_REF)? - .into_iter() - .map(|(name, command)| Check { - name, - command: command.trim_end().to_owned(), - }) - .collect()) +pub fn load(repo: &Path) -> Result<Vec<Check>, git_store::Error> { + git_store::Store::open(repo)?.load_rows::<Checks, Check>(CHECKS_REF) } /// Write `checks` to [`CHECKS_REF`], replacing any existing set, as a new /// commit. -pub fn store(repo: &Path, checks: &[Check]) -> Result<(), Error> { - let entries = checks - .iter() - .map(|check| (check.name.clone(), check.command.clone())) - .collect(); - git_store::Store::open(repo)?.store_entries::<Checks>(CHECKS_REF, entries, "Update checks")?; - Ok(()) +pub fn store(repo: &Path, checks: &[Check]) -> Result<(), git_store::Error> { + git_store::Store::open(repo)?.store_rows::<Checks, _>( + CHECKS_REF, + checks.iter().cloned(), + "Update checks", + ) } /// The namespace under which a commit's check runs are recorded: one ref, @@ -113,6 +110,16 @@ pub outcome: String, } +impl git_store::Row for RunOutcome { + fn from_pair(name: String, outcome: String) -> Self { + Self { name, outcome } + } + + fn into_pair(self) -> (String, String) { + (self.name, self.outcome) + } +} + /// One recorded execution of the check set against a commit. #[derive(Debug, Clone, PartialEq, Eq, Facet)] pub struct Run { @@ -136,7 +143,7 @@ /// Record a run of `outcomes` for `commit` as a new commit on /// `refs/meta/runs/<commit>`, parented on the prior run so the ref's commit /// chain is the run history. The commit's date is the run time. -pub fn record(repo: &Path, commit: &str, outcomes: &[RunOutcome]) -> Result<(), Error> { +pub fn record(repo: &Path, commit: &str, outcomes: &[RunOutcome]) -> Result<(), git_store::Error> { git_store::Store::open(repo)?.store( &format!("{RUNS_NS}/{commit}"), &run_doc(outcomes), @@ -152,7 +159,11 @@ /// /// When no run has been recorded yet the update starts one, so a worker that /// advances a run is self-healing even if the `queued` record never landed. -pub fn update_run(repo: &Path, commit: &str, outcomes: &[RunOutcome]) -> Result<(), Error> { +pub fn update_run( + repo: &Path, + commit: &str, + outcomes: &[RunOutcome], +) -> Result<(), git_store::Error> { git_store::Store::open(repo)?.amend( &format!("{RUNS_NS}/{commit}"), &run_doc(outcomes), @@ -164,7 +175,7 @@ /// List the recorded runs per commit, newest commit first. Each commit's runs /// are the ref's commit chain, newest first, with the run time taken from each /// commit's date. -pub fn runs(repo: &Path) -> Result<Vec<CommitRuns>, Error> { +pub fn runs(repo: &Path) -> Result<Vec<CommitRuns>, git_store::Error> { let store = git_store::Store::open(repo)?; let prefix = format!("{RUNS_NS}/"); let mut commits = Vec::new(); @@ -180,7 +191,7 @@ results: doc .into_entries() .into_iter() - .map(|(name, outcome)| RunOutcome { name, outcome }) + .map(|(name, outcome)| RunOutcome::from_pair(name, outcome)) .collect(), }) .collect(); @@ -197,7 +208,8 @@ RunDoc::from_entries( outcomes .iter() - .map(|outcome| (outcome.name.clone(), outcome.outcome.clone())) + .cloned() + .map(git_store::Row::into_pair) .collect(), ) }
crates/git-ents/src/config.rs @@ -27,20 +27,12 @@ pub topics: Vec<String>, } -/// A failure reading or writing the configuration. -#[derive(Debug, thiserror::Error)] -pub enum Error { - /// The configuration could not be read from or written to its ref. - #[error(transparent)] - Store(#[from] git_store::Error), -} - /// Load the configuration recorded at [`CONFIG_REF`] in `repo`. /// /// An absent ref yields [`Config::default`], as on a repository whose metadata /// has not been set yet. A present but unreadable ref is an error so callers can /// distinguish corruption from "no configuration set". -pub fn load(repo: &Path) -> Result<Config, Error> { +pub fn load(repo: &Path) -> Result<Config, git_store::Error> { Ok(git_store::Store::open(repo)? .load::<Config>(CONFIG_REF)? .unwrap_or_default()) @@ -48,7 +40,7 @@ /// Write `config` to [`CONFIG_REF`], replacing any existing value, as a new /// commit. -pub fn store(repo: &Path, config: &Config) -> Result<(), Error> { +pub fn store(repo: &Path, config: &Config) -> Result<(), git_store::Error> { git_store::Store::open(repo)?.store(CONFIG_REF, config, "Update configuration")?; Ok(()) }
crates/git-ents/src/issues.rs @@ -38,29 +38,21 @@ } } -/// A failure reading or writing an issue. -#[derive(Debug, thiserror::Error)] -pub enum Error { - /// An issue could not be read from or written to its ref. - #[error(transparent)] - Store(#[from] git_store::Error), -} - /// Load the issue recorded at `refs/meta/issues/<id>` in `repo`, or `None` when /// no such issue exists. -pub fn load(repo: &Path, id: &str) -> Result<Option<Issue>, Error> { - Ok(git_store::Store::open(repo)?.load::<Issue>(&format!("{ISSUES_NS}/{id}"))?) +pub fn load(repo: &Path, id: &str) -> Result<Option<Issue>, git_store::Error> { + git_store::Store::open(repo)?.load::<Issue>(&format!("{ISSUES_NS}/{id}")) } /// Write `issue` to `refs/meta/issues/<id>`, replacing any existing value, as a /// new commit so the ref's commit chain is the issue's edit history. -pub fn store(repo: &Path, id: &str, issue: &Issue) -> Result<(), Error> { +pub fn store(repo: &Path, id: &str, issue: &Issue) -> Result<(), git_store::Error> { git_store::Store::open(repo)?.store(&format!("{ISSUES_NS}/{id}"), issue, "Update issue")?; Ok(()) } /// List every issue as `(id, issue)` pairs, newest issue ref first. -pub fn list(repo: &Path) -> Result<Vec<(String, Issue)>, Error> { +pub fn list(repo: &Path) -> Result<Vec<(String, Issue)>, git_store::Error> { let store = git_store::Store::open(repo)?; let prefix = format!("{ISSUES_NS}/"); let mut issues = Vec::new(); @@ -76,7 +68,7 @@ } /// The number of open issues in `repo`. -pub fn open_count(repo: &Path) -> Result<usize, Error> { +pub fn open_count(repo: &Path) -> Result<usize, git_store::Error> { Ok(list(repo)? .into_iter() .filter(|(_id, issue)| issue.is_open())
crates/git-ents/src/signers.rs @@ -40,12 +40,17 @@ pub key: String, } -/// A failure reading or writing the member set. -#[derive(Debug, thiserror::Error)] -pub enum Error { - /// The member set could not be read from or written to its ref. - #[error(transparent)] - Store(#[from] git_store::Error), +impl git_store::Row for Signer { + fn from_pair(fingerprint: String, key: String) -> Self { + Self { + fingerprint, + key: key.trim_end().to_owned(), + } + } + + fn into_pair(self) -> (String, String) { + (self.fingerprint, self.key) + } } /// Load the members recorded at [`MEMBERS_REF`] in `repo`. @@ -53,30 +58,18 @@ /// An absent ref yields an empty set, as on a fresh server whose trust list has /// not been pushed yet. A present but unreadable ref is an error so callers can /// fail closed rather than mistake corruption for "no members". -pub fn load(repo: &Path) -> Result<Vec<Signer>, Error> { - Ok(git_store::Store::open(repo)? - .load_entries::<Members>(MEMBERS_REF)? - .into_iter() - .map(|(fingerprint, key)| Signer { - fingerprint, - key: key.trim_end().to_owned(), - }) - .collect()) +pub fn load(repo: &Path) -> Result<Vec<Signer>, git_store::Error> { + git_store::Store::open(repo)?.load_rows::<Members, Signer>(MEMBERS_REF) } /// Write `signers` to [`MEMBERS_REF`], replacing any existing set, as a new /// commit. -pub fn store(repo: &Path, signers: &[Signer]) -> Result<(), Error> { - let entries = signers - .iter() - .map(|signer| (signer.fingerprint.clone(), signer.key.clone())) - .collect(); - git_store::Store::open(repo)?.store_entries::<Members>( +pub fn store(repo: &Path, signers: &[Signer]) -> Result<(), git_store::Error> { + git_store::Store::open(repo)?.store_rows::<Members, _>( MEMBERS_REF, - entries, + signers.iter().cloned(), "Update members", - )?; - Ok(()) + ) } /// Render `signers` as an OpenSSH `allowed_signers` file that authorizes any