types/model: make ParseName use default without question (#3886)

This commit is contained in:
Blake Mizerany 2024-04-24 11:52:55 -07:00 committed by GitHub
parent a6d62e0617
commit ade4b55520
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 45 additions and 34 deletions

View file

@ -150,7 +150,7 @@ type Name struct {
// For any valid s, the fill string is used to fill in missing parts of the // For any valid s, the fill string is used to fill in missing parts of the
// Name. The fill string must be a valid Name with the exception that any part // Name. The fill string must be a valid Name with the exception that any part
// may be the string ("?"), which will not be considered for filling. // may be the string ("?"), which will not be considered for filling.
func ParseName(s, fill string) Name { func ParseNameFill(s, fill string) Name {
var r Name var r Name
parts(s)(func(kind PartKind, part string) bool { parts(s)(func(kind PartKind, part string) bool {
if kind == PartDigest && !ParseDigest(part).IsValid() { if kind == PartDigest && !ParseDigest(part).IsValid() {
@ -170,6 +170,13 @@ func ParseName(s, fill string) Name {
return Name{} return Name{}
} }
// ParseName parses s into a Name, and returns the result of filling it
// with FillDefault. The input string must be a valid string representation
// of a model
func ParseName(s string) Name {
return ParseNameFill(s, "")
}
func parseMask(s string) Name { func parseMask(s string) Name {
var r Name var r Name
parts(s)(func(kind PartKind, part string) bool { parts(s)(func(kind PartKind, part string) bool {
@ -187,7 +194,7 @@ func parseMask(s string) Name {
} }
func MustParseName(s, fill string) Name { func MustParseName(s, fill string) Name {
r := ParseName(s, fill) r := ParseNameFill(s, fill)
if !r.IsValid() { if !r.IsValid() {
panic("invalid Name: " + s) panic("invalid Name: " + s)
} }
@ -579,7 +586,11 @@ func (r Name) IsValid() bool {
// it trims any leading "/" and then calls [ParseName] with fill. // it trims any leading "/" and then calls [ParseName] with fill.
func ParseNameFromURLPath(s, fill string) Name { func ParseNameFromURLPath(s, fill string) Name {
s = strings.TrimPrefix(s, "/") s = strings.TrimPrefix(s, "/")
return ParseName(s, fill) return ParseNameFill(s, fill)
}
func ParseNameFromURLPathFill(s, fill string) Name {
return ParseNameFill(s, fill)
} }
// URLPath returns a complete, canonicalized, relative URL path using the parts of a // URLPath returns a complete, canonicalized, relative URL path using the parts of a

View file

@ -119,11 +119,11 @@ func TestNameConsecutiveDots(t *testing.T) {
for i := 1; i < 10; i++ { for i := 1; i < 10; i++ {
s := strings.Repeat(".", i) s := strings.Repeat(".", i)
if i > 1 { if i > 1 {
if g := ParseName(s, FillNothing).DisplayLong(); g != "" { if g := ParseNameFill(s, FillNothing).DisplayLong(); g != "" {
t.Errorf("ParseName(%q) = %q; want empty string", s, g) t.Errorf("ParseName(%q) = %q; want empty string", s, g)
} }
} else { } else {
if g := ParseName(s, FillNothing).DisplayLong(); g != s { if g := ParseNameFill(s, FillNothing).DisplayLong(); g != s {
t.Errorf("ParseName(%q) = %q; want %q", s, g, s) t.Errorf("ParseName(%q) = %q; want %q", s, g, s)
} }
} }
@ -156,14 +156,14 @@ func TestParseName(t *testing.T) {
s := prefix + baseName s := prefix + baseName
t.Run(s, func(t *testing.T) { t.Run(s, func(t *testing.T) {
name := ParseName(s, FillNothing) name := ParseNameFill(s, FillNothing)
got := fieldsFromName(name) got := fieldsFromName(name)
if got != want { if got != want {
t.Errorf("ParseName(%q) = %q; want %q", s, got, want) t.Errorf("ParseName(%q) = %q; want %q", s, got, want)
} }
// test round-trip // test round-trip
if !ParseName(name.DisplayLong(), FillNothing).EqualFold(name) { if !ParseNameFill(name.DisplayLong(), FillNothing).EqualFold(name) {
t.Errorf("ParseName(%q).String() = %s; want %s", s, name.DisplayLong(), baseName) t.Errorf("ParseName(%q).String() = %s; want %s", s, name.DisplayLong(), baseName)
} }
}) })
@ -188,7 +188,7 @@ func TestParseNameFill(t *testing.T) {
for _, tt := range cases { for _, tt := range cases {
t.Run(tt.in, func(t *testing.T) { t.Run(tt.in, func(t *testing.T) {
name := ParseName(tt.in, tt.fill) name := ParseNameFill(tt.in, tt.fill)
if g := name.DisplayLong(); g != tt.want { if g := name.DisplayLong(); g != tt.want {
t.Errorf("ParseName(%q, %q) = %q; want %q", tt.in, tt.fill, g, tt.want) t.Errorf("ParseName(%q, %q) = %q; want %q", tt.in, tt.fill, g, tt.want)
} }
@ -201,7 +201,7 @@ func TestParseNameFill(t *testing.T) {
t.Fatal("expected panic") t.Fatal("expected panic")
} }
}() }()
ParseName("x", "^") ParseNameFill("x", "^")
}) })
} }
@ -212,7 +212,7 @@ func TestParseNameHTTPDoublePrefixStrip(t *testing.T) {
} }
for _, s := range cases { for _, s := range cases {
t.Run(s, func(t *testing.T) { t.Run(s, func(t *testing.T) {
name := ParseName(s, FillNothing) name := ParseNameFill(s, FillNothing)
if name.IsValid() { if name.IsValid() {
t.Errorf("expected invalid path; got %#v", name) t.Errorf("expected invalid path; got %#v", name)
} }
@ -237,7 +237,7 @@ func TestCompleteWithAndWithoutBuild(t *testing.T) {
for _, tt := range cases { for _, tt := range cases {
t.Run(tt.in, func(t *testing.T) { t.Run(tt.in, func(t *testing.T) {
p := ParseName(tt.in, FillNothing) p := ParseNameFill(tt.in, FillNothing)
t.Logf("ParseName(%q) = %#v", tt.in, p) t.Logf("ParseName(%q) = %#v", tt.in, p)
if g := p.IsComplete(); g != tt.complete { if g := p.IsComplete(); g != tt.complete {
t.Errorf("Complete(%q) = %v; want %v", tt.in, g, tt.complete) t.Errorf("Complete(%q) = %v; want %v", tt.in, g, tt.complete)
@ -252,7 +252,7 @@ func TestCompleteWithAndWithoutBuild(t *testing.T) {
// inlined when used in Complete, preventing any allocations or // inlined when used in Complete, preventing any allocations or
// escaping to the heap. // escaping to the heap.
allocs := testing.AllocsPerRun(1000, func() { allocs := testing.AllocsPerRun(1000, func() {
keep(ParseName("complete.com/x/mistral:latest+Q4_0", FillNothing).IsComplete()) keep(ParseNameFill("complete.com/x/mistral:latest+Q4_0", FillNothing).IsComplete())
}) })
if allocs > 0 { if allocs > 0 {
t.Errorf("Complete allocs = %v; want 0", allocs) t.Errorf("Complete allocs = %v; want 0", allocs)
@ -269,7 +269,7 @@ func TestNameLogValue(t *testing.T) {
t.Run(s, func(t *testing.T) { t.Run(s, func(t *testing.T) {
var b bytes.Buffer var b bytes.Buffer
log := slog.New(slog.NewTextHandler(&b, nil)) log := slog.New(slog.NewTextHandler(&b, nil))
name := ParseName(s, FillNothing) name := ParseNameFill(s, FillNothing)
log.Info("", "name", name) log.Info("", "name", name)
want := fmt.Sprintf("name=%s", name.GoString()) want := fmt.Sprintf("name=%s", name.GoString())
got := b.String() got := b.String()
@ -316,7 +316,7 @@ func TestNameGoString(t *testing.T) {
for _, tt := range cases { for _, tt := range cases {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
p := ParseName(tt.in, FillNothing) p := ParseNameFill(tt.in, FillNothing)
tt.wantGoString = cmp.Or(tt.wantGoString, tt.in) tt.wantGoString = cmp.Or(tt.wantGoString, tt.in)
if g := fmt.Sprintf("%#v", p); g != tt.wantGoString { if g := fmt.Sprintf("%#v", p); g != tt.wantGoString {
t.Errorf("GoString() = %q; want %q", g, tt.wantGoString) t.Errorf("GoString() = %q; want %q", g, tt.wantGoString)
@ -326,7 +326,7 @@ func TestNameGoString(t *testing.T) {
} }
func TestDisplayLongest(t *testing.T) { func TestDisplayLongest(t *testing.T) {
g := ParseName("example.com/library/mistral:latest+Q4_0", FillNothing).DisplayLongest() g := ParseNameFill("example.com/library/mistral:latest+Q4_0", FillNothing).DisplayLongest()
if g != "example.com/library/mistral:latest" { if g != "example.com/library/mistral:latest" {
t.Errorf("got = %q; want %q", g, "example.com/library/mistral:latest") t.Errorf("got = %q; want %q", g, "example.com/library/mistral:latest")
} }
@ -377,7 +377,7 @@ func TestDisplayShortest(t *testing.T) {
} }
}() }()
p := ParseName(tt.in, FillNothing) p := ParseNameFill(tt.in, FillNothing)
t.Logf("ParseName(%q) = %#v", tt.in, p) t.Logf("ParseName(%q) = %#v", tt.in, p)
if g := p.DisplayShortest(tt.mask); g != tt.want { if g := p.DisplayShortest(tt.mask); g != tt.want {
t.Errorf("got = %q; want %q", g, tt.want) t.Errorf("got = %q; want %q", g, tt.want)
@ -388,7 +388,7 @@ func TestDisplayShortest(t *testing.T) {
func TestParseNameAllocs(t *testing.T) { func TestParseNameAllocs(t *testing.T) {
allocs := testing.AllocsPerRun(1000, func() { allocs := testing.AllocsPerRun(1000, func() {
keep(ParseName("example.com/mistral:7b+Q4_0", FillNothing)) keep(ParseNameFill("example.com/mistral:7b+Q4_0", FillNothing))
}) })
if allocs > 0 { if allocs > 0 {
t.Errorf("ParseName allocs = %v; want 0", allocs) t.Errorf("ParseName allocs = %v; want 0", allocs)
@ -399,7 +399,7 @@ func BenchmarkParseName(b *testing.B) {
b.ReportAllocs() b.ReportAllocs()
for range b.N { for range b.N {
keep(ParseName("example.com/mistral:7b+Q4_0", FillNothing)) keep(ParseNameFill("example.com/mistral:7b+Q4_0", FillNothing))
} }
} }
@ -430,7 +430,7 @@ func FuzzParseName(f *testing.F) {
f.Add(":@!@") f.Add(":@!@")
f.Add("...") f.Add("...")
f.Fuzz(func(t *testing.T, s string) { f.Fuzz(func(t *testing.T, s string) {
r0 := ParseName(s, FillNothing) r0 := ParseNameFill(s, FillNothing)
if strings.Contains(s, "..") && !r0.IsZero() { if strings.Contains(s, "..") && !r0.IsZero() {
t.Fatalf("non-zero value for path with '..': %q", s) t.Fatalf("non-zero value for path with '..': %q", s)
@ -453,7 +453,7 @@ func FuzzParseName(f *testing.F) {
t.Errorf("String() did not round-trip with case insensitivity: %q\ngot = %q\nwant = %q", s, r0.DisplayLong(), s) t.Errorf("String() did not round-trip with case insensitivity: %q\ngot = %q\nwant = %q", s, r0.DisplayLong(), s)
} }
r1 := ParseName(r0.DisplayLong(), FillNothing) r1 := ParseNameFill(r0.DisplayLong(), FillNothing)
if !r0.EqualFold(r1) { if !r0.EqualFold(r1) {
t.Errorf("round-trip mismatch: %+v != %+v", r0, r1) t.Errorf("round-trip mismatch: %+v != %+v", r0, r1)
} }
@ -461,7 +461,7 @@ func FuzzParseName(f *testing.F) {
} }
func TestNameStringAllocs(t *testing.T) { func TestNameStringAllocs(t *testing.T) {
name := ParseName("example.com/ns/mistral:latest+Q4_0", FillNothing) name := ParseNameFill("example.com/ns/mistral:latest+Q4_0", FillNothing)
allocs := testing.AllocsPerRun(1000, func() { allocs := testing.AllocsPerRun(1000, func() {
keep(name.DisplayLong()) keep(name.DisplayLong())
}) })
@ -483,7 +483,7 @@ func TestNamePath(t *testing.T) {
} }
for _, tt := range cases { for _, tt := range cases {
t.Run(tt.in, func(t *testing.T) { t.Run(tt.in, func(t *testing.T) {
p := ParseName(tt.in, FillNothing) p := ParseNameFill(tt.in, FillNothing)
t.Logf("ParseName(%q) = %#v", tt.in, p) t.Logf("ParseName(%q) = %#v", tt.in, p)
if g := p.DisplayURLPath(); g != tt.want { if g := p.DisplayURLPath(); g != tt.want {
t.Errorf("got = %q; want %q", g, tt.want) t.Errorf("got = %q; want %q", g, tt.want)
@ -526,7 +526,7 @@ func TestNameFilepath(t *testing.T) {
} }
for _, tt := range cases { for _, tt := range cases {
t.Run(tt.in, func(t *testing.T) { t.Run(tt.in, func(t *testing.T) {
p := ParseName(tt.in, FillNothing) p := ParseNameFill(tt.in, FillNothing)
t.Logf("ParseName(%q) = %#v", tt.in, p) t.Logf("ParseName(%q) = %#v", tt.in, p)
g := p.Filepath() g := p.Filepath()
g = filepath.ToSlash(g) g = filepath.ToSlash(g)
@ -587,7 +587,7 @@ func TestParseNameFilepath(t *testing.T) {
t.Run(tt.in, func(t *testing.T) { t.Run(tt.in, func(t *testing.T) {
in := strings.ReplaceAll(tt.in, "/", string(filepath.Separator)) in := strings.ReplaceAll(tt.in, "/", string(filepath.Separator))
fill := cmp.Or(tt.fill, FillNothing) fill := cmp.Or(tt.fill, FillNothing)
want := ParseName(tt.want, fill) want := ParseNameFill(tt.want, fill)
if g := ParseNameFromFilepath(in, fill); !g.EqualFold(want) { if g := ParseNameFromFilepath(in, fill); !g.EqualFold(want) {
t.Errorf("got = %q; want %q", g.DisplayLong(), tt.want) t.Errorf("got = %q; want %q", g.DisplayLong(), tt.want)
} }
@ -645,12 +645,12 @@ func ExampleName_MapHash() {
m := map[uint64]bool{} m := map[uint64]bool{}
// key 1 // key 1
m[ParseName("mistral:latest+q4", FillNothing).MapHash()] = true m[ParseNameFill("mistral:latest+q4", FillNothing).MapHash()] = true
m[ParseName("miSTRal:latest+Q4", FillNothing).MapHash()] = true m[ParseNameFill("miSTRal:latest+Q4", FillNothing).MapHash()] = true
m[ParseName("mistral:LATest+Q4", FillNothing).MapHash()] = true m[ParseNameFill("mistral:LATest+Q4", FillNothing).MapHash()] = true
// key 2 // key 2
m[ParseName("mistral:LATest", FillNothing).MapHash()] = true m[ParseNameFill("mistral:LATest", FillNothing).MapHash()] = true
fmt.Println(len(m)) fmt.Println(len(m))
// Output: // Output:
@ -659,9 +659,9 @@ func ExampleName_MapHash() {
func ExampleName_CompareFold_sort() { func ExampleName_CompareFold_sort() {
names := []Name{ names := []Name{
ParseName("mistral:latest", FillNothing), ParseNameFill("mistral:latest", FillNothing),
ParseName("mistRal:7b+q4", FillNothing), ParseNameFill("mistRal:7b+q4", FillNothing),
ParseName("MIstral:7b", FillNothing), ParseNameFill("MIstral:7b", FillNothing),
} }
slices.SortFunc(names, Name.CompareFold) slices.SortFunc(names, Name.CompareFold)
@ -682,7 +682,7 @@ func ExampleName_completeAndResolved() {
"x/y/z:latest+q4_0", "x/y/z:latest+q4_0",
"@sha123-abc", "@sha123-abc",
} { } {
name := ParseName(s, FillNothing) name := ParseNameFill(s, FillNothing)
fmt.Printf("complete:%v resolved:%v digest:%s\n", name.IsComplete(), name.IsResolved(), name.Digest()) fmt.Printf("complete:%v resolved:%v digest:%s\n", name.IsComplete(), name.IsResolved(), name.Digest())
} }
@ -693,7 +693,7 @@ func ExampleName_completeAndResolved() {
} }
func ExampleName_DisplayShortest() { func ExampleName_DisplayShortest() {
name := ParseName("example.com/jmorganca/mistral:latest+Q4_0", FillNothing) name := ParseNameFill("example.com/jmorganca/mistral:latest+Q4_0", FillNothing)
fmt.Println(name.DisplayShortest("example.com/jmorganca/_:latest")) fmt.Println(name.DisplayShortest("example.com/jmorganca/_:latest"))
fmt.Println(name.DisplayShortest("example.com/_/_:latest")) fmt.Println(name.DisplayShortest("example.com/_/_:latest"))
@ -701,7 +701,7 @@ func ExampleName_DisplayShortest() {
fmt.Println(name.DisplayShortest("_/_/_:_")) fmt.Println(name.DisplayShortest("_/_/_:_"))
// Default // Default
name = ParseName("registry.ollama.ai/library/mistral:latest+Q4_0", FillNothing) name = ParseNameFill("registry.ollama.ai/library/mistral:latest+Q4_0", FillNothing)
fmt.Println(name.DisplayShortest("")) fmt.Println(name.DisplayShortest(""))
// Output: // Output: