From 51f9e71c4e79d89b3063552de8a87455ccd83bdc Mon Sep 17 00:00:00 2001 From: Mark Bailey Date: Sun, 31 Aug 2025 07:09:43 -0400 Subject: [PATCH] refactor(route cache): clean and improve, add route -> name conversion and use Storer to persist route cache --- internal/route_cache.go | 71 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 66 insertions(+), 5 deletions(-) diff --git a/internal/route_cache.go b/internal/route_cache.go index cf31f87..9524140 100644 --- a/internal/route_cache.go +++ b/internal/route_cache.go @@ -1,10 +1,17 @@ package internal import ( + "context" + "fmt" "log" + "net/http" + "regexp" + "strings" "sync" + "time" - "git.markbailey.dev/cerbervs/joist/internal/errors" + ee "git.markbailey.dev/cerbervs/joist/internal/errors" + "github.com/go-chi/chi/v5" ) const suff = "route_cache" @@ -16,6 +23,7 @@ type RouteCacher interface { } type routeCache struct { + rx chi.Router rs map[string]string s Storer } @@ -26,7 +34,7 @@ var ( c *routeCache ) -func NewRouteCache(store Storer) (*routeCache, error) { +func NewRouteCache(store Storer, router chi.Router) (*routeCache, error) { rcOnce.Do(func() { if store == nil { st, err := NewBadgerStore(WithSubDir("route_cache")) @@ -39,13 +47,14 @@ func NewRouteCache(store Storer) (*routeCache, error) { } c = &routeCache{ + rx: router, rs: make(map[string]string), s: store, } }) if c == nil { - return nil, errors.ErrCacheUninitialized + return nil, ee.ErrCacheUninitialized } rcLock.Lock() @@ -56,7 +65,7 @@ func NewRouteCache(store Storer) (*routeCache, error) { func (r *routeCache) All() (map[string]string, error) { if r.rs == nil { - return nil, errors.ErrCacheUninitialized + return nil, ee.ErrCacheUninitialized } return r.rs, nil } @@ -70,5 +79,57 @@ func (r *routeCache) GetPath(name string) (string, error) { return path, nil } - return string(""), errors.NewErrRouteNotFound(string(name)) + return string(""), ee.NewErrRouteNotFound(string(name)) +} + +func (r *routeCache) UnmarshallFromStore() error { + if err := chi.Walk(r.rx, walker(r.s)); err != nil { + return err + } + + return nil +} + +func walker(store Storer) chi.WalkFunc { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + return func(method string, route string, handler http.Handler, middlewares ...func(http.Handler) http.Handler) error { + routeName, err := convertRouteToRouteName(route) + if err != nil { + return fmt.Errorf(`failed to name route "%s"`, route) + } + + if routeName[len(routeName)-2:] != ".*" { + route = regexp.MustCompile(`:.*}`).ReplaceAllString(route, "}") + if len(route) > 1 { + route = strings.TrimRight(route, "/") + } + store.Put(ctx, routeName, []byte(route), 1*time.Hour) + } + + return nil + } +} + +func convertRouteToRouteName(route string) (string, error) { + name := "app" + if route == "/" { + return name + ".home", nil + } + + route = strings.ReplaceAll(route, "/", ".") + route = strings.ReplaceAll(route, "-", "_") + + paramRegex := regexp.MustCompile(`\.{[^}]*}`) + params := paramRegex.FindAllString(route, -1) + route = paramRegex.ReplaceAllString(route, "") + + if len(params) > 0 { + route += ".params" + } + + name += strings.TrimRight(route, ".") + + return name, nil }