4dc4f1be34
This allows users of a valid Digest to know it has a minimum of 2 characters in the hash part for use when sharding. This is a reasonable restriction as the hash part is a SHA256 hash which is 64 characters long, which is the common hash used. There is no anticipation of using a hash with less than 2 characters. Also, add MustParseDigest. Also, replace Digest.Type with Digest.Split for getting both the type and hash parts together, which is most the common case when asking for either.
87 lines
1.8 KiB
Go
87 lines
1.8 KiB
Go
package model
|
|
|
|
import (
|
|
"fmt"
|
|
"log/slog"
|
|
"strings"
|
|
"unicode"
|
|
)
|
|
|
|
// Digest represents a digest of a model Manifest. It is a comparable value
|
|
// type and is immutable.
|
|
//
|
|
// The zero Digest is not a valid digest.
|
|
type Digest struct {
|
|
s string
|
|
}
|
|
|
|
// Split returns the digest type and the digest value.
|
|
func (d Digest) Split() (typ, digest string) {
|
|
typ, digest, _ = strings.Cut(d.s, "-")
|
|
return
|
|
}
|
|
|
|
// String returns the digest in the form of "<digest-type>-<digest>", or the
|
|
// empty string if the digest is invalid.
|
|
func (d Digest) String() string { return d.s }
|
|
|
|
// IsValid returns true if the digest is valid (not zero).
|
|
//
|
|
// A valid digest may be created only by ParseDigest, or
|
|
// ParseName(name).Digest().
|
|
func (d Digest) IsValid() bool { return d.s != "" }
|
|
|
|
// LogValue implements slog.Value.
|
|
func (d Digest) LogValue() slog.Value {
|
|
return slog.StringValue(d.String())
|
|
}
|
|
|
|
var (
|
|
_ slog.LogValuer = Digest{}
|
|
)
|
|
|
|
// ParseDigest parses a string in the form of "<digest-type>-<digest>" into a
|
|
// Digest.
|
|
func ParseDigest(s string) Digest {
|
|
typ, digest, ok := strings.Cut(s, "-")
|
|
if !ok {
|
|
typ, digest, ok = strings.Cut(s, ":")
|
|
}
|
|
if ok && isValidDigestType(typ) && isValidHex(digest) && len(digest) >= 2 {
|
|
return Digest{s: fmt.Sprintf("%s-%s", typ, digest)}
|
|
}
|
|
return Digest{}
|
|
}
|
|
|
|
func MustParseDigest(s string) Digest {
|
|
d := ParseDigest(s)
|
|
if !d.IsValid() {
|
|
panic(fmt.Sprintf("invalid digest: %q", s))
|
|
}
|
|
return d
|
|
}
|
|
|
|
func isValidDigestType(s string) bool {
|
|
if len(s) == 0 {
|
|
return false
|
|
}
|
|
for _, r := range s {
|
|
if !unicode.IsLower(r) && !unicode.IsDigit(r) {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
func isValidHex(s string) bool {
|
|
if len(s) == 0 {
|
|
return false
|
|
}
|
|
for i := range s {
|
|
c := s[i]
|
|
if c < '0' || c > '9' && c < 'a' || c > 'f' {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|