diff --git a/.gitignore b/.gitignore index d02ea5c..d3b3343 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ *.exe *.exe cln +cln.log diff --git a/instruction.go b/instruction.go index cb5efaa..decaf83 100644 --- a/instruction.go +++ b/instruction.go @@ -132,12 +132,14 @@ func isTrue(value string) bool { func (instruction *LinkInstruction) RunAsync(status chan (error)) { defer close(status) if !FileExists(instruction.Source) { - status <- fmt.Errorf("instruction source %s%s%s does not exist", SourceColor, instruction.Source, DefaultColor) + status <- fmt.Errorf("instruction source %s does not exist", FormatSourcePath(instruction.Source)) return } if !instruction.Force && AreSame(instruction.Source, instruction.Target) { - status <- fmt.Errorf("source %s%s%s and target %s%s%s are the same, %snothing to do...%s", SourceColor, instruction.Source, DefaultColor, TargetColor, instruction.Target, DefaultColor, PathColor, DefaultColor) + status <- fmt.Errorf("source %s and target %s are the same, nothing to do...", + FormatSourcePath(instruction.Source), + FormatTargetPath(instruction.Target)) return } @@ -145,21 +147,24 @@ func (instruction *LinkInstruction) RunAsync(status chan (error)) { if instruction.Force { isSymlink, err := IsSymlink(instruction.Target) if err != nil { - status <- fmt.Errorf("could not determine whether %s%s%s is a sym link or not, stopping; err: %s%+v%s", TargetColor, instruction.Target, DefaultColor, ErrorColor, err, DefaultColor) + status <- fmt.Errorf("could not determine whether %s is a sym link or not, stopping; err: %v", + FormatTargetPath(instruction.Target), err) return } if instruction.Hard { info, err := os.Stat(instruction.Target) if err != nil { - status <- fmt.Errorf("could not stat %s%s%s, stopping; err: %s%+v%s", TargetColor, instruction.Target, DefaultColor, ErrorColor, err, DefaultColor) + status <- fmt.Errorf("could not stat %s, stopping; err: %v", + FormatTargetPath(instruction.Target), err) return } if info.Mode().IsRegular() && info.Name() == filepath.Base(instruction.Source) { LogTarget("Overwriting existing file %s", instruction.Target) err := os.Remove(instruction.Target) if err != nil { - status <- fmt.Errorf("could not remove existing file %s%s%s; err: %s%+v%s", TargetColor, instruction.Target, DefaultColor, ErrorColor, err, DefaultColor) + status <- fmt.Errorf("could not remove existing file %s; err: %v", + FormatTargetPath(instruction.Target), err) return } } @@ -169,23 +174,27 @@ func (instruction *LinkInstruction) RunAsync(status chan (error)) { LogTarget("Removing symlink at %s", instruction.Target) err = os.Remove(instruction.Target) if err != nil { - status <- fmt.Errorf("failed deleting %s%s%s due to %s%+v%s", TargetColor, instruction.Target, DefaultColor, ErrorColor, err, DefaultColor) + status <- fmt.Errorf("failed deleting %s due to %v", + FormatTargetPath(instruction.Target), err) return } } else { if !instruction.Delete { - status <- fmt.Errorf("refusing to delte actual (non symlink) file %s%s%s", TargetColor, instruction.Target, DefaultColor) + status <- fmt.Errorf("refusing to delte actual (non symlink) file %s", + FormatTargetPath(instruction.Target)) return } LogImportant("Deleting (!!!) %s", instruction.Target) err = os.RemoveAll(instruction.Target) if err != nil { - status <- fmt.Errorf("failed deleting %s%s%s due to %s%+v%s", TargetColor, instruction.Target, DefaultColor, ErrorColor, err, DefaultColor) + status <- fmt.Errorf("failed deleting %s due to %v", + FormatTargetPath(instruction.Target), err) return } } } else { - status <- fmt.Errorf("target %s%s%s exists - handle manually or set the 'forced' flag (3rd field)", TargetColor, instruction.Target, DefaultColor) + status <- fmt.Errorf("target %s exists - handle manually or set the 'forced' flag (3rd field)", + FormatTargetPath(instruction.Target)) return } } @@ -194,7 +203,8 @@ func (instruction *LinkInstruction) RunAsync(status chan (error)) { if _, err := os.Stat(targetDir); os.IsNotExist(err) { err = os.MkdirAll(targetDir, 0755) if err != nil { - status <- fmt.Errorf("failed creating directory %s%s%s due to %s%+v%s", TargetColor, targetDir, DefaultColor, ErrorColor, err, DefaultColor) + status <- fmt.Errorf("failed creating directory %s due to %v", + FormatTargetPath(targetDir), err) return } } @@ -206,7 +216,10 @@ func (instruction *LinkInstruction) RunAsync(status chan (error)) { err = os.Symlink(instruction.Source, instruction.Target) } if err != nil { - status <- fmt.Errorf("failed creating symlink between %s%s%s and %s%s%s with error %s%+v%s", SourceColor, instruction.Source, DefaultColor, TargetColor, instruction.Target, DefaultColor, ErrorColor, err, DefaultColor) + status <- fmt.Errorf("failed creating symlink between %s and %s with error %v", + FormatSourcePath(instruction.Source), + FormatTargetPath(instruction.Target), + err) return } LogInfo("Created symlink between %s and %s", diff --git a/logger.go b/logger.go index e7e0877..a32036b 100644 --- a/logger.go +++ b/logger.go @@ -5,62 +5,88 @@ import ( "log" ) +// Message type prefixes +const ( + InfoPrefix = "INFO" + ErrorPrefix = "ERROR" + WarningPrefix = "WARN" + SourcePrefix = "SOURCE" + TargetPrefix = "TARGET" + PathPrefix = "PATH" + ImportantPrefix = "IMPORTANT" +) + // LogInfo logs an informational message func LogInfo(format string, args ...interface{}) { - log.Printf(format, args...) + message := fmt.Sprintf(format, args...) + log.Printf("%s[%s]%s %s", BGreen, InfoPrefix, Reset, message) } // LogSource logs a message about a source file/path with proper coloring func LogSource(format string, args ...interface{}) { message := fmt.Sprintf(format, args...) - log.Printf("%s%s%s", SourceColor, message, DefaultColor) + log.Printf("%s[%s]%s %s%s%s", BPurple, SourcePrefix, Reset, SourceColor, message, Reset) } // LogTarget logs a message about a target file/path with proper coloring func LogTarget(format string, args ...interface{}) { message := fmt.Sprintf(format, args...) - log.Printf("%s%s%s", TargetColor, message, DefaultColor) + log.Printf("%s[%s]%s %s%s%s", BYellow, TargetPrefix, Reset, TargetColor, message, Reset) } // LogPath logs a message about a path with proper coloring func LogPath(format string, args ...interface{}) { message := fmt.Sprintf(format, args...) - log.Printf("%s%s%s", PathColor, message, DefaultColor) + log.Printf("%s[%s]%s %s%s%s", BGreen, PathPrefix, Reset, PathColor, message, Reset) } // LogImportant logs a message that needs attention with proper coloring func LogImportant(format string, args ...interface{}) { message := fmt.Sprintf(format, args...) - log.Printf("%s%s%s", ImportantColor, message, DefaultColor) + log.Printf("%s[%s]%s %s%s%s", BRed, ImportantPrefix, Reset, ImportantColor, message, Reset) } // LogError logs an error message with proper coloring that won't be cut off func LogError(format string, args ...interface{}) { + // Format the message first with normal text (no red coloring) message := fmt.Sprintf(format, args...) - log.Printf("%s%s%s", ErrorColor, message, DefaultColor) - // Make sure we're properly resetting before applying error color to avoid cutoffs - log.Printf("%s%s%s", ErrorColor, fmt.Sprintf(format, args...), DefaultColor) + + // The Error prefix itself is bold red on a light background for maximum visibility + prefix := fmt.Sprintf("%s%s[%s]%s ", BRed, On_White, ErrorPrefix, Reset) + + // The message is in default color (no red), only the [ERROR] prefix is colored + log.Printf("%s%s", prefix, message) } -// FormatSourcePath formats a source path with proper coloring +// FormatSourcePath returns a source path with proper coloring func FormatSourcePath(path string) string { - return fmt.Sprintf("%s%s%s", SourceColor, path, DefaultColor) + return fmt.Sprintf("%s%s%s", SourceColor, path, Reset) } -// FormatTargetPath formats a target path with proper coloring +// FormatTargetPath returns a target path with proper coloring func FormatTargetPath(path string) string { - return fmt.Sprintf("%s%s%s", TargetColor, path, DefaultColor) + return fmt.Sprintf("%s%s%s", TargetColor, path, Reset) } -// FormatPathValue formats a path value with proper coloring +// FormatPathValue returns a path with proper coloring func FormatPathValue(path string) string { - return fmt.Sprintf("%s%s%s", PathColor, path, DefaultColor) + return fmt.Sprintf("%s%s%s", PathColor, path, Reset) } -// FormatErrorValue formats an error value with proper coloring +// FormatErrorValue returns an error value without any additional formatting +// Since error messages are no longer red, we don't need special formatting func FormatErrorValue(err error) string { if err == nil { return "" } - return fmt.Sprintf("%s%v%s", ErrorColor, err, DefaultColor) + // Just return the error string with no color formatting + return fmt.Sprintf("%v", err) +} + +// FormatErrorMessage formats an error message with no additional color formatting, +// while preserving the special formatting of embedded source/target/path elements. +func FormatErrorMessage(format string, args ...interface{}) string { + // This just formats the message with no additional color formatting + // The path formatting will still be preserved + return fmt.Sprintf(format, args...) } diff --git a/main.go b/main.go index 85eb833..f82f3f6 100644 --- a/main.go +++ b/main.go @@ -15,9 +15,9 @@ import ( const deliminer = "," const SourceColor = Purple const TargetColor = Yellow -const ErrorColor = URed +const ErrorColor = Red const ImportantColor = BRed -const DefaultColor = White +const DefaultColor = Reset const PathColor = Green var FileRegex, _ = regexp.Compile(`sync\.ya?ml$`) @@ -31,7 +31,7 @@ func main() { if *debug { log.SetFlags(log.Lmicroseconds | log.Lshortfile) - logFile, err := os.Create("cln.log") + logFile, err := os.Create(programName + ".log") if err != nil { LogError("Error creating log file: %v", err) os.Exit(1) @@ -94,7 +94,7 @@ func main() { } }() - var instructionsDone int32 + var instructionsDone int32 = 0 var wg sync.WaitGroup for { instruction, ok := <-instructions @@ -108,8 +108,7 @@ func main() { wg.Add(1) err := <-status if err != nil { - LogError("Failed parsing instruction %s due to %v", - FormatSourcePath(instruction.String()), err) + LogError("Failed processing instruction: %v", err) } atomic.AddInt32(&instructionsDone, 1) wg.Done() @@ -136,7 +135,7 @@ func ReadFromFilesRecursively(input string, output chan *LinkInstruction, status workdir, _ := os.Getwd() input = NormalizePath(input, workdir) - LogInfo("Reading input from files recursively starting in %s%s%s", PathColor, input, DefaultColor) + LogInfo("Reading input from files recursively starting in %s", FormatPathValue(input)) files := make(chan string, 128) fileStatus := make(chan error) @@ -171,19 +170,21 @@ func ReadFromFilesRecursively(input string, output chan *LinkInstruction, status defer wg.Done() LogInfo(file) file = NormalizePath(file, workdir) - LogInfo("Processing file: %s%s%s", PathColor, file, DefaultColor) + LogInfo("Processing file: %s", FormatPathValue(file)) // This "has" to be done because instructions are resolved in relation to cwd fileDir := FileRegex.FindStringSubmatch(file) if fileDir == nil { - LogError("Failed to extract directory from %s%s%s", SourceColor, file, DefaultColor) + LogError("Failed to extract directory from %s", FormatSourcePath(file)) return } - LogInfo("Changing directory to %s%s%s (for %s%s%s)", PathColor, fileDir[1], DefaultColor, PathColor, file, DefaultColor) + LogInfo("Changing directory to %s (for %s)", + FormatPathValue(fileDir[1]), + FormatPathValue(file)) err := os.Chdir(fileDir[1]) if err != nil { - LogError("Failed to change directory to %s%s%s: %v", - SourceColor, fileDir[1], DefaultColor, err) + LogError("Failed to change directory to %s: %v", + FormatSourcePath(fileDir[1]), err) return } ReadFromFile(file, output, status, false) @@ -200,15 +201,15 @@ func ReadFromFile(input string, output chan *LinkInstruction, status chan error, } input = NormalizePath(input, filepath.Dir(input)) - LogInfo("Reading input from file: %s%s%s", PathColor, input, DefaultColor) + LogInfo("Reading input from file: %s", FormatPathValue(input)) // Check if this is a YAML file if IsYAMLFile(input) { LogInfo("Parsing as YAML file") instructions, err := ParseYAMLFile(input, filepath.Dir(input)) if err != nil { - LogError("Failed to parse YAML file %s%s%s: %s%+v%s", - SourceColor, input, DefaultColor, ErrorColor, err, DefaultColor) + LogError("Failed to parse YAML file %s: %v", + FormatSourcePath(input), err) status <- err return } @@ -231,8 +232,7 @@ func ReadFromArgs(output chan *LinkInstruction, status chan error) { for _, arg := range flag.Args() { instruction, err := ParseInstruction(arg, workdir) if err != nil { - LogError("Error parsing arg: %s'%s'%s, error: %v", - SourceColor, arg, DefaultColor, err) + LogError("Error parsing arg '%s': %v", arg, err) continue } output <- &instruction @@ -251,8 +251,7 @@ func ReadFromStdin(output chan *LinkInstruction, status chan error) { line := scanner.Text() instruction, err := ParseInstruction(line, workdir) if err != nil { - LogError("Error parsing line: %s'%s'%s, error: %v", - SourceColor, line, DefaultColor, err) + LogError("Error parsing line '%s': %v", line, err) continue } output <- &instruction