package utils import ( "os" "path/filepath" "strconv" "strings" logger "git.site.quack-lab.dev/dave/cylogger" ) // fileLogger is a scoped logger for the utils/file package. var fileLogger = logger.Default.WithPrefix("utils/file") func CleanPath(path string) string { cleanPathLogger := fileLogger.WithPrefix("CleanPath") cleanPathLogger.Debug("Cleaning path: %q", path) cleanPathLogger.Trace("Original path: %q", path) path = filepath.Clean(path) path = strings.ReplaceAll(path, "\\", "/") cleanPathLogger.Trace("Cleaned path result: %q", path) return path } func ToAbs(path string) string { toAbsLogger := fileLogger.WithPrefix("ToAbs") toAbsLogger.Debug("Converting path to absolute: %q", path) toAbsLogger.Trace("Input path: %q", path) if filepath.IsAbs(path) { toAbsLogger.Debug("Path is already absolute, cleaning it.") cleanedPath := CleanPath(path) toAbsLogger.Trace("Already absolute path after cleaning: %q", cleanedPath) return cleanedPath } cwd, err := os.Getwd() if err != nil { toAbsLogger.Error("Error getting current working directory: %v", err) return CleanPath(path) } toAbsLogger.Trace("Current working directory: %q", cwd) cleanedPath := CleanPath(filepath.Join(cwd, path)) toAbsLogger.Trace("Converted absolute path result: %q", cleanedPath) return cleanedPath } // LimitString truncates a string to maxLen and adds "..." if truncated func LimitString(s string, maxLen int) string { limitStringLogger := fileLogger.WithPrefix("LimitString").WithField("originalLength", len(s)).WithField("maxLength", maxLen) limitStringLogger.Debug("Limiting string length") s = strings.ReplaceAll(s, "\n", "\\n") if len(s) <= maxLen { limitStringLogger.Trace("String length (%d) is within max length (%d), no truncation", len(s), maxLen) return s } limited := s[:maxLen-3] + "..." limitStringLogger.Trace("String truncated from %d to %d characters: %q", len(s), len(limited), limited) return limited } // StrToFloat converts a string to a float64, returning 0 on error. func StrToFloat(s string) float64 { strToFloatLogger := fileLogger.WithPrefix("StrToFloat").WithField("inputString", s) strToFloatLogger.Debug("Attempting to convert string to float") f, err := strconv.ParseFloat(s, 64) if err != nil { strToFloatLogger.Warning("Failed to convert string %q to float, returning 0: %v", s, err) return 0 } strToFloatLogger.Trace("Successfully converted %q to float: %f", s, f) return f } func ResetWhereNecessary(associations map[string]FileCommandAssociation, db DB) error { resetWhereNecessaryLogger := fileLogger.WithPrefix("ResetWhereNecessary") resetWhereNecessaryLogger.Debug("Starting reset where necessary operation") resetWhereNecessaryLogger.Trace("File-command associations input: %v", associations) dirtyFiles := make(map[string]struct{}) for _, association := range associations { resetWhereNecessaryLogger.Debug("Processing association for file: %q", association.File) for _, command := range association.Commands { resetWhereNecessaryLogger.Debug("Checking command %q for reset requirement", command.Name) resetWhereNecessaryLogger.Trace("Command details: %v", command) if command.Reset { resetWhereNecessaryLogger.Debug("Command %q requires reset for file %q, marking as dirty", command.Name, association.File) dirtyFiles[association.File] = struct{}{} } } for _, command := range association.IsolateCommands { resetWhereNecessaryLogger.Debug("Checking isolate command %q for reset requirement", command.Name) resetWhereNecessaryLogger.Trace("Isolate command details: %v", command) if command.Reset { resetWhereNecessaryLogger.Debug("Isolate command %q requires reset for file %q, marking as dirty", command.Name, association.File) dirtyFiles[association.File] = struct{}{} } } } resetWhereNecessaryLogger.Debug("Identified %d files that need to be reset", len(dirtyFiles)) resetWhereNecessaryLogger.Trace("Dirty files identified: %v", dirtyFiles) for file := range dirtyFiles { resetWhereNecessaryLogger.Debug("Resetting file %q", file) fileData, err := db.GetFile(file) if err != nil { resetWhereNecessaryLogger.Warning("Failed to get original content for file %q from database: %v", file, err) continue } resetWhereNecessaryLogger.Trace("Retrieved original file data length for %q: %d", file, len(fileData)) resetWhereNecessaryLogger.Debug("Writing original content back to file %q", file) err = os.WriteFile(file, fileData, 0644) if err != nil { resetWhereNecessaryLogger.Warning("Failed to write original content back to file %q: %v", file, err) continue } resetWhereNecessaryLogger.Debug("Successfully reset file %q", file) } resetWhereNecessaryLogger.Debug("Finished reset where necessary operation") return nil } func ResetAllFiles(db DB) error { resetAllFilesLogger := fileLogger.WithPrefix("ResetAllFiles") resetAllFilesLogger.Debug("Starting reset all files operation") fileSnapshots, err := db.GetAllFiles() if err != nil { resetAllFilesLogger.Error("Failed to get all file snapshots from database: %v", err) return err } resetAllFilesLogger.Debug("Found %d files in database to reset", len(fileSnapshots)) resetAllFilesLogger.Trace("File snapshots retrieved: %v", fileSnapshots) for _, fileSnapshot := range fileSnapshots { resetAllFilesLogger.Debug("Resetting file %q", fileSnapshot.FilePath) err = os.WriteFile(fileSnapshot.FilePath, fileSnapshot.FileData, 0644) if err != nil { resetAllFilesLogger.Warning("Failed to write file %q to disk: %v", fileSnapshot.FilePath, err) continue } resetAllFilesLogger.Debug("File %q written to disk successfully", fileSnapshot.FilePath) } resetAllFilesLogger.Debug("Finished reset all files operation") return nil }