coverage~bigbes/sr-ht-compare9720ccc2core/spec.go

Coverage
95.2% 20/21 statements
Δ
Blob
3da3a1c
Uncovered L57-L58
1 package core
2
3 import (
4 "fmt"
5 "net/url"
6 "strings"
7 )
8
9 // RepoRef identifies a repository by its owner (without the leading '~') and
10 // name. Both fields should be validated with ValidOwner / ValidRepoName before
11 // they are used to build a filesystem path.
12 type RepoRef struct {
13 Owner string
14 Name string
15 }
16
17 // CompareSpec is a parsed "base..head" or "base...head" comparison request.
18 // ThreeDot selects merge-base (symmetric-difference) semantics, matching git's
19 // "base...head"; when false the comparison is the plain "base..head" range.
20 type CompareSpec struct {
21 Base string
22 Head string
23 ThreeDot bool
24 }
25
26 // ParseCompareSpec parses the compare wildcard from a URL path segment into a
27 // CompareSpec. The grammar is:
28 //
29 // base "..." head -> ThreeDot = true (merge-base / symmetric diff)
30 // base ".." head -> ThreeDot = false (direct range)
31 //
32 // The three-dot form is tried first, because "..." contains "..". Each side is
33 // then url.PathUnescape'd and validated with ValidRef. Both sides must be
34 // non-empty and valid or ErrBadRef (wrapped with detail) is returned.
35 //
36 // Stripping a trailing ".patch" (the raw-diff escape hatch) is the caller's
37 // responsibility and is intentionally not handled here.
38 30 func ParseCompareSpec(raw string) (CompareSpec, error) {
39 30 var base, head string
40 30 var threeDot bool
41 30
42 30 switch {
43 24 case strings.Contains(raw, "..."):
44 24 i := strings.Index(raw, "...")
45 24 threeDot = true
46 24 base, head = raw[:i], raw[i+3:]
47 4 case strings.Contains(raw, ".."):
48 4 i := strings.Index(raw, "..")
49 4 threeDot = false
50 4 base, head = raw[:i], raw[i+2:]
51 2 default:
52 2 return CompareSpec{}, fmt.Errorf("%w: missing '..' or '...' separator in %q", ErrBadRef, raw)
53 }
54
55 28 b, err := url.PathUnescape(base)
56 28 if err != nil {
57 0 return CompareSpec{}, fmt.Errorf("%w: bad percent-encoding in base: %v", ErrBadRef, err)
58 0 }
59 28 h, err := url.PathUnescape(head)
60 28 if err != nil {
61 1 return CompareSpec{}, fmt.Errorf("%w: bad percent-encoding in head: %v", ErrBadRef, err)
62 1 }
63
64 27 if !ValidRef(b) {
65 3 return CompareSpec{}, fmt.Errorf("%w: invalid base ref %q", ErrBadRef, b)
66 3 }
67 24 if !ValidRef(h) {
68 15 return CompareSpec{}, fmt.Errorf("%w: invalid head ref %q", ErrBadRef, h)
69 15 }
70
71 9 return CompareSpec{Base: b, Head: h, ThreeDot: threeDot}, nil
72 }