From 012c49d4f7ff739efe98ef353d2d390cbec2816a Mon Sep 17 00:00:00 2001 From: PhatPhuckDave Date: Sat, 22 Mar 2025 04:06:30 +0100 Subject: [PATCH] Much more better logs --- main.go | 303 ++++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 240 insertions(+), 63 deletions(-) diff --git a/main.go b/main.go index 1b6d641..9787ba8 100644 --- a/main.go +++ b/main.go @@ -16,18 +16,55 @@ import ( var Error *log.Logger var Warning *log.Logger +var Info *log.Logger +var Success *log.Logger + +// ModificationRecord tracks a single value modification +type ModificationRecord struct { + File string + OldValue string + NewValue string + Operation string + Context string +} + +// GlobalStats tracks all modifications across files +type GlobalStats struct { + TotalMatches int + TotalModifications int + Modifications []ModificationRecord + ProcessedFiles int + FailedFiles int +} + +var stats GlobalStats func init() { + // Configure standard logging to be hidden by default log.SetFlags(log.Lmicroseconds | log.Lshortfile) - logger := io.MultiWriter(os.Stdout) - log.SetOutput(logger) + log.SetOutput(io.Discard) // Disable default logging to stdout + // Set up custom loggers with different severity levels Error = log.New(io.MultiWriter(os.Stderr, os.Stdout), fmt.Sprintf("%sERROR:%s ", "\033[0;101m", "\033[0m"), log.Lmicroseconds|log.Lshortfile) + Warning = log.New(os.Stdout, fmt.Sprintf("%sWarning:%s ", "\033[0;93m", "\033[0m"), log.Lmicroseconds|log.Lshortfile) + + Info = log.New(os.Stdout, + fmt.Sprintf("%sInfo:%s ", "\033[0;94m", "\033[0m"), + log.Lmicroseconds|log.Lshortfile) + + Success = log.New(os.Stdout, + fmt.Sprintf("%s✨ SUCCESS:%s ", "\033[0;92m", "\033[0m"), + log.Lmicroseconds|log.Lshortfile) + + // Initialize global stats + stats = GlobalStats{ + Modifications: make([]ModificationRecord, 0), + } } func main() { @@ -46,7 +83,7 @@ func main() { flag.Parse() args := flag.Args() if len(args) < 3 { - Error.Println("Insufficient arguments") + Error.Println("Insufficient arguments - need regex pattern, lua expression, and at least one file") flag.Usage() return } @@ -55,110 +92,199 @@ func main() { luaExpr := args[1] files := args[2:] - log.Printf("Parsed arguments: regexPattern=%s, luaExpr=%s, files=%v", regexPattern, luaExpr, files) + Info.Printf("Starting modifier with pattern '%s', expression '%s' on %d files", regexPattern, luaExpr, len(files)) // Prepare the Lua expression + originalLuaExpr := luaExpr luaExpr = buildLuaScript(luaExpr) - log.Printf("Built Lua expression: %s", luaExpr) + if originalLuaExpr != luaExpr { + Info.Printf("Transformed Lua expression from '%s' to '%s'", originalLuaExpr, luaExpr) + } + + // Handle special pattern modifications + originalPattern := regexPattern + patternModified := false if strings.Contains(regexPattern, "!num") { regexPattern = strings.ReplaceAll(regexPattern, "!num", "(\\d*\\.?\\d+)") - log.Printf("Updated regexPattern after replacing !num: %s", regexPattern) + patternModified = true } // Make sure the regex can match across multiple lines by adding (?s) flag if !strings.HasPrefix(regexPattern, "(?s)") { regexPattern = "(?s)" + regexPattern - log.Printf("Updated regexPattern to match across multiple lines: %s", regexPattern) + patternModified = true + } + + if patternModified { + Info.Printf("Modified regex pattern from '%s' to '%s'", originalPattern, regexPattern) } // Compile the pattern for file processing pattern, err := regexp.Compile(regexPattern) if err != nil { - Error.Printf("Invalid regex pattern: %v", err) + Error.Printf("Invalid regex pattern '%s': %v", regexPattern, err) return } // Process each file for _, file := range files { - log.Printf("Processing file: %s", file) - err := processFile(file, pattern, luaExpr) + Info.Printf("šŸ”„ Processing file: %s", file) + err := processFile(file, pattern, luaExpr, originalLuaExpr) if err != nil { - Error.Printf("Error processing file %s: %v", file, err) + Error.Printf("āŒ Failed to process file %s: %v", file, err) + stats.FailedFiles++ + } else { + Info.Printf("āœ… Successfully processed file: %s", file) + stats.ProcessedFiles++ } } + + // Print summary of all modifications + printSummary(originalLuaExpr) +} + +// printSummary outputs a formatted summary of all modifications made +func printSummary(operation string) { + if stats.TotalModifications == 0 { + Warning.Printf("No modifications were made in any files") + return + } + + Success.Printf("Operation complete! Modified %d values in %d/%d files using '%s'", + stats.TotalModifications, stats.ProcessedFiles, stats.ProcessedFiles+stats.FailedFiles, operation) + + // Group modifications by file for better readability + fileGroups := make(map[string][]ModificationRecord) + for _, mod := range stats.Modifications { + fileGroups[mod.File] = append(fileGroups[mod.File], mod) + } + + // Print modifications by file + for file, mods := range fileGroups { + Success.Printf("šŸ“„ %s: %d modifications", file, len(mods)) + + // Show some sample modifications (max 5 per file to avoid overwhelming output) + maxSamples := 5 + if len(mods) > maxSamples { + for i := 0; i < maxSamples; i++ { + mod := mods[i] + Success.Printf(" %d. '%s' → '%s' %s", + i+1, limitString(mod.OldValue, 20), limitString(mod.NewValue, 20), mod.Context) + } + Success.Printf(" ... and %d more modifications", len(mods)-maxSamples) + } else { + for i, mod := range mods { + Success.Printf(" %d. '%s' → '%s' %s", + i+1, limitString(mod.OldValue, 20), limitString(mod.NewValue, 20), mod.Context) + } + } + } + + // Print a nice visual indicator of success + if stats.TotalModifications > 0 { + Success.Printf("šŸŽ‰ All done! Modified %d values successfully!", stats.TotalModifications) + } } // buildLuaScript creates a complete Lua script from the expression func buildLuaScript(luaExpr string) string { - if strings.HasPrefix(luaExpr, "*") { - luaExpr = "v1 = v1" + luaExpr - } else if strings.HasPrefix(luaExpr, "/") { - luaExpr = "v1 = v1" + luaExpr - } else if strings.HasPrefix(luaExpr, "+") { - luaExpr = "v1 = v1" + luaExpr - } else if strings.HasPrefix(luaExpr, "-") { - luaExpr = "v1 = v1" + luaExpr - } else if strings.HasPrefix(luaExpr, "^") { - luaExpr = "v1 = v1" + luaExpr - } else if strings.HasPrefix(luaExpr, "%") { + // Track if we modified the expression + modified := false + original := luaExpr + + // Auto-prepend v1 for expressions starting with operators + if strings.HasPrefix(luaExpr, "*") || + strings.HasPrefix(luaExpr, "/") || + strings.HasPrefix(luaExpr, "+") || + strings.HasPrefix(luaExpr, "-") || + strings.HasPrefix(luaExpr, "^") || + strings.HasPrefix(luaExpr, "%") { luaExpr = "v1 = v1" + luaExpr + modified = true } else if strings.HasPrefix(luaExpr, "=") { // Handle direct assignment with = operator luaExpr = "v1 " + luaExpr + modified = true } - // Check if the expression starts with an operator that needs special handling + + // Add assignment if needed if !strings.Contains(luaExpr, "=") { luaExpr = "v1 = " + luaExpr + modified = true } // Replace shorthand v1, v2, etc. with their direct variable names shorthandRegex := regexp.MustCompile(`\bv\[(\d+)\]\b`) - luaExpr = shorthandRegex.ReplaceAllString(luaExpr, "v$1") + newExpr := shorthandRegex.ReplaceAllString(luaExpr, "v$1") + if newExpr != luaExpr { + luaExpr = newExpr + modified = true + } + + if modified { + Info.Printf("Transformed Lua expression: '%s' → '%s'", original, luaExpr) + } - log.Printf("Final Lua expression after processing: %s", luaExpr) return luaExpr } -func processFile(filename string, pattern *regexp.Regexp, luaExpr string) error { +func processFile(filename string, pattern *regexp.Regexp, luaExpr string, originalExpr string) error { fullPath := filepath.Join(".", filename) - log.Printf("Processing file: %s", fullPath) + // Read file content content, err := os.ReadFile(fullPath) if err != nil { - Error.Printf("Error reading file %s: %v", fullPath, err) + Error.Printf("Cannot read file %s: %v", fullPath, err) return fmt.Errorf("error reading file: %v", err) } - log.Printf("Successfully read file: %s, content length: %d bytes", fullPath, len(content)) fileContent := string(content) - result, err := process(fileContent, pattern, luaExpr) + Info.Printf("File %s loaded: %d bytes", fullPath, len(content)) + + // Process the content + result, modificationCount, matchCount, err := process(fileContent, pattern, luaExpr, filename, originalExpr) if err != nil { - Error.Printf("Error processing content of file %s: %v", fullPath, err) + Error.Printf("Processing failed for %s: %v", fullPath, err) return err } - log.Printf("Successfully processed content of file: %s, result length: %d bytes", fullPath, len(result)) + // Update global stats + stats.TotalMatches += matchCount + stats.TotalModifications += modificationCount + + if modificationCount == 0 { + Warning.Printf("No modifications made to %s - pattern didn't match any content", fullPath) + return nil + } + + // Write the modified content back err = os.WriteFile(fullPath, []byte(result), 0644) if err != nil { - Error.Printf("Error writing file %s: %v", fullPath, err) + Error.Printf("Failed to save changes to %s: %v", fullPath, err) return fmt.Errorf("error writing file: %v", err) } - log.Printf("Successfully wrote modified content to file: %s, new content length: %d bytes", fullPath, len(result)) + + Info.Printf("Made %d modifications to %s and saved (%d bytes)", + modificationCount, fullPath, len(result)) return nil } -func process(data string, pattern *regexp.Regexp, luaExpr string) (string, error) { +func process(data string, pattern *regexp.Regexp, luaExpr string, filename string, originalExpr string) (string, int, int, error) { L := lua.NewState() defer L.Close() + // Initialize Lua environment + modificationCount := 0 + matchCount := 0 + // Load math library L.Push(L.GetGlobal("require")) L.Push(lua.LString("math")) if err := L.PCall(1, 1, nil); err != nil { - Error.Printf("Error loading Lua math library: %v", err) - return data, fmt.Errorf("error loading Lua math library: %v", err) + Error.Printf("Failed to load Lua math library: %v", err) + return data, 0, 0, fmt.Errorf("error loading Lua math library: %v", err) } // Initialize helper functions @@ -171,58 +297,53 @@ function floor(x) return math.floor(x) end function ceil(x) return math.ceil(x) end ` if err := L.DoString(helperScript); err != nil { - Error.Printf("Error loading helper functions: %v", err) - return data, fmt.Errorf("error loading helper functions: %v", err) + Error.Printf("Failed to load Lua helper functions: %v", err) + return data, 0, 0, fmt.Errorf("error loading helper functions: %v", err) } // Process all regex matches result := pattern.ReplaceAllStringFunc(data, func(match string) string { + matchCount++ captures := pattern.FindStringSubmatch(match) if len(captures) <= 1 { // No capture groups, return unchanged - log.Printf("No capture groups found for match: %s", match) + Warning.Printf("Match found but no capture groups: %s", limitString(match, 50)) return match } - // Set up global variables v1, v2, etc. + // Set up global variables v1, v2, etc. for the Lua context + captureValues := make([]string, len(captures)-1) for i, capture := range captures[1:] { + captureValues[i] = capture // Convert each capture to float if possible floatVal, err := strconv.ParseFloat(capture, 64) if err == nil { L.SetGlobal(fmt.Sprintf("v%d", i+1), lua.LNumber(floatVal)) - log.Printf("Set global v%d to float value: %f", i+1, floatVal) } else { L.SetGlobal(fmt.Sprintf("v%d", i+1), lua.LString(capture)) - log.Printf("Set global v%d to string value: %s", i+1, capture) } } // Execute the user's Lua code if err := L.DoString(luaExpr); err != nil { - Error.Printf("Error executing Lua script: %v", err) + Error.Printf("Lua execution failed for match '%s': %v", limitString(match, 50), err) return match // Return unchanged on error } - // Get the values of v1-v12 after code execution - returnValues := make([]lua.LValue, 12) - for i := 0; i < 12; i++ { - varName := fmt.Sprintf("v%d", i+1) - returnValues[i] = L.GetGlobal(varName) - log.Printf("Retrieved value for %s: %v", varName, returnValues[i]) - } - - // Replace each capture group with its new value (if available) - result := match + // Get the modified values after Lua execution + modifications := make(map[int]string) for i := 0; i < len(captures)-1 && i < 12; i++ { - if returnValues[i] == lua.LNil { - log.Printf("Skipping replacement for %s as it is nil", captures[i+1]) - continue // Skip if nil + varName := fmt.Sprintf("v%d", i+1) + luaVal := L.GetGlobal(varName) + + if luaVal == lua.LNil { + continue // Skip if nil (no modification) } oldVal := captures[i+1] var newVal string - switch v := returnValues[i].(type) { + switch v := luaVal.(type) { case lua.LNumber: newVal = strconv.FormatFloat(float64(v), 'f', -1, 64) case lua.LString: @@ -231,15 +352,71 @@ function ceil(x) return math.ceil(x) end newVal = fmt.Sprintf("%v", v) } - // If we have a value, replace it - if newVal != "" { - log.Printf("Replacing %s with %s in match: %s", oldVal, newVal, match) - result = strings.Replace(result, oldVal, newVal, 1) + // Record modification if the value actually changed + if newVal != oldVal { + modifications[i] = newVal } } + // Apply modifications to the matched text + if len(modifications) == 0 { + return match // No changes + } + + result := match + for i, newVal := range modifications { + oldVal := captures[i+1] + result = strings.Replace(result, oldVal, newVal, 1) + + // Extract a bit of context from the match for better reporting + contextStart := max(0, strings.Index(match, oldVal)-10) + contextLength := min(30, len(match)-contextStart) + if contextStart+contextLength > len(match) { + contextLength = len(match) - contextStart + } + contextStr := "..." + match[contextStart:contextStart+contextLength] + "..." + + // Log the modification + Info.Printf("Modified value [%d]: '%s' → '%s'", i+1, limitString(oldVal, 30), limitString(newVal, 30)) + + // Record the modification for summary + stats.Modifications = append(stats.Modifications, ModificationRecord{ + File: filename, + OldValue: oldVal, + NewValue: newVal, + Operation: originalExpr, + Context: fmt.Sprintf("(in %s)", limitString(contextStr, 30)), + }) + } + + modificationCount++ return result }) - return result, nil + return result, modificationCount, matchCount, nil +} + +// limitString truncates a string to maxLen and adds "..." if truncated +func limitString(s string, maxLen int) string { + s = strings.ReplaceAll(s, "\n", "\\n") + if len(s) <= maxLen { + return s + } + return s[:maxLen-3] + "..." +} + +// max returns the maximum of two integers +func max(a, b int) int { + if a > b { + return a + } + return b +} + +// min returns the minimum of two integers +func min(a, b int) int { + if a < b { + return a + } + return b }