10 // An arbitrary object that can be stringified by fmt.Sprint().
12 // The stringification is filtered to ensure it doesn't contain characters
13 // that are invalid on Windows, which has the most restrictive filesystem.
14 // The "bad" characters (\, /, :, *, ?, ", <, >, |) are replaced with _.
16 // On a list of CacheKeys, the last component is taken to represent a file
17 // and all the other components represent the intermediary directories.
18 // This means that it's not possible to have subkeys of an existing file key.
20 // NOTE: when running on Windows, directories that start with a '.' get the
21 // '.' replaced by a '_'. This is because regular Windows tools can't deal
22 // with directories starting with a dot.
23 type CacheKey interface{}
25 // All "bad characters" that can't go in Windows paths.
26 // It's a superset of the "bad characters" on other OSes, so this works.
27 var badPath = regexp.MustCompile(`[\\/:\*\?\"<>\|]`)
29 func Stringify(stuff ...CacheKey) []string {
30 ret := make([]string, len(stuff))
31 for i := range stuff {
32 s := fmt.Sprint(stuff[i])
33 ret[i] = badPath.ReplaceAllLiteralString(s, "_")
38 // Each key but the last is treated as a directory.
39 // The last key is treated as a regular file.
41 // This also means that cache keys that are file-backed
42 // cannot have subkeys.
43 func (cd *CacheDir) cachePath(key ...CacheKey) string {
44 parts := append([]string{cd.GetCacheDir()}, Stringify(key...)...)
45 p := filepath.Join(filterDots(parts...)...)
49 var invalidPath = []CacheKey{".invalid"}
51 // Returns the time the given key was marked as invalid.
52 // If the key is valid, then calling IsZero() on the returned
53 // time will return true.
54 func (cd *CacheDir) GetInvalid(key ...CacheKey) (ts time.Time) {
55 invKey := append(invalidPath, key...)
57 if stat, err := cd.Stat(invKey...); err == nil {
63 // Checks if the given key is not marked as invalid, or if it is,
64 // checks if it was marked more than maxDuration time ago.
66 // Calls UnsetInvalid if the keys are valid.
67 func (cd *CacheDir) IsValid(maxDuration time.Duration, key ...CacheKey) bool {
68 ts := cd.GetInvalid(key...)
73 case time.Now().Sub(ts) > maxDuration:
74 cd.UnsetInvalid(key...)
81 // Deletes the given key and caches it as invalid.
82 func (cd *CacheDir) SetInvalid(key ...CacheKey) error {
83 invKey := append(invalidPath, key...)
86 return cd.Touch(invKey...)
89 // Removes the given key from the invalid key cache.
90 func (cd *CacheDir) UnsetInvalid(key ...CacheKey) error {
91 invKey := append(invalidPath, key...)
93 return cd.Delete(invKey...)