Repository: ollama/ollama
Pull request: types/model: add FilepathNoBuild

Also, add test for DisplayLongest.

Also, plumb fill param to ParseName in MustParseName

Changed files:
filename: types/model/name.go
patch: @@ -185,8 +185,8 @@ func parseMask(s string) Name {
 	return r
 }
 
-func MustParseName(s, defaults string) Name {
-	r := ParseName(s, "")
+func MustParseName(s, fill string) Name {
+	r := ParseName(s, fill)
 	if !r.IsValid() {
 		panic("invalid Name: " + s)
 	}
@@ -643,6 +643,15 @@ func (r Name) Filepath() string {
 	return filepath.Join(r.parts[:]...)
 }
 
+// FilepathNoBuild returns a complete, canonicalized, relative file path using
+// the parts of a complete Name, but without the build part.
+func (r Name) FilepathNoBuild() string {
+	for i := range PartBuild {
+		r.parts[i] = strings.ToLower(r.parts[i])
+	}
+	return filepath.Join(r.parts[:PartBuild]...)
+}
+
 // isValidPart reports if s contains all valid characters for the given
 // part kind.
 func isValidPart(kind PartKind, s string) bool {

filename: types/model/name_test.go
patch: @@ -318,6 +318,13 @@ func TestNameGoString(t *testing.T) {
 	}
 }
 
+func TestDisplayLongest(t *testing.T) {
+	g := ParseName("example.com/library/mistral:latest+Q4_0", FillNothing).DisplayLongest()
+	if g != "example.com/library/mistral:latest" {
+		t.Errorf("got = %q; want %q", g, "example.com/library/mistral:latest")
+	}
+}
+
 func TestDisplayShortest(t *testing.T) {
 	cases := []struct {
 		in        string
@@ -478,30 +485,36 @@ func TestNamePath(t *testing.T) {
 	}
 }
 
-func TestNameFromFilepath(t *testing.T) {
+func TestNameFilepath(t *testing.T) {
 	cases := []struct {
-		in   string
-		want string
+		in          string
+		want        string
+		wantNoBuild string
 	}{
 		{
-			in:   "example.com/library/mistral:latest+Q4_0",
-			want: "example.com/library/mistral/latest/Q4_0",
+			in:          "example.com/library/mistral:latest+Q4_0",
+			want:        "example.com/library/mistral/latest/Q4_0",
+			wantNoBuild: "example.com/library/mistral/latest",
 		},
 		{
-			in:   "Example.Com/Library/Mistral:Latest+Q4_0",
-			want: "example.com/library/mistral/latest/Q4_0",
+			in:          "Example.Com/Library/Mistral:Latest+Q4_0",
+			want:        "example.com/library/mistral/latest/Q4_0",
+			wantNoBuild: "example.com/library/mistral/latest",
 		},
 		{
-			in:   "Example.Com/Library/Mistral:Latest+Q4_0",
-			want: "example.com/library/mistral/latest/Q4_0",
+			in:          "Example.Com/Library/Mistral:Latest+Q4_0",
+			want:        "example.com/library/mistral/latest/Q4_0",
+			wantNoBuild: "example.com/library/mistral/latest",
 		},
 		{
-			in:   "example.com/library/mistral:latest",
-			want: "example.com/library/mistral/latest",
+			in:          "example.com/library/mistral:latest",
+			want:        "example.com/library/mistral/latest",
+			wantNoBuild: "example.com/library/mistral/latest",
 		},
 		{
-			in:   "",
-			want: "",
+			in:          "",
+			want:        "",
+			wantNoBuild: "",
 		},
 	}
 	for _, tt := range cases {
@@ -513,6 +526,11 @@ func TestNameFromFilepath(t *testing.T) {
 			if g != tt.want {
 				t.Errorf("got = %q; want %q", g, tt.want)
 			}
+			g = p.FilepathNoBuild()
+			g = filepath.ToSlash(g)
+			if g != tt.wantNoBuild {
+				t.Errorf("got = %q; want %q", g, tt.wantNoBuild)
+			}
 		})
 	}
 }