]> git.lizzy.rs Git - go-fscache.git/blob - cachekey.go
7d059dafd6af6e7021eba048c606ed80c42e80d7
[go-fscache.git] / cachekey.go
1 package fscache
2
3 import (
4         "fmt"
5         "path/filepath"
6         "regexp"
7         "time"
8 )
9
10 // An arbitrary object that can be stringified by fmt.Sprint().
11 //
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 _.
15 //
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.
19 //
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{}
24
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(`[\\/:\*\?\"<>\|]`)
28
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, "_")
34         }
35         return ret
36 }
37
38 // Each key but the last is treated as a directory.
39 // The last key is treated as a regular file.
40 //
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...)...)
46         return p
47 }
48
49 var invalidPath = []CacheKey{".invalid"}
50
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...)
56
57         stat, _ := cd.Stat(invKey...)
58         return stat.ModTime()
59 }
60
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.
63 //
64 // Calls UnsetInvalid if the keys are valid.
65 func (cd *CacheDir) IsValid(maxDuration time.Duration, key ...CacheKey) bool {
66         ts := cd.GetInvalid(key...)
67
68         switch {
69         case ts.IsZero():
70                 return true
71         case time.Now().Sub(ts) > maxDuration:
72                 cd.UnsetInvalid(key...)
73                 return true
74         default:
75                 return false
76         }
77 }
78
79 // Deletes the given key and caches it as invalid.
80 func (cd *CacheDir) SetInvalid(key ...CacheKey) error {
81         invKey := append(invalidPath, key...)
82
83         cd.Delete(key...)
84         return cd.Touch(invKey...)
85 }
86
87 // Removes the given key from the invalid key cache.
88 func (cd *CacheDir) UnsetInvalid(key ...CacheKey) error {
89         invKey := append(invalidPath, key...)
90
91         return cd.Delete(invKey...)
92 }