From 08b7a3f7ae0ec0787393c470ffa5e7f01b702b2d Mon Sep 17 00:00:00 2001 From: "kayos@tcp.direct" Date: Mon, 26 Sep 2022 10:10:17 -0700 Subject: [PATCH] CLI: implement help --- internal/cli/cli.go | 18 ++--------- internal/cli/commands.go | 62 ++++++++++++++++++------------------ internal/cli/completer.go | 67 ++++++++++++++++++++++++++++----------- internal/cli/help.go | 47 +++++++++++++++++++++++++++ internal/cli/process.go | 6 ++-- main.go | 4 +++ 6 files changed, 136 insertions(+), 68 deletions(-) create mode 100644 internal/cli/help.go diff --git a/internal/cli/cli.go b/internal/cli/cli.go index 8a05bec..35b0751 100644 --- a/internal/cli/cli.go +++ b/internal/cli/cli.go @@ -23,7 +23,7 @@ var ( extraDebug = false ) -// Interpret is where we will actuall define our CLICommands +// Interpret is where we will actuall define our Commands func executor(cmd string) { cmd = strings.TrimSpace(cmd) var args = strings.Fields(cmd) @@ -60,7 +60,7 @@ func executor(cmd string) { } case "help": if len(args) < 2 { - getHelp("meta") + getHelp("") return } getHelp(args[len(args)-1]) @@ -77,7 +77,7 @@ func executor(cmd string) { if len(args) == 0 { return } - bcmd, ok := CLICommands[args[0]] + bcmd, ok := Commands[args[0]] if !ok { return } @@ -109,18 +109,6 @@ func executor(cmd string) { } } -func getHelp(target string) { - /* if target == "" { - */ - for _, sug := range suggestions { - for _, su := range sug { - println(su.Text + "(" + strings.Join(su.inner.aliases, ", ") + ")\t" + su.Description) - } - } - return - // } -} - func cmdScan(br *ziggy.Bridge, args []string) error { r, err := br.FindLights() if err != nil { diff --git a/internal/cli/commands.go b/internal/cli/commands.go index 599f393..e493f43 100644 --- a/internal/cli/commands.go +++ b/internal/cli/commands.go @@ -22,22 +22,23 @@ var ( ) type ziggsCommand struct { - reactor reactor - aliases []string - isAlias bool + reactor reactor + description string + aliases []string + isAlias bool } type reactor func(bridge *ziggy.Bridge, args []string) error -func newZiggsCommand( - react reactor, - aliases ...string) *ziggsCommand { +func newZiggsCommand(react reactor, desc string, aliases ...string) *ziggsCommand { ret := &ziggsCommand{ - reactor: react, - isAlias: false, + reactor: react, + aliases: aliases, + isAlias: false, + description: desc, } for _, alias := range aliases { - CLICommands[alias] = &ziggsCommand{ + Commands[alias] = &ziggsCommand{ reactor: react, isAlias: true, } @@ -45,21 +46,7 @@ func newZiggsCommand( return ret } -var CLICommands = make(map[string]*ziggsCommand) - -func init() { - CLICommands["ls"] = newZiggsCommand(cmdList) - CLICommands["schedules"] = newZiggsCommand(cmdSchedules, "lssched", "crontab") - CLICommands["rules"] = newZiggsCommand(cmdRules, "lsrule") - CLICommands["sensors"] = newZiggsCommand(cmdSensors, "lssens") - CLICommands["scenes"] = newZiggsCommand(cmdScenes, "lsscene") - CLICommands["lights"] = newZiggsCommand(cmdLights, "lslight") - CLICommands["groups"] = newZiggsCommand(cmdGroups, "lsgrp") - CLICommands["delete"] = newZiggsCommand(cmdDelete, "del", "remove") - CLICommands["scan"] = newZiggsCommand(cmdScan, "search", "find") - CLICommands["rename"] = newZiggsCommand(cmdRename, "mv") - CLICommands["set"] = newZiggsCommand(cmdSet, "update") -} +var Commands = make(map[string]*ziggsCommand) func cmdList(br *ziggy.Bridge, args []string) error { var runs = []reactor{cmdLights, cmdGroups, cmdScenes, cmdSensors} @@ -91,7 +78,9 @@ func cmdScenes(br *ziggy.Bridge, args []string) error { return err } for _, scene := range scenes { - log.Info().Str("caller", scene.Name).Msgf("%v", spew.Sprint(scene)) + log.Info().Str("caller", strings.Split(br.Host, "://")[1]). + Str("ID", scene.ID).Msgf("Scene: %s", scene.Name) + log.Trace().Msgf("%v", spew.Sprint(scene)) } return nil } @@ -99,8 +88,9 @@ func cmdScenes(br *ziggy.Bridge, args []string) error { func cmdLights(br *ziggy.Bridge, args []string) error { for name, l := range ziggy.GetLightMap() { log.Info(). - Int("ID", l.ID).Str("type", l.ProductName). - Str("model", l.ModelID).Bool("on", l.IsOn()).Msgf("[+] %s", name) + Str("caller", strings.Split(br.Host, "://")[1]).Int("ID", l.ID).Str("type", l.ProductName). + Str("model", l.ModelID).Bool("on", l.IsOn()).Msgf("Light: %s", name) + log.Trace().Msgf("%v", spew.Sprint(l)) } return nil } @@ -114,7 +104,9 @@ func cmdRules(br *ziggy.Bridge, args []string) error { return errors.New("no rules found") } for _, r := range rules { - log.Info().Str("caller", r.Name).Int("ID", r.ID).Msgf("%v", spew.Sprint(r)) + log.Info().Str("caller", strings.Split(br.Host, "://")[1]).Int("ID", r.ID). + Str("status", r.Status).Msgf("Rule: %s", r.Name) + log.Trace().Msgf("%v", spew.Sprint(r)) } return nil } @@ -128,7 +120,9 @@ func cmdSchedules(br *ziggy.Bridge, args []string) error { return errors.New("no schedules found") } for _, s := range schedules { - log.Info().Str("caller", s.Name).Int("ID", s.ID).Msgf("%v", spew.Sprint(s)) + log.Info().Str("caller", strings.Split(br.Host, "://")[1]).Int("ID", s.ID). + Str("desc", s.Description).Msgf("Schedule: %s", s.Name) + log.Trace().Msgf("%v", spew.Sprint(s)) } return nil } @@ -142,7 +136,9 @@ func cmdSensors(br *ziggy.Bridge, args []string) error { return errors.New("no sensors found") } for _, s := range sensors { - log.Info().Str("caller", s.Name).Int("ID", s.ID).Msgf("%v", spew.Sprint(s)) + log.Info().Str("caller", strings.Split(br.Host, "://")[1]).Int("ID", s.ID). + Str("type", s.Type).Msgf("Sensor: %s", s.Name) + log.Trace().Msgf("%v", spew.Sprint(s)) } return nil } @@ -156,8 +152,10 @@ func cmdGroups(br *ziggy.Bridge, args []string) error { return errors.New("no groups found") } for n, g := range groupmap { - log.Info().Str("caller", g.Name).Str("mapname", n).Str("type", g.Type).Int("ID", g.ID). - Str("class", g.Class).Bool("on", g.IsOn()).Msgf("%v", g.GroupState) + log.Info().Str("caller", strings.Split(br.Host, "://")[1]). + Str("mapname", n).Str("type", g.Type).Int("ID", g.ID). + Str("class", g.Class).Bool("on", g.IsOn()).Msgf("Group: %s", g.Name) + log.Trace().Msgf("%v", spew.Sprint(g)) } return nil } diff --git a/internal/cli/completer.go b/internal/cli/completer.go index 0f7a987..5412313 100644 --- a/internal/cli/completer.go +++ b/internal/cli/completer.go @@ -9,7 +9,6 @@ import ( const ( grn = "\033[32m" red = "\033[31m" - ylw = "\033[33m" rst = "\033[0m" ) @@ -17,6 +16,7 @@ type completion struct { cli.Suggest inner *ziggsCommand requires map[int][]string + isAlias bool root bool } @@ -65,30 +65,61 @@ func (c completion) qualifies(line string) bool { return count >= len(c.requires) } -var suggestions map[int][]completion +var suggestions map[int][]*completion func init() { - suggestions = make(map[int][]completion) - suggestions[0] = []completion{ - {Suggest: cli.Suggest{Text: "lights", Description: "print all known lights"}, inner: CLICommands["lights"], root: true}, - {Suggest: cli.Suggest{Text: "groups", Description: "print all known groups"}, inner: CLICommands["groups"], root: true}, - {Suggest: cli.Suggest{Text: "rules", Description: "print all known rules"}, inner: CLICommands["rules"], root: true}, - {Suggest: cli.Suggest{Text: "scenes", Description: "print all known scenes"}, inner: CLICommands["scenes"], root: true}, - {Suggest: cli.Suggest{Text: "schedules", Description: "print all known schedules"}, inner: CLICommands["schedules"], root: true}, - {Suggest: cli.Suggest{Text: "sensors", Description: "print all known sensors"}, inner: CLICommands["sensors"], root: true}, - {Suggest: cli.Suggest{Text: "set", Description: "set state of target"}, inner: CLICommands["set"], root: true}, - {Suggest: cli.Suggest{Text: "create", Description: "create object"}, inner: CLICommands["create"], root: true}, - {Suggest: cli.Suggest{Text: "delete", Description: "delete object"}, inner: CLICommands["delete"], root: true}, - {Suggest: cli.Suggest{Text: "clear", Description: "clear screen"}}, - {Suggest: cli.Suggest{Text: "scan", Description: "scan for bridges"}}, - {Suggest: cli.Suggest{Text: "exit", Description: "exit ziggs"}}, + Commands["ls"] = newZiggsCommand(cmdList, "list all lights, groups, scenes, rules, and schedules") + Commands["schedules"] = newZiggsCommand(cmdSchedules, "list schedules", "lssched", "crontab") + Commands["rules"] = newZiggsCommand(cmdRules, "list rules", "lsrule") + Commands["sensors"] = newZiggsCommand(cmdSensors, "list sensors", "lssens") + Commands["scenes"] = newZiggsCommand(cmdScenes, "list scenes", "lsscene") + Commands["lights"] = newZiggsCommand(cmdLights, "list lights", "lslight") + Commands["groups"] = newZiggsCommand(cmdGroups, "list groups", "lsgrp") + Commands["create"] = newZiggsCommand(cmdCreate, "create a new object in bridge", "new", "mk") + Commands["delete"] = newZiggsCommand(cmdDelete, "delete objects from bridges", "del", "remove") + Commands["scan"] = newZiggsCommand(cmdScan, "scan for bridges/lights/sensors", "search", "find") + Commands["rename"] = newZiggsCommand(cmdRename, "rename object in bridge", "mv") + Commands["set"] = newZiggsCommand(cmdSet, "update object properties in bridge", "update") + initCompletion() +} + +func initCompletion() { + suggestions = make(map[int][]*completion) + suggestions[0] = []*completion{ + {Suggest: cli.Suggest{Text: "lights"}, inner: Commands["lights"]}, + {Suggest: cli.Suggest{Text: "groups"}, inner: Commands["groups"]}, + {Suggest: cli.Suggest{Text: "rules"}, inner: Commands["rules"]}, + {Suggest: cli.Suggest{Text: "scenes"}, inner: Commands["scenes"]}, + {Suggest: cli.Suggest{Text: "schedules"}, inner: Commands["schedules"]}, + {Suggest: cli.Suggest{Text: "sensors"}, inner: Commands["sensors"]}, + {Suggest: cli.Suggest{Text: "set"}, inner: Commands["set"]}, + {Suggest: cli.Suggest{Text: "create"}, inner: Commands["create"]}, + {Suggest: cli.Suggest{Text: "delete"}, inner: Commands["delete"]}, + {Suggest: cli.Suggest{Text: "scan"}, inner: Commands["scan"]}, {Suggest: cli.Suggest{Text: "use", Description: "select bridge to perform actions on"}}, + {Suggest: cli.Suggest{Text: "clear", Description: "clear screen"}}, + {Suggest: cli.Suggest{Text: "exit", Description: "exit ziggs"}}, } + for _, sug := range suggestions[0] { sug.requires = map[int][]string{} sug.root = true + if sug.inner != nil { + sug.Suggest.Description = sug.inner.description + } + if sug.inner != nil && len(sug.inner.aliases) > 0 { + for _, a := range sug.inner.aliases { + suggestions[0] = append(suggestions[0], &completion{ + Suggest: cli.Suggest{Text: a, Description: sug.Description}, + inner: sug.inner, + root: true, + isAlias: true, + }) + } + } } - suggestions[1] = []completion{ + + suggestions[1] = []*completion{ {Suggest: cli.Suggest{Text: "group", Description: "target group"}}, {Suggest: cli.Suggest{Text: "light", Description: "target light"}}, } @@ -96,7 +127,7 @@ func init() { sug.requires = map[int][]string{0: {"delete", "del", "set", "s"}} sug.root = false } - delCompletion := []completion{ + delCompletion := []*completion{ {Suggest: cli.Suggest{Text: "scene", Description: "target scene"}}, {Suggest: cli.Suggest{Text: "schedule", Description: "target schedule"}}, {Suggest: cli.Suggest{Text: "sensor", Description: "target sensor"}}, diff --git a/internal/cli/help.go b/internal/cli/help.go new file mode 100644 index 0000000..0a9ed5e --- /dev/null +++ b/internal/cli/help.go @@ -0,0 +1,47 @@ +package cli + +import ( + "fmt" + "os" + "strings" + "text/tabwriter" +) + +var tabber = tabwriter.NewWriter(os.Stdout, 0, 8, 1, '\t', tabwriter.AlignRight) + +func getHelp(target string) { + if target != "" && target != "meta" { + for _, su := range suggestions[0] { + if strings.Contains(strings.ToLower(su.Text), strings.ToLower(target)) { + println(su.Text + "\t" + su.Description) + } + } + return + } + + for _, su := range suggestions[0] { + var desc string + if su.inner == nil { + desc = su.Description + } else { + desc = su.inner.description + if su.inner.isAlias || su.isAlias { + su.isAlias = true + if extraDebug { + log.Trace().Msgf("alias: %s", su.Text) + } + } + } + if su.isAlias { + continue + } + if extraDebug { + log.Trace().Interface("details", su).Send() + } + _, err := fmt.Fprintln(tabber, su.Text+"\t"+desc) + if err != nil { + panic(err.Error()) + } + } + tabber.Flush() +} diff --git a/internal/cli/process.go b/internal/cli/process.go index b853573..8dc3443 100644 --- a/internal/cli/process.go +++ b/internal/cli/process.go @@ -14,7 +14,7 @@ func processGroups(grps map[string]*huego.Group) { suffix = " (" + g.Type + ")" } suggestions[2] = append(suggestions[2], - completion{ + &completion{ Suggest: cli.Suggest{ Text: grp, Description: "Group" + suffix, @@ -35,7 +35,7 @@ func processLights() { suffix = " (" + l.Type + ")" } suggestions[2] = append(suggestions[2], - completion{ + &completion{ Suggest: cli.Suggest{ Text: lt, Description: "Light" + suffix, @@ -52,7 +52,7 @@ func processLights() { func processBridges() { for brd, b := range ziggy.Lucifer.Bridges { suggestions[1] = append(suggestions[1], - completion{ + &completion{ Suggest: cli.Suggest{ Text: brd, Description: "Bridge: " + b.Host, diff --git a/main.go b/main.go index ea087bd..8699504 100644 --- a/main.go +++ b/main.go @@ -125,6 +125,10 @@ func main() { data.Start() defer data.Close() + if len(os.Args) < 2 { + cli.StartCLI() + } + for _, arg := range os.Args { switch arg { case "discover":