types/model: reintroduce Digest (#4065)
This commit is contained in:
parent
fcf4d60eee
commit
b9f74ff3d6
|
@ -4,6 +4,7 @@ package model
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"cmp"
|
"cmp"
|
||||||
|
"encoding/hex"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"log/slog"
|
"log/slog"
|
||||||
|
@ -308,3 +309,57 @@ func cutPromised(s, sep string) (before, after string, ok bool) {
|
||||||
}
|
}
|
||||||
return cmp.Or(before, MissingPart), cmp.Or(after, MissingPart), true
|
return cmp.Or(before, MissingPart), cmp.Or(after, MissingPart), true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type DigestType byte
|
||||||
|
|
||||||
|
const (
|
||||||
|
DigestTypeInvalid DigestType = iota
|
||||||
|
DigestTypeSHA256
|
||||||
|
)
|
||||||
|
|
||||||
|
func (t DigestType) String() string {
|
||||||
|
switch t {
|
||||||
|
case DigestTypeSHA256:
|
||||||
|
return "sha256"
|
||||||
|
default:
|
||||||
|
return "invalid"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Digest struct {
|
||||||
|
Type DigestType
|
||||||
|
Sum [32]byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseDigest(s string) (Digest, error) {
|
||||||
|
i := strings.IndexAny(s, "-:")
|
||||||
|
if i < 0 {
|
||||||
|
return Digest{}, fmt.Errorf("invalid digest %q", s)
|
||||||
|
}
|
||||||
|
typ, encSum := s[:i], s[i+1:]
|
||||||
|
if typ != "sha256" {
|
||||||
|
return Digest{}, fmt.Errorf("unsupported digest type %q", typ)
|
||||||
|
}
|
||||||
|
d := Digest{
|
||||||
|
Type: DigestTypeSHA256,
|
||||||
|
}
|
||||||
|
n, err := hex.Decode(d.Sum[:], []byte(encSum))
|
||||||
|
if err != nil {
|
||||||
|
return Digest{}, err
|
||||||
|
}
|
||||||
|
if n != 32 {
|
||||||
|
return Digest{}, fmt.Errorf("digest %q decoded to %d bytes; want 32", encSum, n)
|
||||||
|
}
|
||||||
|
return d, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d Digest) String() string {
|
||||||
|
if d.Type == DigestTypeInvalid {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("sha256-%x", d.Sum)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d Digest) IsValid() bool {
|
||||||
|
return d.Type != DigestTypeInvalid
|
||||||
|
}
|
||||||
|
|
|
@ -232,6 +232,40 @@ func TestFilepathAllocs(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
validSha256 = "sha256-1000000000000000000000000000000000000000000000000000000000000000"
|
||||||
|
validSha256Old = "sha256:1000000000000000000000000000000000000000000000000000000000000000"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestParseDigest(t *testing.T) {
|
||||||
|
cases := []struct {
|
||||||
|
in string
|
||||||
|
want string
|
||||||
|
}{
|
||||||
|
{"", ""}, // empty
|
||||||
|
{"sha123-12", ""}, // invalid type
|
||||||
|
{"sha256-", ""}, // invalid sum
|
||||||
|
{"sha256-123", ""}, // invalid odd length sum
|
||||||
|
|
||||||
|
{validSha256, validSha256},
|
||||||
|
{validSha256Old, validSha256},
|
||||||
|
}
|
||||||
|
for _, tt := range cases {
|
||||||
|
t.Run(tt.in, func(t *testing.T) {
|
||||||
|
got, err := ParseDigest(tt.in)
|
||||||
|
if err != nil {
|
||||||
|
if tt.want != "" {
|
||||||
|
t.Errorf("parseDigest(%q) = %v; want %v", tt.in, err, tt.want)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if got.String() != tt.want {
|
||||||
|
t.Errorf("parseDigest(%q).String() = %q; want %q", tt.in, got, tt.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func FuzzName(f *testing.F) {
|
func FuzzName(f *testing.F) {
|
||||||
for s := range testCases {
|
for s := range testCases {
|
||||||
f.Add(s)
|
f.Add(s)
|
||||||
|
|
Loading…
Reference in a new issue