detached b292b48

refactor(kvstore): decode the mergeability record without mutating it

commit
b292b481851341fb5e5d43b1276c918ab1bb2cb4
author
brice · · 2026-10-19 13:06 UTC
committer
brice · · 2026-10-19 14:36 UTC
parent
0fe1a7c
signature
GPG VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · arch-gpg Verified for the committer email
1 file · +23 -21
M internal/adapters/kvstore/mergerequests.go +23 -21
@@ -679,38 +679,40 @@ func encodeMergeability(m domain.Mergeability) ([]byte, error) {
679679 })
680680 }
681681
682-//hexcheck:ignore suspicious-business-logic-in-adapter decodes the stored record into the domain value; no rule is applied
683682 func decodeMergeability(repo domain.RepoName, number int64, item kv.Item) (domain.Mergeability, error) {
684683 var pb storagepb.Mergeability
685684 if err := proto.Unmarshal(item.Value, &pb); err != nil {
686685 return domain.Mergeability{}, fmt.Errorf("decode mergeability of %d in %q: %w", number, repo, err)
687686 }
688- out := domain.Mergeability{
689- Repo: repo, Number: number,
690- Behind: int(pb.GetBehind()), BehindCapped: pb.GetBehindCapped(),
691- ConflictPaths: pb.GetConflictPaths(),
692- ConflictPathsTruncated: pb.GetConflictPathsTruncated(),
693- CheckedAt: pb.GetCheckedAt().AsTime(),
694- Version: item.Version,
695- }
696- switch pb.GetState() {
697- case storagepb.MergeabilityState_MERGEABILITY_STATE_UP_TO_DATE:
698- out.State = domain.MergeabilityUpToDate
699- case storagepb.MergeabilityState_MERGEABILITY_STATE_BEHIND:
700- out.State = domain.MergeabilityBehind
701- case storagepb.MergeabilityState_MERGEABILITY_STATE_CONFLICT:
702- out.State = domain.MergeabilityConflict
703- default:
687+ state, ok := mergeabilityStates[pb.GetState()]
688+ if !ok {
704689 return domain.Mergeability{}, fmt.Errorf("decode mergeability of %d in %q: unknown state %d: %w", number, repo, pb.GetState(), domain.ErrNotFound)
705690 }
706- var err error
707- if out.TargetID, err = domain.ParseObjectID(pb.GetTargetId()); err != nil {
691+ target, err := domain.ParseObjectID(pb.GetTargetId())
692+ if err != nil {
708693 return domain.Mergeability{}, fmt.Errorf("decode mergeability of %d in %q: %w", number, repo, err)
709694 }
710- if out.HeadID, err = domain.ParseObjectID(pb.GetHeadId()); err != nil {
695+ head, err := domain.ParseObjectID(pb.GetHeadId())
696+ if err != nil {
711697 return domain.Mergeability{}, fmt.Errorf("decode mergeability of %d in %q: %w", number, repo, err)
712698 }
713- return out, nil
699+ return domain.Mergeability{
700+ Repo: repo, Number: number, State: state,
701+ TargetID: target, HeadID: head,
702+ Behind: int(pb.GetBehind()), BehindCapped: pb.GetBehindCapped(),
703+ ConflictPaths: pb.GetConflictPaths(),
704+ ConflictPathsTruncated: pb.GetConflictPathsTruncated(),
705+ CheckedAt: pb.GetCheckedAt().AsTime(),
706+ Version: item.Version,
707+ }, nil
708+}
709+
710+// mergeabilityStates maps the stored states to the domain ones; CHECKING is
711+// never stored.
712+var mergeabilityStates = map[storagepb.MergeabilityState]domain.MergeabilityState{
713+ storagepb.MergeabilityState_MERGEABILITY_STATE_UP_TO_DATE: domain.MergeabilityUpToDate,
714+ storagepb.MergeabilityState_MERGEABILITY_STATE_BEHIND: domain.MergeabilityBehind,
715+ storagepb.MergeabilityState_MERGEABILITY_STATE_CONFLICT: domain.MergeabilityConflict,
714716 }
715717
716718 func encodeMergeRequest(mr domain.MergeRequest) ([]byte, error) {
@@ -679,38 +679,40 @@ func encodeMergeability(m domain.Mergeability) ([]byte, error) {
679 })679 })
680 }680 }
681 681
682-//hexcheck:ignore suspicious-business-logic-in-adapter decodes the stored record into the domain value; no rule is applied
683 func decodeMergeability(repo domain.RepoName, number int64, item kv.Item) (domain.Mergeability, error) {682 func decodeMergeability(repo domain.RepoName, number int64, item kv.Item) (domain.Mergeability, error) {
684 var pb storagepb.Mergeability683 var pb storagepb.Mergeability
685 if err := proto.Unmarshal(item.Value, &pb); err != nil {684 if err := proto.Unmarshal(item.Value, &pb); err != nil {
686 return domain.Mergeability{}, fmt.Errorf("decode mergeability of %d in %q: %w", number, repo, err)685 return domain.Mergeability{}, fmt.Errorf("decode mergeability of %d in %q: %w", number, repo, err)
687 }686 }
688- out := domain.Mergeability{687+ state, ok := mergeabilityStates[pb.GetState()]
689- Repo: repo, Number: number,688+ if !ok {
690- Behind: int(pb.GetBehind()), BehindCapped: pb.GetBehindCapped(),
691- ConflictPaths: pb.GetConflictPaths(),
692- ConflictPathsTruncated: pb.GetConflictPathsTruncated(),
693- CheckedAt: pb.GetCheckedAt().AsTime(),
694- Version: item.Version,
695- }
696- switch pb.GetState() {
697- case storagepb.MergeabilityState_MERGEABILITY_STATE_UP_TO_DATE:
698- out.State = domain.MergeabilityUpToDate
699- case storagepb.MergeabilityState_MERGEABILITY_STATE_BEHIND:
700- out.State = domain.MergeabilityBehind
701- case storagepb.MergeabilityState_MERGEABILITY_STATE_CONFLICT:
702- out.State = domain.MergeabilityConflict
703- default:
704 return domain.Mergeability{}, fmt.Errorf("decode mergeability of %d in %q: unknown state %d: %w", number, repo, pb.GetState(), domain.ErrNotFound)689 return domain.Mergeability{}, fmt.Errorf("decode mergeability of %d in %q: unknown state %d: %w", number, repo, pb.GetState(), domain.ErrNotFound)
705 }690 }
706- var err error691+ target, err := domain.ParseObjectID(pb.GetTargetId())
707- if out.TargetID, err = domain.ParseObjectID(pb.GetTargetId()); err != nil {692+ if err != nil {
708 return domain.Mergeability{}, fmt.Errorf("decode mergeability of %d in %q: %w", number, repo, err)693 return domain.Mergeability{}, fmt.Errorf("decode mergeability of %d in %q: %w", number, repo, err)
709 }694 }
710- if out.HeadID, err = domain.ParseObjectID(pb.GetHeadId()); err != nil {695+ head, err := domain.ParseObjectID(pb.GetHeadId())
696+ if err != nil {
711 return domain.Mergeability{}, fmt.Errorf("decode mergeability of %d in %q: %w", number, repo, err)697 return domain.Mergeability{}, fmt.Errorf("decode mergeability of %d in %q: %w", number, repo, err)
712 }698 }
713- return out, nil699+ return domain.Mergeability{
700+ Repo: repo, Number: number, State: state,
701+ TargetID: target, HeadID: head,
702+ Behind: int(pb.GetBehind()), BehindCapped: pb.GetBehindCapped(),
703+ ConflictPaths: pb.GetConflictPaths(),
704+ ConflictPathsTruncated: pb.GetConflictPathsTruncated(),
705+ CheckedAt: pb.GetCheckedAt().AsTime(),
706+ Version: item.Version,
707+ }, nil
708+}
709+
710+// mergeabilityStates maps the stored states to the domain ones; CHECKING is
711+// never stored.
712+var mergeabilityStates = map[storagepb.MergeabilityState]domain.MergeabilityState{
713+ storagepb.MergeabilityState_MERGEABILITY_STATE_UP_TO_DATE: domain.MergeabilityUpToDate,
714+ storagepb.MergeabilityState_MERGEABILITY_STATE_BEHIND: domain.MergeabilityBehind,
715+ storagepb.MergeabilityState_MERGEABILITY_STATE_CONFLICT: domain.MergeabilityConflict,
714 }716 }
715 717
716 func encodeMergeRequest(mr domain.MergeRequest) ([]byte, error) {718 func encodeMergeRequest(mr domain.MergeRequest) ([]byte, error) {

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 4

Tags 45

↑↓ moveenter openesc close