161 lines
2.7 KiB
Go
161 lines
2.7 KiB
Go
package cache
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"encoding/json"
|
|
"io"
|
|
"os"
|
|
"path/filepath"
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
type Directory struct {
|
|
BaseFolder string
|
|
MaxLife time.Duration
|
|
|
|
lock sync.Mutex
|
|
}
|
|
|
|
var _ Cache = &Directory{}
|
|
|
|
func (d *Directory) GetPath(key string) string {
|
|
return filepath.Join(d.BaseFolder, key+".json")
|
|
}
|
|
|
|
func (d *Directory) Cleanup(_ context.Context) error {
|
|
d.lock.Lock()
|
|
|
|
defer func() {
|
|
d.lock.Unlock()
|
|
}()
|
|
|
|
// go through the BaseFilder looking for any files that are older than MaxLife
|
|
return filepath.Walk(d.BaseFolder, func(path string, info os.FileInfo, err error) error {
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// ignore directories
|
|
if info.IsDir() {
|
|
return nil
|
|
}
|
|
|
|
// only files that end in .json
|
|
if filepath.Ext(path) != ".json" {
|
|
return nil
|
|
}
|
|
|
|
// if the openFile is older than MaxLife, delete it
|
|
if time.Since(info.ModTime()) > d.MaxLife {
|
|
return os.Remove(path)
|
|
}
|
|
|
|
return nil
|
|
})
|
|
}
|
|
|
|
// AutoCleanupRoutine will continually loop and cleanup the directory, until the context is cancelled or an error occurs
|
|
// returns nil on context cancellation, or an error if one occurs during cleanup
|
|
func (d *Directory) AutoCleanupRoutine(ctx context.Context) error {
|
|
for {
|
|
select {
|
|
case <-ctx.Done():
|
|
return nil
|
|
|
|
case <-time.After(d.MaxLife):
|
|
err := d.Cleanup(ctx)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func (d *Directory) openFile(key string) (*os.File, error) {
|
|
path := d.GetPath(key)
|
|
|
|
return os.Open(path)
|
|
}
|
|
|
|
func (d *Directory) Set(key string, value io.Reader) error {
|
|
d.lock.Lock()
|
|
defer d.lock.Unlock()
|
|
|
|
fp, err := d.openFile(key)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
defer func(fp *os.File) {
|
|
_ = fp.Close()
|
|
}(fp)
|
|
|
|
_, err = io.Copy(fp, value)
|
|
|
|
return err
|
|
}
|
|
|
|
func (d *Directory) SetJSON(key string, value any) error {
|
|
d.lock.Lock()
|
|
defer d.lock.Unlock()
|
|
|
|
fp, err := d.openFile(key)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
defer func(fp *os.File) {
|
|
_ = fp.Close()
|
|
}(fp)
|
|
|
|
return json.NewEncoder(fp).Encode(value)
|
|
}
|
|
|
|
func (d *Directory) SetString(key, value string) error {
|
|
return d.Set(key, bytes.NewReader([]byte(value)))
|
|
}
|
|
|
|
func (d *Directory) Get(key string, w io.Writer) error {
|
|
d.lock.Lock()
|
|
defer d.lock.Unlock()
|
|
|
|
fp, err := d.openFile(key)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer fp.Close()
|
|
|
|
_, err = io.Copy(w, fp)
|
|
return err
|
|
}
|
|
|
|
func (d *Directory) GetJSON(key string, v any) error {
|
|
d.lock.Lock()
|
|
defer d.lock.Unlock()
|
|
|
|
fp, err := d.openFile(key)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
defer fp.Close()
|
|
|
|
return json.NewEncoder(fp).Encode(v)
|
|
}
|
|
|
|
func (d *Directory) GetString(key string) (string, error) {
|
|
var buf bytes.Buffer
|
|
|
|
err := d.Get(key, &buf)
|
|
return buf.String(), err
|
|
}
|
|
|
|
func (d *Directory) Delete(key string) error {
|
|
d.lock.Lock()
|
|
defer d.lock.Unlock()
|
|
|
|
return os.Remove(d.GetPath(key))
|
|
}
|