git-ents.gitmain
⌘K
foforge
review.rs145 lines · 5.0 KB · rusthistorycomment on this file
1//! `git ents review`: a thin wrapper around `ents_forge::review`'s
2//! business logic — this module only resolves the signer/actor identity
3//! against [`LocalRoot`] (plus, for [`new`], the reviewer's own member id —
4//! the composite key's `<member>` segment, `meta-ref.identity-binding`) and
5//! translates a reached `Outcome` into a CLI-facing [`Result`]
6//! (`crate::mutate::outcome_to_result`), exactly as every other mutation
7//! command does. Every operation is the library call itself
8//! (`lens.parity`); nothing here re-implements one.
9
10use ents_forge::comment::Comment;
11use ents_forge::review;
12use ents_forge::review::{NewReview, Review};
13use ents_model::MemberId;
14use ents_receive::Identity;
15
16use super::{actor, signer};
17use crate::error::Result;
18use crate::mutate::outcome_to_result;
19use crate::root::LocalRoot;
20
21/// `git ents review new`: review a commit as the signer's own member,
22/// writing both its entity ref and its retention pin — or, when this
23/// member already has a review of an ancestor of the target, advancing
24/// that same review fast-forward (`model.review-pin`).
25///
26/// # Errors
27///
28/// [`crate::error::Error::Forge`] if `new.target` does not resolve to a
29/// commit, or serialization or `receive` itself fails for either ref; see
30/// [`crate::mutate::outcome_to_result`] for how a reached refusal renders.
31pub fn new(root: &LocalRoot, new: NewReview, key: Option<std::path::PathBuf>) -> Result<String> {
32 let signer = signer(root, key)?;
33 let member = reviewer_member_id(root, &signer)?;
34 let identity = Identity {
35 actor: actor(&signer),
36 author: None,
37 sign: &|payload| signer.sign(payload),
38 };
39 let (target, outcome) = review::new(
40 &root.refs,
41 &root.objects,
42 &root.events,
43 &root.path,
44 new,
45 &member,
46 &identity,
47 root.mode(),
48 )?;
49 outcome_to_result(outcome, None)?;
50 Ok(target)
51}
52
53/// `git ents review withdraw`: retract the signer's own review of `target`,
54/// resolving the reviewer's member id exactly as [`new`] does — the
55/// withdrawing member is always the signing identity's own resolved
56/// member, never one named on the command line, so this can never be
57/// pointed at someone else's review (`gate.owner-mutation` refuses it even
58/// if it were).
59///
60/// # Errors
61///
62/// [`crate::error::Error::Forge`] (wrapping [`ents_forge::Error::NotFound`])
63/// if this member has no existing review reaching `target`; otherwise as
64/// [`new`].
65pub fn withdraw(root: &LocalRoot, target: String, key: Option<std::path::PathBuf>) -> Result<String> {
66 let signer = signer(root, key)?;
67 let member = reviewer_member_id(root, &signer)?;
68 let identity = Identity {
69 actor: actor(&signer),
70 author: None,
71 sign: &|payload| signer.sign(payload),
72 };
73 let (target, outcome) = review::withdraw(
74 &root.refs,
75 &root.objects,
76 &root.events,
77 &root.path,
78 &target,
79 &member,
80 &identity,
81 root.mode(),
82 )?;
83 outcome_to_result(outcome, None)?;
84 Ok(target)
85}
86
87/// The member id owning the signer's key — the composite review key's
88/// `<member>` segment — via the same key-to-member scan
89/// [`super::members::find_by_key`] already performs for `git ents members
90/// check`. When the signing key enrolls no member (`roots.local`'s
91/// advisory gate never requires enrollment before a local mutation lands),
92/// falls back to the same fingerprint-derived placeholder [`super::actor`]
93/// already uses for its own commit signature: a composite review key still
94/// needs *some* member segment, and `gate.owner-mutation` — not this
95/// fallback — is what actually keys ownership once a real deployment's
96/// mandatory gate is in force.
97///
98/// # Errors
99///
100/// Propagates a ref-store or object read failure.
101fn reviewer_member_id(root: &LocalRoot, signer: &crate::sign::Signer) -> Result<MemberId> {
102 let pubkey = signer.public_openssh();
103 if let Some((username, _state)) =
104 super::members::find_by_key(&root.refs, &root.objects, &pubkey)?
105 {
106 return Ok(MemberId::new(username));
107 }
108 Ok(MemberId::new(super::short_fingerprint(signer)))
109}
110
111/// `git ents review list [--target rev]`: every review recorded in this
112/// repository, keyed by its composite `(target, member)` segments,
113/// optionally filtered to those reviewing `target`.
114///
115/// # Errors
116///
117/// Propagates a ref-store, object read, or revision-resolution failure.
118pub fn list(root: &LocalRoot, target: Option<String>) -> Result<Vec<((String, MemberId), Review)>> {
119 Ok(review::list(
120 &root.refs,
121 &root.objects,
122 &root.path,
123 target.as_deref(),
124 )?)
125}
126
127/// `git ents review show`: `target`/`member`'s review, plus its discussion
128/// thread.
129///
130/// # Errors
131///
132/// [`crate::error::Error::Forge`] (wrapping [`ents_forge::Error::NotFound`])
133/// if `target`/`member` has no review ref.
134pub fn show(
135 root: &LocalRoot,
136 target: &str,
137 member: &str,
138) -> Result<(Review, Vec<(String, Comment)>)> {
139 Ok(review::show(
140 &root.refs,
141 &root.objects,
142 target,
143 &MemberId::new(member),
144 )?)
145}