diff --git a/main.go b/main.go index c49981e..15a3ee0 100644 --- a/main.go +++ b/main.go @@ -96,6 +96,23 @@ func main() { flag.Usage() return } + // Collect global modifiers from special entries and filter them out + vars := map[string]interface{}{} + filtered := make([]utils.ModifyCommand, 0, len(commands)) + for _, c := range commands { + if len(c.Modifiers) > 0 && c.Name == "" && c.Regex == "" && len(c.Regexes) == 0 && c.Lua == "" && len(c.Files) == 0 { + for k, v := range c.Modifiers { + vars[k] = v + } + continue + } + filtered = append(filtered, c) + } + if len(vars) > 0 { + mainLogger.Info("Loaded %d global modifiers", len(vars)) + processor.SetVariables(vars) + } + commands = filtered mainLogger.Info("Loaded %d commands", len(commands)) if *utils.Filter != "" { @@ -111,7 +128,11 @@ func main() { for _, command := range commands { mainLogger.Trace("Command: %s", command.Name) - mainLogger.Trace("Regex: %s", command.Regex) + if len(command.Regexes) > 0 { + mainLogger.Trace("Regexes: %v", command.Regexes) + } else { + mainLogger.Trace("Regex: %s", command.Regex) + } mainLogger.Trace("Files: %v", command.Files) mainLogger.Trace("Lua: %s", command.Lua) mainLogger.Trace("Reset: %t", command.Reset) @@ -428,31 +449,36 @@ func RunOtherCommands(file string, fileDataStr string, association utils.FileCom cmdLogger = cmdLog } - cmdLogger.Debug("Begin processing file with command %q", command.Regex) - numCommandsConsidered++ - newModifications, err := processor.ProcessRegex(fileDataStr, command, file) - if err != nil { - runOtherCommandsLogger.Error("Failed to process file with command %q: %v", command.Regex, err) - continue + patterns := command.Regexes + if len(patterns) == 0 { + patterns = []string{command.Regex} } - modifications = append(modifications, newModifications...) - // It is not guranteed that all the commands will be executed... - // TODO: Make this better - // We'd have to pass the map to executemodifications or something... - count, ok := stats.ModificationsPerCommand.Load(command.Name) - if !ok { - count = 0 - } - stats.ModificationsPerCommand.Store(command.Name, count.(int)+len(newModifications)) + for idx, pattern := range patterns { + tmpCmd := command + tmpCmd.Regex = pattern + cmdLogger.Debug("Begin processing file with command %q (pattern %d/%d)", command.Name, idx+1, len(patterns)) + numCommandsConsidered++ + newModifications, err := processor.ProcessRegex(fileDataStr, tmpCmd, file) + if err != nil { + runOtherCommandsLogger.Error("Failed to process file with command %q: %v", command.Name, err) + continue + } + modifications = append(modifications, newModifications...) + count, ok := stats.ModificationsPerCommand.Load(command.Name) + if !ok { + count = 0 + } + stats.ModificationsPerCommand.Store(command.Name, count.(int)+len(newModifications)) - cmdLogger.Debug("Command %q generated %d modifications", command.Name, len(newModifications)) - cmdLogger.Trace("Modifications generated by command %q: %v", command.Name, newModifications) - if len(newModifications) == 0 { - cmdLogger.Debug("No modifications yielded by command %q", command.Name) + cmdLogger.Debug("Command %q generated %d modifications (pattern %d/%d)", command.Name, len(newModifications), idx+1, len(patterns)) + cmdLogger.Trace("Modifications generated by command %q: %v", command.Name, newModifications) + if len(newModifications) == 0 { + cmdLogger.Debug("No modifications yielded by command %q (pattern %d/%d)", command.Name, idx+1, len(patterns)) + } } } - runOtherCommandsLogger.Debug("Aggregated %d modifications from %d commands", len(modifications), numCommandsConsidered) + runOtherCommandsLogger.Debug("Aggregated %d modifications from %d command-pattern runs", len(modifications), numCommandsConsidered) runOtherCommandsLogger.Trace("All aggregated modifications: %v", modifications) if len(modifications) == 0 { @@ -480,27 +506,35 @@ func RunIsolateCommands(association utils.FileCommandAssociation, file string, f anythingDone := false for _, isolateCommand := range association.IsolateCommands { runIsolateCommandsLogger.Debug("Begin processing file with isolate command %q", isolateCommand.Regex) - modifications, err := processor.ProcessRegex(fileDataStr, isolateCommand, file) - if err != nil { - runIsolateCommandsLogger.Error("Failed to process file with isolate command %q: %v", isolateCommand.Regex, err) - continue + patterns := isolateCommand.Regexes + if len(patterns) == 0 { + patterns = []string{isolateCommand.Regex} } + for idx, pattern := range patterns { + tmpCmd := isolateCommand + tmpCmd.Regex = pattern + modifications, err := processor.ProcessRegex(fileDataStr, tmpCmd, file) + if err != nil { + runIsolateCommandsLogger.Error("Failed to process file with isolate command %q (pattern %d/%d): %v", isolateCommand.Name, idx+1, len(patterns), err) + continue + } - if len(modifications) == 0 { - runIsolateCommandsLogger.Debug("Isolate command %q produced no modifications", isolateCommand.Name) - continue + if len(modifications) == 0 { + runIsolateCommandsLogger.Debug("Isolate command %q produced no modifications (pattern %d/%d)", isolateCommand.Name, idx+1, len(patterns)) + continue + } + anythingDone = true + + runIsolateCommandsLogger.Debug("Executing %d isolate modifications for file", len(modifications)) + runIsolateCommandsLogger.Trace("Isolate modifications: %v", modifications) + var count int + fileDataStr, count = utils.ExecuteModifications(modifications, fileDataStr) + runIsolateCommandsLogger.Trace("File data after isolate modifications: %s", utils.LimitString(fileDataStr, 200)) + + atomic.AddInt64(&stats.TotalModifications, int64(count)) + + runIsolateCommandsLogger.Info("Executed %d isolate modifications for file", count) } - anythingDone = true - - runIsolateCommandsLogger.Debug("Executing %d isolate modifications for file", len(modifications)) - runIsolateCommandsLogger.Trace("Isolate modifications: %v", modifications) - var count int - fileDataStr, count = utils.ExecuteModifications(modifications, fileDataStr) - runIsolateCommandsLogger.Trace("File data after isolate modifications: %s", utils.LimitString(fileDataStr, 200)) - - atomic.AddInt64(&stats.TotalModifications, int64(count)) - - runIsolateCommandsLogger.Info("Executed %d isolate modifications for file", count) } if !anythingDone { runIsolateCommandsLogger.Debug("No isolate modifications were made for file") diff --git a/processor/processor.go b/processor/processor.go index 1541a71..d40b7d8 100644 --- a/processor/processor.go +++ b/processor/processor.go @@ -4,6 +4,7 @@ import ( "fmt" "io" "net/http" + "regexp" "strings" "cook/utils" @@ -18,6 +19,14 @@ var processorLogger = logger.Default.WithPrefix("processor") // Maybe we make this an interface again for the shits and giggles // We will see, it could easily be... +var globalVariables = map[string]interface{}{} + +func SetVariables(vars map[string]interface{}) { + for k, v := range vars { + globalVariables[k] = v + } +} + func NewLuaState() (*lua.LState, error) { newLStateLogger := processorLogger.WithPrefix("NewLuaState") newLStateLogger.Debug("Creating new Lua state") @@ -42,6 +51,34 @@ func NewLuaState() (*lua.LState, error) { } newLStateLogger.Debug("Lua helper functions initialized") + // Inject global variables + if len(globalVariables) > 0 { + newLStateLogger.Debug("Injecting %d global variables into Lua state", len(globalVariables)) + for k, v := range globalVariables { + switch val := v.(type) { + case int: + L.SetGlobal(k, lua.LNumber(float64(val))) + case int64: + L.SetGlobal(k, lua.LNumber(float64(val))) + case float32: + L.SetGlobal(k, lua.LNumber(float64(val))) + case float64: + L.SetGlobal(k, lua.LNumber(val)) + case string: + L.SetGlobal(k, lua.LString(val)) + case bool: + if val { + L.SetGlobal(k, lua.LTrue) + } else { + L.SetGlobal(k, lua.LFalse) + } + default: + // Fallback to string representation + L.SetGlobal(k, lua.LString(fmt.Sprintf("%v", val))) + } + } + } + newLStateLogger.Debug("New Lua state created successfully") return L, nil } @@ -246,6 +283,9 @@ func BuildLuaScript(luaExpr string) string { buildLuaScriptLogger := processorLogger.WithPrefix("BuildLuaScript").WithField("inputLuaExpr", luaExpr) buildLuaScriptLogger.Debug("Building full Lua script from expression") + // Perform $var substitutions from globalVariables + luaExpr = replaceVariables(luaExpr) + luaExpr = PrependLuaAssignment(luaExpr) fullScript := fmt.Sprintf(` @@ -260,6 +300,32 @@ func BuildLuaScript(luaExpr string) string { return fullScript } +func replaceVariables(expr string) string { + // $varName -> literal value + varNameRe := regexp.MustCompile(`\$(\w+)`) + return varNameRe.ReplaceAllStringFunc(expr, func(m string) string { + name := varNameRe.FindStringSubmatch(m)[1] + if v, ok := globalVariables[name]; ok { + switch val := v.(type) { + case int, int64, float32, float64: + return fmt.Sprintf("%v", val) + case bool: + if val { + return "true" + } else { + return "false" + } + case string: + // Quote strings for Lua literal + return fmt.Sprintf("%q", val) + default: + return fmt.Sprintf("%q", fmt.Sprintf("%v", val)) + } + } + return m + }) +} + func printToGo(L *lua.LState) int { printToGoLogger := processorLogger.WithPrefix("printToGo") printToGoLogger.Debug("Lua print function called, redirecting to Go logger") diff --git a/utils/modifycommand.go b/utils/modifycommand.go index 44a7ac7..8ecdb2a 100644 --- a/utils/modifycommand.go +++ b/utils/modifycommand.go @@ -15,15 +15,17 @@ import ( var modifyCommandLogger = logger.Default.WithPrefix("utils/modifycommand") type ModifyCommand struct { - Name string `yaml:"name"` - Regex string `yaml:"regex"` - Lua string `yaml:"lua"` - Files []string `yaml:"files"` - Reset bool `yaml:"reset"` - LogLevel string `yaml:"loglevel"` - Isolate bool `yaml:"isolate"` - NoDedup bool `yaml:"nodedup"` - Disabled bool `yaml:"disable"` + Name string `yaml:"name"` + Regex string `yaml:"regex"` + Regexes []string `yaml:"regexes"` + Lua string `yaml:"lua"` + Files []string `yaml:"files"` + Reset bool `yaml:"reset"` + LogLevel string `yaml:"loglevel"` + Isolate bool `yaml:"isolate"` + NoDedup bool `yaml:"nodedup"` + Disabled bool `yaml:"disable"` + Modifiers map[string]interface{} `yaml:"modifiers"` } type CookFile []ModifyCommand @@ -31,7 +33,7 @@ type CookFile []ModifyCommand func (c *ModifyCommand) Validate() error { validateLogger := modifyCommandLogger.WithPrefix("Validate").WithField("commandName", c.Name) validateLogger.Debug("Validating command") - if c.Regex == "" { + if c.Regex == "" && len(c.Regexes) == 0 { validateLogger.Error("Validation failed: Regex pattern is required") return fmt.Errorf("pattern is required") }