Moonshark/router/compile.go

177 lines
4.3 KiB
Go

package router
import (
"os"
"strings"
"time"
)
// compileWithMiddleware combines middleware and handler source, then compiles
func (r *LuaRouter) compileWithMiddleware(n *node, fsPath, scriptPath string) error {
if scriptPath == "" {
return nil
}
// Check if we need to recompile by comparing modification times
sourceKey := r.getSourceCacheKey(fsPath, scriptPath)
needsRecompile := false
// Check handler modification time
handlerInfo, err := os.Stat(scriptPath)
if err != nil {
n.err = err
return err
}
lastCompiled, exists := r.sourceMtimes[sourceKey]
if !exists || handlerInfo.ModTime().After(lastCompiled) {
needsRecompile = true
}
// Check middleware modification times
if !needsRecompile {
middlewareChain := r.getMiddlewareChain(fsPath)
for _, mwPath := range middlewareChain {
mwInfo, err := os.Stat(mwPath)
if err != nil {
n.err = err
return err
}
if mwInfo.ModTime().After(lastCompiled) {
needsRecompile = true
break
}
}
}
// Use cached bytecode if available and fresh
if !needsRecompile {
if bytecode, exists := r.sourceCache[sourceKey]; exists {
bytecodeKey := getBytecodeKey(scriptPath)
r.bytecodeCache.Set(bytecodeKey, bytecode)
return nil
}
}
// Build combined source
combinedSource, err := r.buildCombinedSource(fsPath, scriptPath)
if err != nil {
n.err = err
return err
}
// Compile combined source using shared state
r.compileStateMu.Lock()
bytecode, err := r.compileState.CompileBytecode(combinedSource, scriptPath)
r.compileStateMu.Unlock()
if err != nil {
n.err = err
return err
}
// Cache everything
bytecodeKey := getBytecodeKey(scriptPath)
r.bytecodeCache.Set(bytecodeKey, bytecode)
r.sourceCache[sourceKey] = bytecode
r.sourceMtimes[sourceKey] = time.Now()
n.err = nil
return nil
}
// buildCombinedSource builds the combined middleware + handler source
func (r *LuaRouter) buildCombinedSource(fsPath, scriptPath string) (string, error) {
var combinedSource strings.Builder
// Get middleware chain using filesystem path
middlewareChain := r.getMiddlewareChain(fsPath)
// Add middleware in order
for _, mwPath := range middlewareChain {
content, err := r.getFileContent(mwPath)
if err != nil {
return "", err
}
combinedSource.WriteString("-- Middleware: ")
combinedSource.WriteString(mwPath)
combinedSource.WriteString("\n")
combinedSource.Write(content)
combinedSource.WriteString("\n")
}
// Add main handler
content, err := r.getFileContent(scriptPath)
if err != nil {
return "", err
}
combinedSource.WriteString("-- Handler: ")
combinedSource.WriteString(scriptPath)
combinedSource.WriteString("\n")
combinedSource.Write(content)
return combinedSource.String(), nil
}
// getFileContent reads file content with caching
func (r *LuaRouter) getFileContent(path string) ([]byte, error) {
// Check cache first
if content, exists := r.middlewareCache[path]; exists {
// Verify file hasn't changed
info, err := os.Stat(path)
if err == nil {
if cachedTime, exists := r.sourceMtimes[path]; exists && !info.ModTime().After(cachedTime) {
return content, nil
}
}
}
// Read from disk
content, err := os.ReadFile(path)
if err != nil {
return nil, err
}
// Cache it
r.middlewareCache[path] = content
r.sourceMtimes[path] = time.Now()
return content, nil
}
// getSourceCacheKey generates a unique key for combined source
func (r *LuaRouter) getSourceCacheKey(fsPath, scriptPath string) string {
middlewareChain := r.getMiddlewareChain(fsPath)
var keyParts []string
keyParts = append(keyParts, middlewareChain...)
keyParts = append(keyParts, scriptPath)
return strings.Join(keyParts, "|")
}
// getMiddlewareChain returns middleware files that apply to the given filesystem path
func (r *LuaRouter) getMiddlewareChain(fsPath string) []string {
var chain []string
// Collect middleware from root to specific path using filesystem path (includes groups)
pathParts := strings.Split(strings.Trim(fsPath, "/"), "/")
if pathParts[0] == "" {
pathParts = []string{}
}
// Add root middleware
if mw, exists := r.middlewareFiles["/"]; exists {
chain = append(chain, mw...)
}
// Add middleware from each path level (including groups)
currentPath := ""
for _, part := range pathParts {
currentPath += "/" + part
if mw, exists := r.middlewareFiles[currentPath]; exists {
chain = append(chain, mw...)
}
}
return chain
}