Skip to content

Commit

Permalink
Merge pull request #992 from rsteube/generic-cache
Browse files Browse the repository at this point in the history
generic cache
  • Loading branch information
rsteube committed Feb 17, 2024
2 parents 91af433 + 1dbe37c commit 53f4f74
Show file tree
Hide file tree
Showing 5 changed files with 116 additions and 55 deletions.
8 changes: 4 additions & 4 deletions action.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import (
shlex "github.com/rsteube/carapace-shlex"
"github.com/rsteube/carapace/internal/cache"
"github.com/rsteube/carapace/internal/common"
pkgcache "github.com/rsteube/carapace/pkg/cache"
"github.com/rsteube/carapace/pkg/cache/key"
"github.com/rsteube/carapace/pkg/match"
"github.com/rsteube/carapace/pkg/style"
pkgtraverse "github.com/rsteube/carapace/pkg/traverse"
Expand All @@ -31,7 +31,7 @@ type ActionMap map[string]Action
type CompletionCallback func(c Context) Action

// Cache cashes values of a CompletionCallback for given duration and keys.
func (a Action) Cache(timeout time.Duration, keys ...pkgcache.Key) Action {
func (a Action) Cache(timeout time.Duration, keys ...key.Key) Action {
if a.callback != nil { // only relevant for callback actions
cachedCallback := a.callback
_, file, line, _ := runtime.Caller(1) // generate uid from wherever Cache() was called
Expand All @@ -41,14 +41,14 @@ func (a Action) Cache(timeout time.Duration, keys ...pkgcache.Key) Action {
return cachedCallback(c)
}

if cached, err := cache.Load(cacheFile, timeout); err == nil {
if cached, err := cache.LoadE(cacheFile, timeout); err == nil {
return Action{meta: cached.Meta, rawValues: cached.Values}
}

invokedAction := (Action{callback: cachedCallback}).Invoke(c)
if invokedAction.action.meta.Messages.IsEmpty() {
if cacheFile, err := cache.File(file, line, keys...); err == nil { // regenerate as cache keys might have changed due to invocation
_ = cache.Write(cacheFile, invokedAction.export())
_ = cache.WriteE(cacheFile, invokedAction.export())
}
}
return invokedAction.ToA()
Expand Down
4 changes: 2 additions & 2 deletions example/cmd/modifier.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import (
"time"

"github.com/rsteube/carapace"
"github.com/rsteube/carapace/pkg/cache"
"github.com/rsteube/carapace/pkg/cache/key"
"github.com/rsteube/carapace/pkg/condition"
"github.com/rsteube/carapace/pkg/style"
"github.com/rsteube/carapace/pkg/traverse"
Expand Down Expand Up @@ -85,7 +85,7 @@ func init() {
return carapace.ActionValues(
time.Now().Format("15:04:05"),
)
}).Cache(10*time.Second, cache.String(c.Parts[0]))
}).Cache(10*time.Second, key.String(c.Parts[0]))
default:
return carapace.ActionValues()
}
Expand Down
38 changes: 24 additions & 14 deletions internal/cache/cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,31 +14,41 @@ import (
"github.com/rsteube/carapace/internal/env"
"github.com/rsteube/carapace/internal/export"
"github.com/rsteube/carapace/internal/uid"
"github.com/rsteube/carapace/pkg/cache"
"github.com/rsteube/carapace/pkg/cache/key"
"github.com/rsteube/carapace/pkg/xdg"
)

// Write persistests given values to file as json.
func Write(file string, e export.Export) (err error) {
func WriteE(file string, e export.Export) (err error) {
var m []byte
if m, err = json.Marshal(e); err == nil {
err = os.WriteFile(file, m, 0600)
err = Write(file, m)
}
return
}

// Load loads values from file unless modification date exceeds timeout.
func Load(file string, timeout time.Duration) (e export.Export, err error) {
func Write(file string, content []byte) (err error) {
return os.WriteFile(file, content, 0600)
}

func LoadE(file string, timeout time.Duration) (*export.Export, error) { // TODO reference
content, err := Load(file, timeout)
if err != nil {
return nil, err
}

var e export.Export
if err := json.Unmarshal(content, &e); err != nil {
return nil, err
}
return &e, nil
}

func Load(file string, timeout time.Duration) (b []byte, err error) {
var stat os.FileInfo
if stat, err = os.Stat(file); os.IsNotExist(err) || (timeout >= 0 && stat.ModTime().Add(timeout).Before(time.Now())) {
err = errors.New("not exists or timeout exceeded")
} else {
var content []byte
if content, err = os.ReadFile(file); err == nil {
err = json.Unmarshal(content, &e)
}
return nil, errors.New("not exists or timeout exceeded")
}
return
return os.ReadFile(file)
}

// CacheDir creates a cache folder for current user and returns the path.
Expand All @@ -60,7 +70,7 @@ func CacheDir(name string) (dir string, err error) {

// File returns the cache filename for given values
// TODO cleanup
func File(callerFile string, callerLine int, keys ...cache.Key) (file string, err error) {
func File(callerFile string, callerLine int, keys ...key.Key) (file string, err error) {
uid := uidKeys(callerFile, strconv.Itoa(callerLine))
ids := make([]string, 0)
for _, key := range keys {
Expand Down
54 changes: 19 additions & 35 deletions pkg/cache/cache.go
Original file line number Diff line number Diff line change
@@ -1,46 +1,30 @@
// Package cache provides cache keys
package cache

import (
"crypto/sha1"
"fmt"
"os"
"path/filepath"
"strconv"
"strings"
)

// Key provides a cache key.
type Key func() (string, error)
"runtime"
"time"

// String creates a CacheKey for given strings.
func String(s ...string) Key {
return func() (string, error) {
return strings.Join(s, "\n"), nil
}
}
"github.com/rsteube/carapace/internal/cache"
"github.com/rsteube/carapace/pkg/cache/key"
)

// FileChecksum creates a CacheKey for given file.
func FileChecksum(file string) Key {
return func() (checksum string, err error) {
var content []byte
if content, err = os.ReadFile(file); err == nil {
checksum = fmt.Sprintf("%x", sha1.Sum(content))
// Cache caches a function for given duration and keys.
func Cache(timeout time.Duration, keys ...key.Key) func(f func() ([]byte, error)) ([]byte, error) {
return func(f func() ([]byte, error)) ([]byte, error) {
_, file, line, _ := runtime.Caller(1)
cacheFile, err := cache.File(file, line, keys...)
if err != nil {
return nil, err
}
return
}
}

// FileStats creates a CacheKey for given file.
func FileStats(file string) Key {
return func() (checksum string, err error) {
var path string
if path, err = filepath.Abs(file); err == nil {
var info os.FileInfo
if info, err = os.Stat(file); err == nil {
return String(path, strconv.FormatInt(info.Size(), 10), info.ModTime().String())()
content, err := cache.Load(cacheFile, timeout)
if err != nil {
content, err = f()
if err != nil {
return nil, err
}
return content, cache.Write(cacheFile, content)
}
return
return content, nil
}
}
67 changes: 67 additions & 0 deletions pkg/cache/key/cache.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
// Package cache provides cache keys
package key

import (
"crypto/sha1"
"fmt"
"io/fs"
"os"
"path/filepath"
"strconv"
"strings"
)

// Key provides a cache key.
type Key func() (string, error)

// String creates a CacheKey for given strings.
func String(s ...string) Key {
return func() (string, error) {
return strings.Join(s, "\n"), nil
}
}

// FileChecksum creates a CacheKey for given file.
func FileChecksum(file string) Key {
return func() (checksum string, err error) {
var content []byte
if content, err = os.ReadFile(file); err == nil {
checksum = fmt.Sprintf("%x", sha1.Sum(content))
}
return
}
}

// FileStats creates a CacheKey for given file.
func FileStats(file string) Key {
return func() (checksum string, err error) {
var path string
if path, err = filepath.Abs(file); err == nil {
var info os.FileInfo
if info, err = os.Stat(file); err == nil {
return String(path, strconv.FormatInt(info.Size(), 10), info.ModTime().String())()
}
}
return
}
}

func FolderStats(folder string) Key {
return func() (string, error) {
sums := make([]string, 0)
err := filepath.Walk(folder, func(path string, info fs.FileInfo, err error) error {
if !info.IsDir() {
sum, err := String(info.Name(), strconv.FormatInt(info.Size(), 10), info.ModTime().String())()
if err != nil {
return err
}
sums = append(sums, sum)
}
return nil
})
if err != nil {
return "", err
}
return strings.Join(sums, "\n"), nil
}
}

0 comments on commit 53f4f74

Please sign in to comment.