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 stat, _ := cd.Stat(invKey...)
61 // Checks if the given key is not marked as invalid, or if it is,
62 // checks if it was marked more than maxDuration time ago.
64 // Calls UnsetInvalid if the keys are valid.
65 func (cd *CacheDir) IsValid(maxDuration time.Duration, key ...CacheKey) bool {
66 ts := cd.GetInvalid(key...)
71 case time.Now().Sub(ts) > maxDuration:
72 cd.UnsetInvalid(key...)
79 // Deletes the given key and caches it as invalid.
80 func (cd *CacheDir) SetInvalid(key ...CacheKey) error {
81 invKey := append(invalidPath, key...)
84 return cd.Touch(invKey...)
87 // Removes the given key from the invalid key cache.
88 func (cd *CacheDir) UnsetInvalid(key ...CacheKey) error {
89 invKey := append(invalidPath, key...)
91 return cd.Delete(invKey...)