refactor: route the meta-ref set documents through `Row`
commit
b4b8b84refactor: 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
Reviews
No reviews of this commit yet — record a verdict below.
Start a review
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