From e22f177efd467fd78e3f656f2b8db46e53d45f81 Mon Sep 17 00:00:00 2001 From: Magnus Reyes Date: Fri, 26 Jul 2024 10:00:40 -0400 Subject: [PATCH] Add color formatting to log output --- cmd/cmd.go | 27 +++++++++++++++++++++----- go.mod | 9 ++++++++- go.sum | 13 +++++++++++++ lib/imgchop/imgchop.go | 44 +++++++++++++++++++++--------------------- lib/logger/logger.go | 42 +++++++++++++++++++++++++++++++++++++--- 5 files changed, 104 insertions(+), 31 deletions(-) diff --git a/cmd/cmd.go b/cmd/cmd.go index 2f58625..489d563 100644 --- a/cmd/cmd.go +++ b/cmd/cmd.go @@ -5,6 +5,7 @@ import ( "path/filepath" "sync" + "github.com/fatih/color" "github.com/mass8326/imgchop/lib/imgchop" "github.com/mass8326/imgchop/lib/logger" "github.com/spf13/cobra" @@ -24,15 +25,31 @@ func Execute() { logger.Exit(1) } - warning := false + c := make(chan logger.Message, 1) var wg sync.WaitGroup + wg.Add(len(args)) for _, file := range args { - wg.Add(1) - go imgchop.Process(&wg, &warning, file, *flags.intelligent) + go imgchop.Process(&wg, c, file, *flags.intelligent) } - wg.Wait() - if warning { + messaged := false + go func() { + for msg := range c { + messaged = true + var level string + switch msg.Level { + case "warn": + level = color.New(color.FgYellow).Sprintf("[%s]", msg.Level) + case "info": + level = color.New(color.FgCyan).Sprintf("[%s]", msg.Level) + } + logger.RawLogger.Printf("%s %s", level, color.HiBlackString(msg.Source)) + logger.RawLogger.Println(msg.Msg) + } + }() + + wg.Wait() + if messaged { logger.Exit(0) } }, diff --git a/go.mod b/go.mod index d7f8823..4ba2915 100644 --- a/go.mod +++ b/go.mod @@ -3,8 +3,15 @@ module github.com/mass8326/imgchop go 1.22.3 require ( + github.com/fatih/color v1.17.0 github.com/inconshreveable/mousetrap v1.1.0 github.com/spf13/cobra v1.8.1 + golang.org/x/term v0.22.0 ) -require github.com/spf13/pflag v1.0.5 // indirect +require ( + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/spf13/pflag v1.0.5 // indirect + golang.org/x/sys v0.22.0 // indirect +) diff --git a/go.sum b/go.sum index 912390a..14cee8d 100644 --- a/go.sum +++ b/go.sum @@ -1,10 +1,23 @@ github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4= +github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= +golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.22.0 h1:BbsgPEJULsl2fV/AT3v15Mjva5yXKQDyKf+TbDz7QJk= +golang.org/x/term v0.22.0/go.mod h1:F3qCibpT5AMpCRfhfT53vVJwhLtIVHhB9XDjfFvnMI4= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/lib/imgchop/imgchop.go b/lib/imgchop/imgchop.go index b58b916..36acb59 100644 --- a/lib/imgchop/imgchop.go +++ b/lib/imgchop/imgchop.go @@ -13,55 +13,55 @@ import ( "github.com/mass8326/imgchop/lib/util" ) -func Process(wg *sync.WaitGroup, warning *bool, name string, intelligent bool) { +func Process(wg *sync.WaitGroup, c chan logger.Message, name string, intelligent bool) { defer wg.Done() - warn := func(msg string) { - logger.Logger.Printf("%s (%s)\n", msg, name) - *warning = true + lgr := logger.Logger{ + Messages: c, + Source: name, } fd, err := os.Open(name) if err != nil { - warn("Unable to open path!") + lgr.Warn("Unable to open path!") return } defer fd.Close() stat, err := fd.Stat() if err != nil { - warn("Unable to stat path!") + lgr.Warn("Unable to stat path!") return } if stat.IsDir() { entries, err := fd.ReadDir(0) if err != nil { - warn("Unable to read directory!") + lgr.Warn("Unable to read directory!") return } + wg.Add(len(entries)) for _, entry := range entries { - wg.Add(1) - go Process(wg, warning, filepath.Join(name, entry.Name()), true) + go Process(wg, c, filepath.Join(name, entry.Name()), true) } } else { wg.Add(1) - Crop(wg, warning, fd, intelligent) + Crop(wg, c, fd, intelligent) } } -func Crop(wg *sync.WaitGroup, warning *bool, fd *os.File, intelligent bool) { +func Crop(wg *sync.WaitGroup, c chan logger.Message, fd *os.File, intelligent bool) { defer wg.Done() fname := fd.Name() - warn := func(msg string) { - logger.Logger.Printf("%s (%s)\n", msg, fname) - *warning = true + lgr := logger.Logger{ + Messages: c, + Source: fname, } input, _, err := image.Decode(fd) if err != nil { - warn("Unable to decode image!") + lgr.Warn("Unable to decode image!") return } @@ -71,7 +71,7 @@ func Crop(wg *sync.WaitGroup, warning *bool, fd *os.File, intelligent bool) { } croppable, ok := input.(Croppable) if !ok { - warn("Image does not support cropping!") + lgr.Warn("Image does not support cropping!") return } @@ -79,13 +79,13 @@ func Crop(wg *sync.WaitGroup, warning *bool, fd *os.File, intelligent bool) { width, height := target.Dx(), target.Dy() switch { case width == height: - warn("Image is already a square!") + lgr.Warn("Image is already a square!") return case width&1 == 1: - warn("Image width is an odd number and the image cannot be cropped into a square!") + lgr.Warn("Image width is an odd number and the image cannot be cropped into a square!") return case height&1 == 1: - warn("Image height is an odd number and the image cannot be cropped into a square!") + lgr.Warn("Image height is an odd number and the image cannot be cropped into a square!") return } @@ -110,7 +110,7 @@ func Crop(wg *sync.WaitGroup, warning *bool, fd *os.File, intelligent bool) { minimum := min(r, g, b) lum := float32(maximum-minimum) * 100 / 255 / 2 if lum > 20 { - warn(fmt.Sprintf("Image did not pass intelligent filter (%f%% pixel luminosity at [%d, %d])", lum, x, y)) + lgr.Info(fmt.Sprintf("Image did not pass intelligent filter (%f%% pixel luminosity at [%d, %d])", lum, x, y)) return } @@ -121,7 +121,7 @@ func Crop(wg *sync.WaitGroup, warning *bool, fd *os.File, intelligent bool) { avg := float32(maximums-minimums) / 2 / float32(bounds.Dx()) / float32(bounds.Dy()) lum := avg * 100 / 255 if lum > 1 { - warn(fmt.Sprintf("Image did not pass intelligent filter (%f%% average luminosity)", lum)) + lgr.Info(fmt.Sprintf("Image did not pass intelligent filter (%f%% average luminosity)", lum)) return } } @@ -140,7 +140,7 @@ func Crop(wg *sync.WaitGroup, warning *bool, fd *os.File, intelligent bool) { basename := strings.TrimSuffix(filepath.Base(fname), filepath.Ext(fname)) err = writeImage(result, filepath.Join(dir, basename+"-imgchop.png")) if err != nil { - warn("Could not save output image!") + lgr.Warn("Could not save output image!") } } diff --git a/lib/logger/logger.go b/lib/logger/logger.go index 3483aab..d489640 100644 --- a/lib/logger/logger.go +++ b/lib/logger/logger.go @@ -7,14 +7,50 @@ import ( "github.com/mass8326/imgchop/lib/util" "github.com/spf13/cobra" + "golang.org/x/term" ) -var Logger = log.New(os.Stderr, "", 0) +var RawLogger = log.New(os.Stderr, "", 0) + +type Message struct { + Level string + Source string + Msg string +} + +type Logger struct { + Messages chan Message + Source string +} + +func (lg Logger) Warn(msg string) { + lg.Messages <- Message{ + Level: "warn", + Source: lg.Source, + Msg: msg, + } +} + +func (lg Logger) Info(msg string) { + lg.Messages <- Message{ + Level: "info", + Source: lg.Source, + Msg: msg, + } +} func Exit(code int) { if util.StartedByExplorer { - Logger.Println("\nPress 'Enter' to exit...") - bufio.NewReader(os.Stdin).ReadBytes('\n') + previous, err := term.MakeRaw(int(os.Stdin.Fd())) + if err != nil { + RawLogger.Println("\nPress 'Enter' to exit...") + bufio.NewReader(os.Stdin).ReadBytes('\n') + } else { + defer term.Restore(int(os.Stdin.Fd()), previous) + RawLogger.Println("\nPress any key to exit...") + b := make([]byte, 1) + os.Stdin.Read(b) + } } os.Exit(code) }