Repository: ollama/ollama
Pull request: types/model: remove Digest (for now)

The Digest type needs more thought and is not necessary at the moment.

Changed files:
filename: types/model/name.go
patch: @@ -4,7 +4,6 @@ package model
 
 import (
 	"cmp"
-	"encoding/hex"
 	"errors"
 	"fmt"
 	"log/slog"
@@ -171,11 +170,6 @@ func Merge(a, b Name) Name {
 	return a
 }
 
-// Digest returns the result of [ParseDigest] with the RawDigest field.
-func (n Name) Digest() Digest {
-	return ParseDigest(n.RawDigest)
-}
-
 // String returns the name string, in the format that [ParseNameNoDefaults]
 // accepts as valid, if [Name.IsValid] reports true; otherwise the empty
 // string is returned.
@@ -204,7 +198,7 @@ func (n Name) String() string {
 // IsValid reports whether all parts of the name are present and valid. The
 // digest is a special case, and is checked for validity only if present.
 func (n Name) IsValid() bool {
-	if n.RawDigest != "" && !ParseDigest(n.RawDigest).IsValid() {
+	if n.RawDigest != "" && !isValidPart(kindDigest, n.RawDigest) {
 		return false
 	}
 	return n.IsFullyQualified()
@@ -282,7 +276,7 @@ func isValidPart(kind partKind, s string) bool {
 				return false
 			}
 		case ':':
-			if kind != kindHost {
+			if kind != kindHost && kind != kindDigest {
 				return false
 			}
 		default:
@@ -317,75 +311,3 @@ func cutPromised(s, sep string) (before, after string, ok bool) {
 	}
 	return cmp.Or(before, MissingPart), cmp.Or(after, MissingPart), true
 }
-
-type DigestType int
-
-const (
-	DigestTypeInvalid DigestType = iota
-	DigestTypeSHA256
-)
-
-func (t DigestType) String() string {
-	if t == DigestTypeSHA256 {
-		return "sha256"
-	}
-	return "unknown"
-}
-
-// Digest represents a type and hash of a digest. It is comparable and can
-// be used as a map key.
-type Digest struct {
-	Type DigestType
-	Hash [32]byte
-}
-
-// ParseDigest parses a digest string into a Digest struct. It accepts both
-// the forms:
-//
-//	sha256:deadbeef
-//	sha256-deadbeef
-//
-// The hash part must be exactly 64 characters long.
-//
-// The form "type:hash" does not round trip through [Digest.String].
-func ParseDigest(s string) Digest {
-	typ, hash, ok := cutLast(s, ":")
-	if !ok {
-		typ, hash, ok = cutLast(s, "-")
-		if !ok {
-			return Digest{}
-		}
-	}
-	if typ != "sha256" {
-		return Digest{}
-	}
-	var d Digest
-	n, err := hex.Decode(d.Hash[:], []byte(hash))
-	if err != nil || n != 32 {
-		return Digest{}
-	}
-	return Digest{Type: DigestTypeSHA256, Hash: d.Hash}
-}
-
-// IsValid returns true if the digest has a valid Type and Hash.
-func (d Digest) IsValid() bool {
-	if d.Type != DigestTypeSHA256 {
-		return false
-	}
-	return d.Hash != [32]byte{}
-}
-
-// String returns the digest as a string in the form "type-hash". The hash
-// is encoded as a hex string.
-func (d Digest) String() string {
-	var b strings.Builder
-	b.WriteString(d.Type.String())
-	b.WriteByte('-')
-	b.WriteString(hex.EncodeToString(d.Hash[:]))
-	return b.String()
-}
-
-// LogValue returns a slog.Value that represents the digest as a string.
-func (d Digest) LogValue() slog.Value {
-	return slog.StringValue(d.String())
-}

filename: types/model/name_test.go
patch: @@ -2,7 +2,6 @@ package model
 
 import (
 	"reflect"
-	"strings"
 	"testing"
 )
 
@@ -82,10 +81,10 @@ func TestParseNameParts(t *testing.T) {
 			wantValidDigest: false,
 		},
 		{
-			in: "model@sha256:" + validSHA256Hex,
+			in: "model@sha256:123",
 			want: Name{
 				Model:     "model",
-				RawDigest: "sha256:" + validSHA256Hex,
+				RawDigest: "sha256:123",
 			},
 			wantValidDigest: true,
 		},
@@ -97,9 +96,6 @@ func TestParseNameParts(t *testing.T) {
 			if !reflect.DeepEqual(got, tt.want) {
 				t.Errorf("parseName(%q) = %v; want %v", tt.in, got, tt.want)
 			}
-			if got.Digest().IsValid() != tt.wantValidDigest {
-				t.Errorf("parseName(%q).Digest().IsValid() = %v; want %v", tt.in, got.Digest().IsValid(), tt.wantValidDigest)
-			}
 		})
 	}
 }
@@ -239,57 +235,3 @@ func FuzzName(f *testing.F) {
 
 	})
 }
-
-const validSHA256Hex = "abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789"
-
-func TestParseDigest(t *testing.T) {
-	cases := map[string]bool{
-		"sha256-1000000000000000000000000000000000000000000000000000000000000000": true,
-		"sha256:1000000000000000000000000000000000000000000000000000000000000000": true,
-		"sha256:0000000000000000000000000000000000000000000000000000000000000000": false,
-
-		"sha256:" + validSHA256Hex: true,
-		"sha256-" + validSHA256Hex: true,
-
-		"":                               false,
-		"sha134:" + validSHA256Hex:       false,
-		"sha256:" + validSHA256Hex + "x": false,
-		"sha256:x" + validSHA256Hex:      false,
-		"sha256-" + validSHA256Hex + "x": false,
-		"sha256-x":                       false,
-	}
-
-	for s, want := range cases {
-		t.Run(s, func(t *testing.T) {
-			d := ParseDigest(s)
-			if d.IsValid() != want {
-				t.Errorf("ParseDigest(%q).IsValid() = %v; want %v", s, d.IsValid(), want)
-			}
-			norm := strings.ReplaceAll(s, ":", "-")
-			if d.IsValid() && d.String() != norm {
-				t.Errorf("ParseDigest(%q).String() = %q; want %q", s, d.String(), norm)
-			}
-		})
-	}
-}
-
-func TestDigestString(t *testing.T) {
-	cases := []struct {
-		in   string
-		want string
-	}{
-		{in: "sha256:" + validSHA256Hex, want: "sha256-" + validSHA256Hex},
-		{in: "sha256-" + validSHA256Hex, want: "sha256-" + validSHA256Hex},
-		{in: "", want: "unknown-0000000000000000000000000000000000000000000000000000000000000000"},
-		{in: "blah-100000000000000000000000000000000000000000000000000000000000000", want: "unknown-0000000000000000000000000000000000000000000000000000000000000000"},
-	}
-
-	for _, tt := range cases {
-		t.Run(tt.in, func(t *testing.T) {
-			d := ParseDigest(tt.in)
-			if d.String() != tt.want {
-				t.Errorf("ParseDigest(%q).String() = %q; want %q", tt.in, d.String(), tt.want)
-			}
-		})
-	}
-}