diff --git a/cmd/breathe.go b/cmd/breathe.go index 0c382c1..d5beafa 100644 --- a/cmd/breathe.go +++ b/cmd/breathe.go @@ -2,6 +2,7 @@ package lumecmd import ( "flag" + "fmt" "git.kill0.net/chill9/lifx-go" ) @@ -102,7 +103,7 @@ func BreatheCmd(ctx Context) (int, error) { } p = NewPrinter(format) - p.Results(r.Results) + fmt.Print(p.Results(r.Results)) return ExitSuccess, nil } diff --git a/cmd/help.go b/cmd/help.go index ad5ff60..f4a8461 100644 --- a/cmd/help.go +++ b/cmd/help.go @@ -3,6 +3,7 @@ package lumecmd import ( "fmt" "sort" + "strings" ) func NewCmdHelp() Command { @@ -16,15 +17,21 @@ func NewCmdHelp() Command { func HelpCmd(ctx Context) (int, error) { if len(ctx.Args) == 0 { - printHelp(commandRegistry) + fmt.Print(printHelp(commandRegistry)) } else if len(ctx.Args) >= 1 { - printCmdHelp(ctx.Args[0]) + if cmdHelp, err := printCmdHelp(ctx.Args[0]); err == nil { + fmt.Print(cmdHelp) + } else { + fmt.Print(err) + } } return ExitSuccess, nil } -func printHelp(commands map[string]Command) { +func printHelp(commands map[string]Command) string { + var b strings.Builder + var maxLen, cmdLen int var keys []string @@ -36,34 +43,45 @@ func printHelp(commands map[string]Command) { } } - fmt.Printf("usage:\n lume []") - fmt.Println() - fmt.Println("\ncommands:") + fmt.Fprintf(&b, "usage:\n lume []") + fmt.Fprintln(&b) + fmt.Fprintln(&b, "\ncommands:") sort.Strings(keys) + for _, k := range keys { c := commands[k] - fmt.Printf(" %-*s %s\n", maxLen, c.Name, c.Short) + fmt.Fprintf(&b, " %-*s %s\n", maxLen, c.Name, c.Short) } + + return b.String() } -func printCmdHelp(name string) error { +func printCmdHelp(name string) (string, error) { + var b strings.Builder + subCmd, ok := commandRegistry[name] + if !ok { - return fmt.Errorf("unknown commnnd: %s\n", name) + return "", fmt.Errorf("unknown commnnd: %s\n", name) } if subCmd.Use != "" { - fmt.Printf("usage:\n lume %s %s\n", subCmd.Name, subCmd.Use) + fmt.Fprintf(&b, "usage:\n lume %s %s\n", subCmd.Name, subCmd.Use) } else { - fmt.Printf("usage:\n lume %s\n", subCmd.Name) + fmt.Fprintf(&b, "usage:\n lume %s\n", subCmd.Name) } if subCmd.Flags != nil { - fmt.Println() - fmt.Print("flags:\n") + out := subCmd.Flags.Output() + defer subCmd.Flags.SetOutput(out) + + fmt.Fprintln(&b) + fmt.Fprint(&b, "flags:\n") + + subCmd.Flags.SetOutput(&b) subCmd.Flags.PrintDefaults() } - return nil + return b.String(), nil } diff --git a/cmd/ls.go b/cmd/ls.go index 4420675..82665fc 100644 --- a/cmd/ls.go +++ b/cmd/ls.go @@ -2,6 +2,7 @@ package lumecmd import ( "flag" + "fmt" ) func NewCmdLs() Command { @@ -42,7 +43,7 @@ func LsCmd(ctx Context) (int, error) { } p = NewPrinter(format) - p.Lights(lights) + fmt.Print(p.Lights(lights)) return ExitSuccess, nil } diff --git a/cmd/poweroff.go b/cmd/poweroff.go index e4f1c57..7b3f768 100644 --- a/cmd/poweroff.go +++ b/cmd/poweroff.go @@ -2,6 +2,7 @@ package lumecmd import ( "flag" + "fmt" "git.kill0.net/chill9/lifx-go" ) @@ -48,7 +49,7 @@ func PoweroffCmd(ctx Context) (int, error) { } p = NewPrinter(format) - p.Results(r.Results) + fmt.Print(p.Results(r.Results)) return ExitSuccess, nil } diff --git a/cmd/poweron.go b/cmd/poweron.go index 0e5b0cc..6e35e87 100644 --- a/cmd/poweron.go +++ b/cmd/poweron.go @@ -2,6 +2,7 @@ package lumecmd import ( "flag" + "fmt" "git.kill0.net/chill9/lifx-go" ) @@ -48,7 +49,7 @@ func PoweronCmd(ctx Context) (int, error) { } p = NewPrinter(format) - p.Results(r.Results) + fmt.Print(p.Results(r.Results)) return ExitSuccess, nil } diff --git a/cmd/print.go b/cmd/print.go index 942ac1e..4f4e9df 100644 --- a/cmd/print.go +++ b/cmd/print.go @@ -2,7 +2,8 @@ package lumecmd import ( "fmt" - "os" + "io" + "strings" "time" "git.kill0.net/chill9/lifx-go" @@ -11,8 +12,8 @@ import ( ) type Printer interface { - Results(results []lifx.Result) - Lights(lights []lifx.Light) + Results(results []lifx.Result) string + Lights(lights []lifx.Light) string } type defaultPrinter struct{} @@ -28,17 +29,19 @@ func NewPrinter(format string) Printer { } } -func (dp *defaultPrinter) Results(results []lifx.Result) { +func (dp *defaultPrinter) Results(results []lifx.Result) string { + var b strings.Builder + sortResults(results) - table := tablewriter.NewWriter(os.Stdout) + table := tablewriter.NewWriter(&b) _, rows := makeResultsTable(results) for _, v := range rows { table.Append(v) } - fmt.Printf("total %d\n", len(results)) + fmt.Fprintf(&b, "total %d\n", len(results)) table.SetAlignment(tablewriter.ALIGN_LEFT) table.SetAutoWrapText(false) table.SetBorder(false) @@ -49,12 +52,16 @@ func (dp *defaultPrinter) Results(results []lifx.Result) { table.SetRowSeparator("") table.SetTablePadding(" ") table.Render() + + return b.String() } -func (tp *tablePrinter) Results(results []lifx.Result) { +func (tp *tablePrinter) Results(results []lifx.Result) string { + var b strings.Builder + sortResults(results) - table := tablewriter.NewWriter(os.Stdout) + table := tablewriter.NewWriter(&b) hdr, rows := makeResultsTable(results) for _, v := range rows { @@ -63,19 +70,23 @@ func (tp *tablePrinter) Results(results []lifx.Result) { table.SetHeader(hdr) table.Render() + + return b.String() } -func (dp *defaultPrinter) Lights(lights []lifx.Light) { +func (dp *defaultPrinter) Lights(lights []lifx.Light) string { + var b strings.Builder + sortLights(lights) - table := tablewriter.NewWriter(os.Stdout) + table := tablewriter.NewWriter(&b) _, rows := makeLightsTable(lights) for _, v := range rows { table.Append(v) } - fmt.Printf("total %d\n", len(lights)) + fmt.Fprintf(&b, "total %d\n", len(lights)) table.SetAlignment(tablewriter.ALIGN_LEFT) table.SetAutoWrapText(false) table.SetBorder(false) @@ -86,12 +97,16 @@ func (dp *defaultPrinter) Lights(lights []lifx.Light) { table.SetRowSeparator("") table.SetTablePadding(" ") table.Render() + + return b.String() } -func (tp *tablePrinter) Lights(lights []lifx.Light) { +func (tp *tablePrinter) Lights(lights []lifx.Light) string { + var b strings.Builder + sortLights(lights) - table := tablewriter.NewWriter(os.Stdout) + table := tablewriter.NewWriter(&b) hdr, rows := makeLightsTable(lights) for _, v := range rows { @@ -100,6 +115,8 @@ func (tp *tablePrinter) Lights(lights []lifx.Light) { table.SetHeader(hdr) table.Render() + + return b.String() } func ColorizeIndicator(s string) string { @@ -138,6 +155,11 @@ func PrintfWithIndent(indent int, format string, a ...interface{}) (n int, err e return fmt.Printf(format, a...) } +func FprintfWithIndent(w io.Writer, indent int, format string, a ...interface{}) (n int, err error) { + format = fmt.Sprintf("%*s%s", indent, "", format) + return fmt.Fprintf(w, format, a...) +} + func makeLightsTable(lights []lifx.Light) (hdr []string, rows [][]string) { hdr = []string{"", "ID", "Location", "Group", "Label", "Last Seen", "Power"} diff --git a/cmd/setcolor.go b/cmd/setcolor.go index 5d0c04d..eade8a5 100644 --- a/cmd/setcolor.go +++ b/cmd/setcolor.go @@ -130,7 +130,7 @@ func SetColorCmd(ctx Context) (int, error) { if !fast { p = NewPrinter(format) - p.Results(r.Results) + fmt.Print(p.Results(r.Results)) } return ExitSuccess, nil diff --git a/cmd/setstate.go b/cmd/setstate.go index 79033fc..ff6e279 100644 --- a/cmd/setstate.go +++ b/cmd/setstate.go @@ -2,6 +2,7 @@ package lumecmd import ( "flag" + "fmt" "git.kill0.net/chill9/lifx-go" ) @@ -96,7 +97,7 @@ func SetStateCmd(ctx Context) (int, error) { if !fast { p = NewPrinter(format) - p.Results(r.Results) + fmt.Print(p.Results(r.Results)) } return ExitSuccess, nil diff --git a/cmd/setwhite.go b/cmd/setwhite.go index d279976..b893876 100644 --- a/cmd/setwhite.go +++ b/cmd/setwhite.go @@ -2,6 +2,7 @@ package lumecmd import ( "flag" + "fmt" "git.kill0.net/chill9/lifx-go" ) @@ -114,7 +115,7 @@ func SetWhiteCmd(ctx Context) (int, error) { if !fast { p = NewPrinter(format) - p.Results(r.Results) + fmt.Print(p.Results(r.Results)) } return ExitSuccess, nil diff --git a/cmd/show.go b/cmd/show.go index 975b163..55d0c9c 100644 --- a/cmd/show.go +++ b/cmd/show.go @@ -3,6 +3,7 @@ package lumecmd import ( "flag" "fmt" + "strings" ) const Tabstop int = 2 @@ -26,6 +27,8 @@ func NewCmdShow() Command { func ShowCmd(ctx Context) (int, error) { var indent int + var b strings.Builder + c := ctx.Client selector := ctx.Flags.String("selector") lights, err := c.ListLights(selector) @@ -38,7 +41,8 @@ func ShowCmd(ctx Context) (int, error) { for i, l := range lights { indent = 0 - fmt.Printf( + fmt.Fprintf( + &b, "%s Light ID: %s, %s, Power: %s\n", ColorizeIndicator(l.Power), l.Id, @@ -46,41 +50,43 @@ func ShowCmd(ctx Context) (int, error) { ColorizePower(l.Power), ) indent += Tabstop + 2 - PrintfWithIndent(indent, "Label: %s, ID: %s\n", l.Label, l.Id) - PrintfWithIndent(indent, "UUID: %s\n", l.UUID) - PrintfWithIndent(indent, "Location: %s, ID: %s\n", l.Location.Name, l.Location.Id) - PrintfWithIndent(indent, "Group: %s, ID: %s\n", l.Group.Name, l.Group.Id) - PrintfWithIndent(indent, "Color: Hue: %.1f, Saturation: %.1f%%, Kelvin: %d\n", + FprintfWithIndent(&b, indent, "Label: %s, ID: %s\n", l.Label, l.Id) + FprintfWithIndent(&b, indent, "UUID: %s\n", l.UUID) + FprintfWithIndent(&b, indent, "Location: %s, ID: %s\n", l.Location.Name, l.Location.Id) + FprintfWithIndent(&b, indent, "Group: %s, ID: %s\n", l.Group.Name, l.Group.Id) + FprintfWithIndent(&b, indent, "Color: Hue: %.1f, Saturation: %.1f%%, Kelvin: %d\n", *l.Color.H, *l.Color.S, *l.Color.K) - PrintfWithIndent(indent, "Brightness: %.1f%%\n", l.Brightness*100) + FprintfWithIndent(&b, indent, "Brightness: %.1f%%\n", l.Brightness*100) if l.Effect != "" { - PrintfWithIndent(indent, "Effect: %s\n", l.Effect) + FprintfWithIndent(&b, indent, "Effect: %s\n", l.Effect) } - PrintfWithIndent(indent, "Product: %s\n", l.Product.Name) - PrintfWithIndent(indent, "Capabilities: ") - fmt.Printf("Color: %s, ", YesNo(l.Product.Capabilities.HasColor)) - fmt.Printf("Variable Color Temp: %s, ", YesNo(l.Product.Capabilities.HasVariableColorTemp)) - fmt.Printf("IR: %s, ", YesNo(l.Product.Capabilities.HasIR)) - fmt.Printf("Chain: %s, ", YesNo(l.Product.Capabilities.HasChain)) - fmt.Printf("Multizone: %s, ", YesNo(l.Product.Capabilities.HasMultizone)) - fmt.Printf("Min Kelvin: %.1f, ", l.Product.Capabilities.MinKelvin) - fmt.Printf("Max Kelvin: %.1f ", l.Product.Capabilities.MaxKelvin) - fmt.Println() + FprintfWithIndent(&b, indent, "Product: %s\n", l.Product.Name) + FprintfWithIndent(&b, indent, "Capabilities: ") + fmt.Fprintf(&b, "Color: %s, ", YesNo(l.Product.Capabilities.HasColor)) + fmt.Fprintf(&b, "Variable Color Temp: %s, ", YesNo(l.Product.Capabilities.HasVariableColorTemp)) + fmt.Fprintf(&b, "IR: %s, ", YesNo(l.Product.Capabilities.HasIR)) + fmt.Fprintf(&b, "Chain: %s, ", YesNo(l.Product.Capabilities.HasChain)) + fmt.Fprintf(&b, "Multizone: %s, ", YesNo(l.Product.Capabilities.HasMultizone)) + fmt.Fprintf(&b, "Min Kelvin: %.1f, ", l.Product.Capabilities.MinKelvin) + fmt.Fprintf(&b, "Max Kelvin: %.1f ", l.Product.Capabilities.MaxKelvin) + fmt.Fprintln(&b) // List applicable selectors (most to least specific) - PrintfWithIndent(indent, "Selectors:\n") + FprintfWithIndent(&b, indent, "Selectors:\n") indent += Tabstop - PrintfWithIndent(indent, "id:%s\n", l.Id) - PrintfWithIndent(indent, "label:%s\n", l.Label) - PrintfWithIndent(indent, "group_id:%s\n", l.Group.Id) - PrintfWithIndent(indent, "group:%s\n", l.Group.Name) - PrintfWithIndent(indent, "location_id:%s\n", l.Location.Id) - PrintfWithIndent(indent, "location:%s\n", l.Location.Name) + FprintfWithIndent(&b, indent, "id:%s\n", l.Id) + FprintfWithIndent(&b, indent, "label:%s\n", l.Label) + FprintfWithIndent(&b, indent, "group_id:%s\n", l.Group.Id) + FprintfWithIndent(&b, indent, "group:%s\n", l.Group.Name) + FprintfWithIndent(&b, indent, "location_id:%s\n", l.Location.Id) + FprintfWithIndent(&b, indent, "location:%s\n", l.Location.Name) indent -= Tabstop - PrintfWithIndent(indent, "Last Seen: %s (%.1fs ago)\n", l.LastSeen, l.SecondsLastSeen) + FprintfWithIndent(&b, indent, "Last Seen: %s (%.1fs ago)\n", l.LastSeen, l.SecondsLastSeen) if i < len(lights)-1 { - fmt.Println() + fmt.Fprintln(&b) } + + fmt.Print(b.String()) } return ExitSuccess, nil } diff --git a/cmd/toggle.go b/cmd/toggle.go index 6c6e6ac..ef2945f 100644 --- a/cmd/toggle.go +++ b/cmd/toggle.go @@ -2,6 +2,7 @@ package lumecmd import ( "flag" + "fmt" ) func NewCmdToggle() Command { @@ -45,7 +46,7 @@ func ToggleCmd(ctx Context) (int, error) { } p = NewPrinter(format) - p.Results(r.Results) + fmt.Print(p.Results(r.Results)) return ExitSuccess, nil } diff --git a/cmd/validate.go b/cmd/validate.go index 5eefb19..2d100ad 100644 --- a/cmd/validate.go +++ b/cmd/validate.go @@ -4,6 +4,7 @@ import ( "errors" "flag" "fmt" + "strings" "git.kill0.net/chill9/lifx-go" ) @@ -23,10 +24,11 @@ func NewCmdValidate() Command { } func ValidateCmd(ctx Context) (int, error) { + var b strings.Builder c := ctx.Client if len(ctx.Args) != 1 { - printCmdHelp(ctx.Name) + fmt.Print(printCmdHelp(ctx.Name)) return ExitFailure, nil } @@ -38,11 +40,12 @@ func ValidateCmd(ctx Context) (int, error) { } if validColor, ok := i.(*lifx.HSBKColor); ok { - fmt.Print(validColor) + fmt.Fprintln(&b, validColor) } else { return ExitFailure, errors.New("go type %T but wanted *HSBKColor") } - fmt.Println() + + fmt.Print(b.String()) return ExitSuccess, nil }