change version, add logical route groups

This commit is contained in:
Sky Johnson 2025-05-28 22:40:34 -05:00
parent 39d14d0025
commit 163e94d576
2 changed files with 79 additions and 39 deletions

View File

@ -51,7 +51,7 @@ type LuaRouter struct {
bytecodeCache *fastcache.Cache // Cache for compiled bytecode bytecodeCache *fastcache.Cache // Cache for compiled bytecode
// Middleware tracking for path hierarchy // Middleware tracking for path hierarchy
middlewareFiles map[string][]string // path -> middleware file paths middlewareFiles map[string][]string // filesystem path -> middleware file paths
// New caching fields // New caching fields
middlewareCache map[string][]byte // path -> content middlewareCache map[string][]byte // path -> content
@ -72,6 +72,40 @@ type node struct {
paramChild *node // Parameter/wildcard child paramChild *node // Parameter/wildcard child
err error // Compilation error if any err error // Compilation error if any
modTime time.Time // Last modification time modTime time.Time // Last modification time
fsPath string // Original filesystem path (includes groups)
}
// pathInfo holds both filesystem and URL path information
type pathInfo struct {
fsPath string // Filesystem path (includes groups)
urlPath string // URL path (excludes groups)
}
// parsePathWithGroups separates filesystem path from URL path
func parsePathWithGroups(fsPath string) pathInfo {
segments := strings.Split(strings.Trim(fsPath, "/"), "/")
var urlSegments []string
for _, segment := range segments {
if segment == "" {
continue
}
// Skip group segments for URL path
if len(segment) >= 3 && segment[0] == '(' && segment[len(segment)-1] == ')' {
continue
}
urlSegments = append(urlSegments, segment)
}
urlPath := "/"
if len(urlSegments) > 0 {
urlPath = "/" + strings.Join(urlSegments, "/")
}
return pathInfo{
fsPath: fsPath,
urlPath: urlPath,
}
} }
// NewLuaRouter creates a new LuaRouter instance // NewLuaRouter creates a new LuaRouter instance
@ -136,12 +170,13 @@ func (r *LuaRouter) buildRoutes() error {
return err return err
} }
urlPath := "/" fsPath := "/"
if relDir != "." { if relDir != "." {
urlPath = "/" + strings.ReplaceAll(relDir, "\\", "/") fsPath = "/" + strings.ReplaceAll(relDir, "\\", "/")
} }
r.middlewareFiles[urlPath] = append(r.middlewareFiles[urlPath], path) // Use filesystem path for middleware (includes groups)
r.middlewareFiles[fsPath] = append(r.middlewareFiles[fsPath], path)
} }
return nil return nil
@ -169,19 +204,22 @@ func (r *LuaRouter) buildRoutes() error {
return err return err
} }
urlPath := "/" fsPath := "/"
if relDir != "." { if relDir != "." {
urlPath = "/" + strings.ReplaceAll(relDir, "\\", "/") fsPath = "/" + strings.ReplaceAll(relDir, "\\", "/")
} }
pathInfo := parsePathWithGroups(fsPath)
// Handle index.lua files // Handle index.lua files
if fileName == "index" { if fileName == "index" {
for _, method := range []string{"GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS", "HEAD"} { for _, method := range []string{"GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS", "HEAD"} {
root := r.routes[method] root := r.routes[method]
node := r.findOrCreateNode(root, urlPath) node := r.findOrCreateNode(root, pathInfo.urlPath)
node.indexFile = path node.indexFile = path
node.modTime = info.ModTime() node.modTime = info.ModTime()
r.compileWithMiddleware(node, urlPath, path) node.fsPath = pathInfo.fsPath
r.compileWithMiddleware(node, pathInfo.fsPath, path)
} }
return nil return nil
} }
@ -193,14 +231,14 @@ func (r *LuaRouter) buildRoutes() error {
return nil return nil
} }
r.addRoute(root, urlPath, path, info.ModTime()) r.addRoute(root, pathInfo, path, info.ModTime())
return nil return nil
}) })
} }
// addRoute adds a route to the routing tree // addRoute adds a route to the routing tree
func (r *LuaRouter) addRoute(root *node, urlPath, handlerPath string, modTime time.Time) error { func (r *LuaRouter) addRoute(root *node, pathInfo pathInfo, handlerPath string, modTime time.Time) error {
segments := strings.Split(strings.Trim(urlPath, "/"), "/") segments := strings.Split(strings.Trim(pathInfo.urlPath, "/"), "/")
current := root current := root
for _, segment := range segments { for _, segment := range segments {
@ -230,18 +268,19 @@ func (r *LuaRouter) addRoute(root *node, urlPath, handlerPath string, modTime ti
current.handler = handlerPath current.handler = handlerPath
current.modTime = modTime current.modTime = modTime
current.fsPath = pathInfo.fsPath
return r.compileWithMiddleware(current, urlPath, handlerPath) return r.compileWithMiddleware(current, pathInfo.fsPath, handlerPath)
} }
// compileWithMiddleware combines middleware and handler source, then compiles // compileWithMiddleware combines middleware and handler source, then compiles
func (r *LuaRouter) compileWithMiddleware(n *node, urlPath, scriptPath string) error { func (r *LuaRouter) compileWithMiddleware(n *node, fsPath, scriptPath string) error {
if scriptPath == "" { if scriptPath == "" {
return nil return nil
} }
// Check if we need to recompile by comparing modification times // Check if we need to recompile by comparing modification times
sourceKey := r.getSourceCacheKey(urlPath, scriptPath) sourceKey := r.getSourceCacheKey(fsPath, scriptPath)
needsRecompile := false needsRecompile := false
// Check handler modification time // Check handler modification time
@ -258,7 +297,7 @@ func (r *LuaRouter) compileWithMiddleware(n *node, urlPath, scriptPath string) e
// Check middleware modification times // Check middleware modification times
if !needsRecompile { if !needsRecompile {
middlewareChain := r.getMiddlewareChain(urlPath) middlewareChain := r.getMiddlewareChain(fsPath)
for _, mwPath := range middlewareChain { for _, mwPath := range middlewareChain {
mwInfo, err := os.Stat(mwPath) mwInfo, err := os.Stat(mwPath)
if err != nil { if err != nil {
@ -282,7 +321,7 @@ func (r *LuaRouter) compileWithMiddleware(n *node, urlPath, scriptPath string) e
} }
// Build combined source // Build combined source
combinedSource, err := r.buildCombinedSource(urlPath, scriptPath) combinedSource, err := r.buildCombinedSource(fsPath, scriptPath)
if err != nil { if err != nil {
n.err = err n.err = err
return err return err
@ -309,11 +348,11 @@ func (r *LuaRouter) compileWithMiddleware(n *node, urlPath, scriptPath string) e
} }
// buildCombinedSource builds the combined middleware + handler source // buildCombinedSource builds the combined middleware + handler source
func (r *LuaRouter) buildCombinedSource(urlPath, scriptPath string) (string, error) { func (r *LuaRouter) buildCombinedSource(fsPath, scriptPath string) (string, error) {
var combinedSource strings.Builder var combinedSource strings.Builder
// Get middleware chain // Get middleware chain using filesystem path
middlewareChain := r.getMiddlewareChain(urlPath) middlewareChain := r.getMiddlewareChain(fsPath)
// Add middleware in order // Add middleware in order
for _, mwPath := range middlewareChain { for _, mwPath := range middlewareChain {
@ -368,20 +407,20 @@ func (r *LuaRouter) getFileContent(path string) ([]byte, error) {
} }
// getSourceCacheKey generates a unique key for combined source // getSourceCacheKey generates a unique key for combined source
func (r *LuaRouter) getSourceCacheKey(urlPath, scriptPath string) string { func (r *LuaRouter) getSourceCacheKey(fsPath, scriptPath string) string {
middlewareChain := r.getMiddlewareChain(urlPath) middlewareChain := r.getMiddlewareChain(fsPath)
var keyParts []string var keyParts []string
keyParts = append(keyParts, middlewareChain...) keyParts = append(keyParts, middlewareChain...)
keyParts = append(keyParts, scriptPath) keyParts = append(keyParts, scriptPath)
return strings.Join(keyParts, "|") return strings.Join(keyParts, "|")
} }
// getMiddlewareChain returns middleware files that apply to the given path // getMiddlewareChain returns middleware files that apply to the given filesystem path
func (r *LuaRouter) getMiddlewareChain(urlPath string) []string { func (r *LuaRouter) getMiddlewareChain(fsPath string) []string {
var chain []string var chain []string
// Collect middleware from root to specific path // Collect middleware from root to specific path using filesystem path (includes groups)
pathParts := strings.Split(strings.Trim(urlPath, "/"), "/") pathParts := strings.Split(strings.Trim(fsPath, "/"), "/")
if pathParts[0] == "" { if pathParts[0] == "" {
pathParts = []string{} pathParts = []string{}
} }
@ -391,7 +430,7 @@ func (r *LuaRouter) getMiddlewareChain(urlPath string) []string {
chain = append(chain, mw...) chain = append(chain, mw...)
} }
// Add middleware from each path level // Add middleware from each path level (including groups)
currentPath := "" currentPath := ""
for _, part := range pathParts { for _, part := range pathParts {
currentPath += "/" + part currentPath += "/" + part
@ -403,7 +442,7 @@ func (r *LuaRouter) getMiddlewareChain(urlPath string) []string {
return chain return chain
} }
// findOrCreateNode finds or creates a node at the given path // findOrCreateNode finds or creates a node at the given URL path (excludes groups)
func (r *LuaRouter) findOrCreateNode(root *node, urlPath string) *node { func (r *LuaRouter) findOrCreateNode(root *node, urlPath string) *node {
segments := strings.Split(strings.Trim(urlPath, "/"), "/") segments := strings.Split(strings.Trim(urlPath, "/"), "/")
current := root current := root
@ -467,7 +506,7 @@ func getBytecodeKey(handlerPath string) []byte {
return uint64ToBytes(key) return uint64ToBytes(key)
} }
// Match finds a handler for the given method and path // Match finds a handler for the given method and path (URL path, excludes groups)
func (r *LuaRouter) Match(method, path string, params *Params) (*node, bool) { func (r *LuaRouter) Match(method, path string, params *Params) (*node, bool) {
params.Count = 0 params.Count = 0
@ -565,8 +604,12 @@ func (r *LuaRouter) GetRouteInfo(method, path string, params *Params) ([]byte, s
scriptPath = n.indexFile scriptPath = n.indexFile
} }
urlPath := r.getNodeURLPath(n) fsPath := n.fsPath
if err := r.compileWithMiddleware(n, urlPath, scriptPath); err != nil { if fsPath == "" {
fsPath = "/"
}
if err := r.compileWithMiddleware(n, fsPath, scriptPath); err != nil {
return nil, handlerPath, n.err, true return nil, handlerPath, n.err, true
} }
@ -602,8 +645,11 @@ func (r *LuaRouter) GetRouteInfo(method, path string, params *Params) ([]byte, s
bytecode := r.bytecodeCache.Get(nil, bytecodeKey) bytecode := r.bytecodeCache.Get(nil, bytecodeKey)
if len(bytecode) == 0 { if len(bytecode) == 0 {
urlPath := r.getNodeURLPath(node) fsPath := node.fsPath
if err := r.compileWithMiddleware(node, urlPath, scriptPath); err != nil { if fsPath == "" {
fsPath = "/"
}
if err := r.compileWithMiddleware(node, fsPath, scriptPath); err != nil {
return nil, scriptPath, node.err, true return nil, scriptPath, node.err, true
} }
bytecode = r.bytecodeCache.Get(nil, bytecodeKey) bytecode = r.bytecodeCache.Get(nil, bytecodeKey)
@ -617,12 +663,6 @@ func (r *LuaRouter) GetRouteInfo(method, path string, params *Params) ([]byte, s
return bytecode, scriptPath, node.err, true return bytecode, scriptPath, node.err, true
} }
// getNodeURLPath reconstructs URL path for a node (simplified)
func (r *LuaRouter) getNodeURLPath(node *node) string {
// This is a simplified implementation - in practice you'd traverse up the tree
return "/"
}
// nodeForHandler finds a node by its handler path // nodeForHandler finds a node by its handler path
func (r *LuaRouter) nodeForHandler(handlerPath string) (*node, bool) { func (r *LuaRouter) nodeForHandler(handlerPath string) (*node, bool) {
r.mu.RLock() r.mu.RLock()

View File

@ -1,7 +1,7 @@
package metadata package metadata
// Version holds the current Moonshark version // Version holds the current Moonshark version
const Version = "0.1" const Version = "1.0"
// Build time information // Build time information
var ( var (