151 lines
3.0 KiB
Go
151 lines
3.0 KiB
Go
package joist
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"log"
|
|
"sync"
|
|
"time"
|
|
|
|
"git.markbailey.dev/cerbervs/joist/internal/badger"
|
|
jerr "git.markbailey.dev/cerbervs/joist/internal/errors"
|
|
)
|
|
|
|
type routeCache struct {
|
|
rs map[string]string
|
|
ris map[string]*RouteInfo
|
|
s Storer
|
|
ttl time.Duration
|
|
}
|
|
|
|
type RouteCacheOpt func(*routeCache)
|
|
|
|
var (
|
|
rcOnce sync.Once
|
|
cache *routeCache
|
|
)
|
|
|
|
func WithTTL(ttl time.Duration) RouteCacheOpt {
|
|
return func(r *routeCache) {
|
|
r.ttl = ttl
|
|
}
|
|
}
|
|
|
|
func WithStore(store Storer) RouteCacheOpt {
|
|
return func(r *routeCache) {
|
|
r.s = store
|
|
}
|
|
}
|
|
|
|
func NewRouteCacheService(opts ...RouteCacheOpt) (*routeCache, error) {
|
|
rcOnce.Do(func() {
|
|
c, err := newRouteCache(opts...)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
cache = c
|
|
})
|
|
|
|
if cache == nil {
|
|
return nil, jerr.ErrCacheUninitialized
|
|
}
|
|
|
|
return cache, nil
|
|
}
|
|
|
|
func (r *routeCache) All() (map[string]string, error) {
|
|
if r.rs == nil {
|
|
return nil, jerr.ErrCacheUninitialized
|
|
}
|
|
return r.rs, nil
|
|
}
|
|
|
|
func (r *routeCache) Add(name string, path string) error {
|
|
ttl := r.ttl
|
|
if ttl == 0 {
|
|
ttl = 1 * time.Hour
|
|
}
|
|
|
|
if err := r.s.Put(context.Background(), name, []byte(path), ttl); err != nil {
|
|
return err
|
|
}
|
|
|
|
r.rs[name] = path
|
|
|
|
return nil
|
|
}
|
|
|
|
func (r *routeCache) GetPath(name string) (string, error) {
|
|
if path, ok := r.rs[name]; ok {
|
|
return path, nil
|
|
}
|
|
|
|
if path, err := r.s.Get(context.Background(), name); err == nil {
|
|
return string(path), nil
|
|
}
|
|
|
|
return "", errors.New("route not found in cache")
|
|
}
|
|
|
|
func (r *routeCache) AddRouteInfo(routeInfo RouteInfo) error {
|
|
ttl := r.ttl
|
|
if ttl == 0 {
|
|
ttl = 1 * time.Hour
|
|
}
|
|
|
|
if err := r.s.Put(context.Background(), routeInfo.Name+":method", []byte(routeInfo.Method), ttl); err != nil {
|
|
return err
|
|
}
|
|
if err := r.s.Put(context.Background(), routeInfo.Name+":path", []byte(routeInfo.Path), ttl); err != nil {
|
|
return err
|
|
}
|
|
if err := r.s.Put(context.Background(), routeInfo.Name+":description", []byte(routeInfo.Description), ttl); err != nil {
|
|
return err
|
|
}
|
|
if err := r.s.Put(context.Background(), routeInfo.Name+":title", []byte(routeInfo.Title), ttl); err != nil {
|
|
return err
|
|
}
|
|
|
|
r.rs[routeInfo.Name] = routeInfo.Path
|
|
r.ris[routeInfo.Name] = &routeInfo
|
|
|
|
return nil
|
|
}
|
|
|
|
func (r *routeCache) GetRouteInfo(name string) (RouteInfo, error) {
|
|
if ri, ok := r.ris[name]; ok {
|
|
return *ri, nil
|
|
}
|
|
|
|
if info, err := r.s.GetForPrefix(context.Background(), name); err == nil {
|
|
return RouteInfo{
|
|
Method: string(info[":method"]),
|
|
Name: name,
|
|
Path: string(info[":path"]),
|
|
Description: string(info[":description"]),
|
|
Title: string(info[":title"]),
|
|
}, nil
|
|
}
|
|
|
|
return RouteInfo{}, errors.New("route info not found in cache")
|
|
}
|
|
|
|
func newRouteCache(opts ...RouteCacheOpt) (*routeCache, error) {
|
|
s, err := badger.NewBadgerStore(badger.WithSubDir("route_cache"))
|
|
if err != nil {
|
|
return nil, jerr.Wrap(jerr.ErrFailedToCreateRouteCache, err)
|
|
}
|
|
|
|
cache := &routeCache{
|
|
rs: make(map[string]string),
|
|
s: s,
|
|
}
|
|
|
|
for _, opt := range opts {
|
|
opt(cache)
|
|
}
|
|
|
|
return cache, nil
|
|
}
|