blob: 4e7f2dbb8bbecc5fa27ce8cbac6b0f3f753dbfd8 [file] [log] [blame]
// named is a utility for dealing with named fiddles.
package named
import (
"errors"
"fmt"
"path/filepath"
"regexp"
"strings"
"go.skia.org/infra/fiddlek/go/store"
"go.skia.org/infra/go/sklog"
)
var (
DuplicateNameErr = errors.New("Name already exists.")
// fiddleHashRe is used to validate fiddle hashes.
fiddleHashRe = regexp.MustCompile("^[0-9a-zA-Z]{32}$")
// fiddlNameRe is used to validate fiddle names.
fiddleNameRe = regexp.MustCompile("^[0-9a-zA-Z_]+$")
// trailingToMedia maps the end of each image URL to the store.Media type
// that it corresponds to.
trailingToMedia = map[string]store.Media{
"_raster.png": store.CPU,
"_gpu.png": store.GPU,
".pdf": store.PDF,
".skp": store.SKP,
".txt": store.TXT,
"_cpu.webm": store.ANIM_CPU,
"_gpu.webm": store.ANIM_GPU,
"_glinfo.txt": store.GLINFO,
}
)
// NameStore is an interface that store.Store conforms to that is just the
// methods that Named uses.
type NameStore interface {
GetHashFromName(name string) (string, error)
WriteName(name, hash, user, status string) error
}
// Named deals with creating and dereferencing named fiddles.
type Named struct {
st NameStore
}
// New creates a new Named.
func New(st NameStore) *Named {
return &Named{
st: st,
}
}
// Add a named fiddle.
//
// name - The name of the fidde, w/o the @ prefix.
// hash - The fiddle hash.
// user - The email of the user that created the name.
// overwrite - True if the write should proceed if the name already exists.
func (n *Named) Add(name, hash, user string, overwrite bool) error {
if !fiddleNameRe.MatchString(name) {
return fmt.Errorf("Not a valid fiddle name %q", name)
}
if !fiddleHashRe.MatchString(hash) {
return fmt.Errorf("Not a valid fiddle hash %q", hash)
}
oldHash, err := n.DereferenceID("@" + name)
if err == nil {
// This name exists already.
if !overwrite {
return DuplicateNameErr
}
if oldHash == hash {
// Don't bother writing if the hash is already correct.
return nil
}
sklog.Infof("Named Fiddle Changed: %s %s -> %s by %s", name, oldHash, hash, user)
} else {
sklog.Infof("Named Fiddle Created: %s %s by %s", name, hash, user)
}
if err := n.st.WriteName(name, hash, user, ""); err != nil {
return fmt.Errorf("Failed to write name: %s", err)
}
return nil
}
// Dereference converts the id to a fiddlehash, where id could
// be either a fiddle name or a fiddle hash. Fiddle names are
// presumed to be prefixed with "@".
//
// Returns an error if the fiddle hash is not valid looking.
func (n *Named) DereferenceID(id string) (string, error) {
if len(id) < 2 {
return "", fmt.Errorf("Invalid ID")
}
if id[0] == '@' {
id = id[1:]
// Look up the name in gs://skia-fiddle/named/<id>.
var err error
fiddleHash, err := n.st.GetHashFromName(id)
if err != nil {
return "", fmt.Errorf("Unknown name: %s", err)
}
if !fiddleHashRe.MatchString(fiddleHash) {
return "", fmt.Errorf("Not a valid fiddle hash found in named file %q: %s", id, fiddleHash)
}
return fiddleHash, nil
} else {
// match against regex?
if !fiddleHashRe.MatchString(id) {
return "", fmt.Errorf("Not a valid fiddle hash: %q", id)
}
return id, nil
}
}
// DereferenceImageID converts the id of an image to the fiddle hash and the
// Media it represents, handling both original fiddle hashes and fiddle names
// as part of the image id. Fiddle names are
// presumed to be prefixed with "@".
//
// I.e. it converts "cbb8dee39e9f1576cd97c2d504db8eee_gpu.png" to
// "cbb8dee39e9f1576cd97c2d504db8eee" and store.GPU. and, could also convert
// "@star_raster.png" to "cbb8dee39e9f1576cd97c2d504db8eee" and store.CPU.
func (n *Named) DereferenceImageID(id string) (string, store.Media, error) {
// First strip off the "_raster.png" or ".pdf" from the end.
trailing := filepath.Ext(id)
if len(trailing) < 4 {
return "", store.UNKNOWN, fmt.Errorf("Not a valid image id: %q", id)
}
id = id[:len(id)-len(trailing)]
// If this is a .png or .webm then we need to strip off the trailing "_raster" or "_gpu".
if strings.HasSuffix(id, "_raster") || strings.HasSuffix(id, "_gpu") || strings.HasSuffix(id, "_cpu") || strings.HasSuffix(id, "_glinfo") {
parts := strings.Split(id, "_")
if len(parts) < 2 {
return "", store.UNKNOWN, fmt.Errorf("Not a valid image id form: %q", id)
}
trailing = "_" + parts[len(parts)-1] + trailing
id = strings.Join(parts[:len(parts)-1], "_")
}
media, ok := trailingToMedia[trailing]
if !ok {
return "", store.UNKNOWN, fmt.Errorf("Unknown media: %q", trailing)
}
// We are left with just the name or fiddle hash, dereference that.
fiddleHash, err := n.DereferenceID(id)
return fiddleHash, media, err
}