package types import ( "fmt" "path/filepath" "regexp" "strings" ) var ( idRegex = regexp.MustCompile(`(.*?)(@(.*))?:(.*)`) ) // ID an id of a file consists of at least a Bucket and a Key. Version is optional. // Can also be used as an ID for a directory. When the key ends with "/" it is treated as dir. type ID struct { Bucket string `json:"bucket"` // Name of the bucket Key string `json:"key"` // Key of the object Version string `json:"version"` // Version of the object. For now we ignore it } // String Return String representation of an ID // Looks like this: "bucketName@version:/id/of/obj" or "bucketName:/id/of/obj" func (i ID) String() string { if i.Version == "" { return fmt.Sprintf("%s:%s", i.Bucket, i.Key) } else { return fmt.Sprintf("%s@%s:%s", i.Bucket, i.Version, i.Key) } } // Normalize normalzes the key to have a "/" prefix func (i *ID) Normalize() { if i.Key == "." { i.Key = "/" } else if !strings.HasPrefix(i.Key, "/") { i.Key = "/" + i.Key } } // Valid checks if bucket and key is not empty func (i *ID) Valid() bool { return i.Bucket != "" && i.Key != "" } func (i *ID) IsDirectory() bool { return strings.HasSuffix(i.Key, "/") } // Raw for the Key interface for the dataloaders so ID can be used as a dataloader key func (i ID) Raw() interface{} { return i } // Parent returns the parent dir ID. func (i ID) Parent() *ID { if i.Key == "/" { // Already at root. We dont have a parent return nil } parent := &ID{ Bucket: i.Bucket, Key: filepath.Dir(i.Key), } parent.Normalize() return parent } // ParseID parses a string to an ID. Null if invalid func ParseID(id string) *ID { match := idRegex.FindStringSubmatch(id) if match == nil { return nil } rtn := &ID{ Bucket: match[1], Version: match[3], Key: match[4], } if !rtn.Valid() { return nil } rtn.Normalize() return rtn }