Modify `FunctionCall` struct to handle arguments as strings. Add debugging logs to facilitate error tracing and improve JSON unmarshalling in various functions.
181 lines
3.1 KiB
Go
181 lines
3.1 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) openFileForWriting(key string) (*os.File, error) {
|
|
path := d.GetPath(key)
|
|
|
|
fp, err := os.Create(path)
|
|
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return fp, nil
|
|
}
|
|
|
|
func (d *Directory) openFile(key string) (*os.File, error) {
|
|
path := d.GetPath(key)
|
|
|
|
res, err := os.Open(path)
|
|
|
|
if err != nil {
|
|
if os.IsNotExist(err) {
|
|
return nil, ErrNotFound
|
|
}
|
|
return nil, err
|
|
}
|
|
|
|
return res, nil
|
|
}
|
|
|
|
func (d *Directory) Set(key string, value io.Reader) error {
|
|
d.lock.Lock()
|
|
defer d.lock.Unlock()
|
|
|
|
fp, err := d.openFileForWriting(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.openFileForWriting(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))
|
|
}
|