x/model: {Valid,Complete,Resolved} -> {IsValid,IsComplete,IsResolved}

This commit is contained in:
Blake Mizerany 2024-04-07 18:17:17 -07:00
parent 6c1c0f9f1a
commit 8b62eaf059
6 changed files with 40 additions and 40 deletions

View File

@ -53,7 +53,7 @@ func Open(dir string) (*Server, error) {
func (s *Server) Build(ref string, f model.File) error { func (s *Server) Build(ref string, f model.File) error {
mp := model.ParseName(ref) mp := model.ParseName(ref)
if !mp.CompleteNoBuild() { if !mp.IsCompleteNoBuild() {
return fmt.Errorf("%w: %q", ErrIncompleteRef, ref) return fmt.Errorf("%w: %q", ErrIncompleteRef, ref)
} }
@ -177,7 +177,7 @@ func (s *Server) setManifestData(mp model.Name, data []byte) error {
} }
func (s *Server) refFileName(mp model.Name) (string, error) { func (s *Server) refFileName(mp model.Name) (string, error) {
if !mp.Complete() { if !mp.IsComplete() {
return "", fmt.Errorf("ref not fully qualified: %q", mp) return "", fmt.Errorf("ref not fully qualified: %q", mp)
} }
return filepath.Join(s.st.Dir(), "manifests", filepath.Join(mp.Parts()...)), nil return filepath.Join(s.st.Dir(), "manifests", filepath.Join(mp.Parts()...)), nil

View File

@ -21,10 +21,10 @@ type Digest struct {
func (d Digest) Type() string { return d.typ } func (d Digest) Type() string { return d.typ }
func (d Digest) Digest() string { return d.digest } func (d Digest) Digest() string { return d.digest }
func (d Digest) Valid() bool { return d != Digest{} } func (d Digest) IsValid() bool { return d != Digest{} }
func (d Digest) String() string { func (d Digest) String() string {
if !d.Valid() { if !d.IsValid() {
return "" return ""
} }
return fmt.Sprintf("%s-%s", d.typ, d.digest) return fmt.Sprintf("%s-%s", d.typ, d.digest)
@ -35,7 +35,7 @@ func (d Digest) MarshalText() ([]byte, error) {
} }
func (d *Digest) UnmarshalText(text []byte) error { func (d *Digest) UnmarshalText(text []byte) error {
if d.Valid() { if d.IsValid() {
return errors.New("model.Digest: illegal UnmarshalText on valid Digest") return errors.New("model.Digest: illegal UnmarshalText on valid Digest")
} }
*d = ParseDigest(string(text)) *d = ParseDigest(string(text))
@ -52,7 +52,7 @@ var (
) )
func (d *Digest) Scan(src any) error { func (d *Digest) Scan(src any) error {
if d.Valid() { if d.IsValid() {
return errors.New("model.Digest: illegal Scan on valid Digest") return errors.New("model.Digest: illegal Scan on valid Digest")
} }
switch v := src.(type) { switch v := src.(type) {

View File

@ -42,7 +42,7 @@ func TestDigestString(t *testing.T) {
// Test cases. // Test cases.
for s, d := range testDigests { for s, d := range testDigests {
want := s want := s
if !d.Valid() { if !d.IsValid() {
want = "" want = ""
} }
got := d.String() got := d.String()

View File

@ -73,7 +73,7 @@ func (k PartKind) String() string {
// are optional. // are optional.
// //
// A Name is considered "complete" if it has all parts present. To check if a // A Name is considered "complete" if it has all parts present. To check if a
// Name is complete, use [Name.Complete]. // Name is complete, use [Name.IsComplete].
// //
// To compare two names in a case-insensitive manner, use [Name.EqualFold]. // To compare two names in a case-insensitive manner, use [Name.EqualFold].
// //
@ -87,7 +87,7 @@ func (k PartKind) String() string {
// //
// The parts can be obtained in their original form by calling [Name.Parts]. // The parts can be obtained in their original form by calling [Name.Parts].
// //
// To check if a Name has at minimum a valid model part, use [Name.Valid]. // To check if a Name has at minimum a valid model part, use [Name.IsValid].
// //
// To make a Name by filling in missing parts from another Name, use [Fill]. // To make a Name by filling in missing parts from another Name, use [Fill].
type Name struct { type Name struct {
@ -142,14 +142,14 @@ func ParseName(s string) Name {
} }
if kind == PartDigest { if kind == PartDigest {
r.digest = ParseDigest(part) r.digest = ParseDigest(part)
if !r.digest.Valid() { if !r.digest.IsValid() {
return Name{} return Name{}
} }
continue continue
} }
r.parts[kind] = part r.parts[kind] = part
} }
if r.Valid() || r.Resolved() { if r.IsValid() || r.IsResolved() {
return r return r
} }
return Name{} return Name{}
@ -254,8 +254,8 @@ var seps = [...]string{
// //
// Missing parts and their seperators are not written. // Missing parts and their seperators are not written.
// //
// The full digest is always prefixed with "@". That is if [Name.Valid] // The full digest is always prefixed with "@". That is if [Name.IsValid]
// reports false and [Name.Resolved] reports true, then the string is // reports false and [Name.IsResolved] reports true, then the string is
// returned as "@<digest-type>-<digest>". // returned as "@<digest-type>-<digest>".
func (r Name) writeTo(w io.StringWriter) { func (r Name) writeTo(w io.StringWriter) {
var partsWritten int var partsWritten int
@ -269,7 +269,7 @@ func (r Name) writeTo(w io.StringWriter) {
w.WriteString(r.parts[i]) w.WriteString(r.parts[i])
partsWritten++ partsWritten++
} }
if r.Resolved() { if r.IsResolved() {
w.WriteString("@") w.WriteString("@")
w.WriteString(r.digest.String()) w.WriteString(r.digest.String())
} }
@ -306,7 +306,7 @@ func (r Name) GoString() string {
for i := range r.parts { for i := range r.parts {
r.parts[i] = cmp.Or(r.parts[i], "?") r.parts[i] = cmp.Or(r.parts[i], "?")
} }
if !r.Resolved() { if !r.IsResolved() {
r.digest = Digest{"?", "?"} r.digest = Digest{"?", "?"}
} }
return r.String() return r.String()
@ -339,7 +339,7 @@ func (r Name) MarshalText() ([]byte, error) {
// //
// It is an error to call UnmarshalText on a valid Name. // It is an error to call UnmarshalText on a valid Name.
func (r *Name) UnmarshalText(text []byte) error { func (r *Name) UnmarshalText(text []byte) error {
if r.Valid() { if r.IsValid() {
// The invariant of UnmarshalText is that it should only be // The invariant of UnmarshalText is that it should only be
// called on an invalid/zero Name. If we allow UnmarshalText // called on an invalid/zero Name. If we allow UnmarshalText
// on a valid Name, then the Name will be mutated, breaking // on a valid Name, then the Name will be mutated, breaking
@ -359,7 +359,7 @@ var (
// Scan implements [database/sql.Scanner]. // Scan implements [database/sql.Scanner].
func (r *Name) Scan(src any) error { func (r *Name) Scan(src any) error {
if r.Valid() { if r.IsValid() {
// The invariant of Scan is that it should only be called on an // The invariant of Scan is that it should only be called on an
// invalid/zero Name. If we allow Scan on a valid Name, then the // invalid/zero Name. If we allow Scan on a valid Name, then the
// Name will be mutated, breaking the immutability of the Name. // Name will be mutated, breaking the immutability of the Name.
@ -382,29 +382,29 @@ func (r Name) Value() (driver.Value, error) {
return r.String(), nil return r.String(), nil
} }
// Complete reports whether the Name is fully qualified. That is it has a // IsComplete reports whether the Name is fully qualified. That is it has a
// domain, namespace, name, tag, and build. // domain, namespace, name, tag, and build.
func (r Name) Complete() bool { func (r Name) IsComplete() bool {
return !slices.Contains(r.parts[:PartDigest], "") return !slices.Contains(r.parts[:PartDigest], "")
} }
// CompleteNoBuild is like [Name.Complete] but it does not require the // IsCompleteNoBuild is like [Name.IsComplete] but it does not require the
// build part to be present. // build part to be present.
func (r Name) CompleteNoBuild() bool { func (r Name) IsCompleteNoBuild() bool {
return !slices.Contains(r.parts[:PartBuild], "") return !slices.Contains(r.parts[:PartBuild], "")
} }
// Resolved reports true if the Name has a valid digest. // IsResolved reports true if the Name has a valid digest.
// //
// It is possible to have a valid Name, or a complete Name that is not // It is possible to have a valid Name, or a complete Name that is not
// resolved. // resolved.
func (r Name) Resolved() bool { func (r Name) IsResolved() bool {
return r.digest.Valid() return r.digest.IsValid()
} }
// Digest returns the digest part of the Name, if any. // Digest returns the digest part of the Name, if any.
// //
// If Digest returns a non-empty string, then [Name.Resolved] will return // If Digest returns a non-empty string, then [Name.IsResolved] will return
// true, and digest is considered valid. // true, and digest is considered valid.
func (r Name) Digest() Digest { func (r Name) Digest() Digest {
return r.digest return r.digest
@ -558,9 +558,9 @@ func Parts(s string) iter.Seq2[PartKind, string] {
} }
} }
// Valid returns true if the Name hPartas a valid nick. To know if a Name is // IsValid returns true if the Name hPartas a valid nick. To know if a Name is
// "complete", use [Name.Complete]. // "complete", use [Name.IsComplete].
func (r Name) Valid() bool { func (r Name) IsValid() bool {
// Parts ensures we only have valid parts, so no need to validate // Parts ensures we only have valid parts, so no need to validate
// them here, only check if we have a name or not. // them here, only check if we have a name or not.
return r.parts[PartModel] != "" return r.parts[PartModel] != ""

View File

@ -29,7 +29,7 @@ func fieldsFromName(p Name) fields {
func mustParse(s string) Name { func mustParse(s string) Name {
p := ParseName(s) p := ParseName(s)
if !p.Valid() { if !p.IsValid() {
panic(fmt.Sprintf("invalid name: %q", s)) panic(fmt.Sprintf("invalid name: %q", s))
} }
return p return p
@ -142,15 +142,15 @@ func TestParseName(t *testing.T) {
t.Errorf("ParseName(%q).String() = %s; want %s", s, name.String(), baseName) t.Errorf("ParseName(%q).String() = %s; want %s", s, name.String(), baseName)
} }
if name.Valid() && name.DisplayModel() == "" { if name.IsValid() && name.DisplayModel() == "" {
t.Errorf("Valid() = true; Model() = %q; want non-empty name", got.model) t.Errorf("Valid() = true; Model() = %q; want non-empty name", got.model)
} else if !name.Valid() && name.DisplayModel() != "" { } else if !name.IsValid() && name.DisplayModel() != "" {
t.Errorf("Valid() = false; Model() = %q; want empty name", got.model) t.Errorf("Valid() = false; Model() = %q; want empty name", got.model)
} }
if name.Resolved() && !name.Digest().Valid() { if name.IsResolved() && !name.Digest().IsValid() {
t.Errorf("Resolved() = true; Digest() = %q; want non-empty digest", got.digest) t.Errorf("Resolved() = true; Digest() = %q; want non-empty digest", got.digest)
} else if !name.Resolved() && name.Digest().Valid() { } else if !name.IsResolved() && name.Digest().IsValid() {
t.Errorf("Resolved() = false; Digest() = %q; want empty digest", got.digest) t.Errorf("Resolved() = false; Digest() = %q; want empty digest", got.digest)
} }
}) })
@ -176,10 +176,10 @@ func TestCompleteWithAndWithoutBuild(t *testing.T) {
t.Run(tt.in, func(t *testing.T) { t.Run(tt.in, func(t *testing.T) {
p := ParseName(tt.in) p := ParseName(tt.in)
t.Logf("ParseName(%q) = %#v", tt.in, p) t.Logf("ParseName(%q) = %#v", tt.in, p)
if g := p.Complete(); 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)
} }
if g := p.CompleteNoBuild(); g != tt.completeNoBuild { if g := p.IsCompleteNoBuild(); g != tt.completeNoBuild {
t.Errorf("CompleteNoBuild(%q) = %v; want %v", tt.in, g, tt.completeNoBuild) t.Errorf("CompleteNoBuild(%q) = %v; want %v", tt.in, g, tt.completeNoBuild)
} }
}) })
@ -189,7 +189,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").Complete()) keep(ParseName("complete.com/x/mistral:latest+Q4_0").IsComplete())
}) })
if allocs > 0 { if allocs > 0 {
t.Errorf("Complete allocs = %v; want 0", allocs) t.Errorf("Complete allocs = %v; want 0", allocs)
@ -337,7 +337,7 @@ func FuzzParseName(f *testing.F) {
f.Add("x/y/z:8n+I") f.Add("x/y/z:8n+I")
f.Fuzz(func(t *testing.T, s string) { f.Fuzz(func(t *testing.T, s string) {
r0 := ParseName(s) r0 := ParseName(s)
if !r0.Valid() { if !r0.IsValid() {
if !r0.EqualFold(Name{}) { if !r0.EqualFold(Name{}) {
t.Errorf("expected invalid path to be zero value; got %#v", r0) t.Errorf("expected invalid path to be zero value; got %#v", r0)
} }
@ -428,7 +428,7 @@ func TestNameTextMarshal(t *testing.T) {
t.Run("TextMarshal allocs", func(t *testing.T) { t.Run("TextMarshal allocs", func(t *testing.T) {
var data []byte var data []byte
name := ParseName("example.com/ns/mistral:latest+Q4_0") name := ParseName("example.com/ns/mistral:latest+Q4_0")
if !name.Complete() { if !name.IsComplete() {
// sanity check // sanity check
panic("sanity check failed") panic("sanity check failed")
} }
@ -540,7 +540,7 @@ func ExampleName_completeAndResolved() {
"@sha123-1", "@sha123-1",
} { } {
p := ParseName(s) p := ParseName(s)
fmt.Printf("complete:%v resolved:%v digest:%s\n", p.Complete(), p.Resolved(), p.Digest()) fmt.Printf("complete:%v resolved:%v digest:%s\n", p.IsComplete(), p.IsResolved(), p.Digest())
} }
// Output: // Output:

View File

@ -83,7 +83,7 @@ func (s *Server) handlePush(w http.ResponseWriter, r *http.Request) error {
} }
mp := model.ParseName(pr.Name) mp := model.ParseName(pr.Name)
if !mp.Complete() { if !mp.IsComplete() {
return oweb.Invalid("name", pr.Name, "must be complete") return oweb.Invalid("name", pr.Name, "must be complete")
} }