output.go 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115
  1. package main
  2. import (
  3. "fmt"
  4. "io"
  5. "github.com/spf13/cobra"
  6. "netdome.biz/paul/qmk-rgb/internal/device"
  7. intrgb "netdome.biz/paul/qmk-rgb/internal/rgb"
  8. "netdome.biz/paul/qmk-rgb/internal/via"
  9. )
  10. // The commands below print text, because that is what a person reads, and JSON
  11. // only when it is asked for. The flag is persistent, so it reads the same
  12. // everywhere: `qmk-rgb-tool --json info` and `qmk-rgb-tool info --json` are the
  13. // same command. An agent that parses the output passes it; a person does not
  14. // have to learn a second shape for the same information.
  15. var jsonOutput bool
  16. func addJSONFlag(cmd *cobra.Command) {
  17. cmd.PersistentFlags().BoolVar(&jsonOutput, "json", false, "Print JSON instead of text")
  18. }
  19. // printEffectListText writes one effect per line, its ID in brackets, under a
  20. // heading per channel. The ID is in the line because it is what a user needs to
  21. // check a name against the register, and what `mode <index>` takes.
  22. func printEffectListText(out io.Writer, catalog *intrgb.Catalog, channels []via.Channel, display map[uint16]string, source string) error {
  23. for _, ch := range channels {
  24. effects := catalog.Effects(ch)
  25. if len(effects) == 0 {
  26. continue
  27. }
  28. fmt.Fprintf(out, "%s (%s)\n", channelName(ch, display), ch.Subsystem())
  29. for _, e := range effects {
  30. fmt.Fprintf(out, " %s (%d)\n", e.Name, e.ID)
  31. }
  32. }
  33. if source != "" {
  34. fmt.Fprintf(out, "\nNames from %s\n", source)
  35. }
  36. return nil
  37. }
  38. // printZoneInfoText writes one line per zone, which is what a person compares.
  39. func printZoneInfoText(out io.Writer, state infoOutput) error {
  40. fmt.Fprintf(out, "%s\n", state.Mode)
  41. for _, z := range state.Zones {
  42. enabled := "off"
  43. if z.Enabled {
  44. enabled = "on"
  45. }
  46. fmt.Fprintf(out, " %-12s %-4s %-28s brightness %3d speed %3d color %s\n",
  47. z.Zone, enabled, z.Effect, z.Brightness, z.Speed, formatInfoColor(z.Color))
  48. }
  49. return nil
  50. }
  51. // formatInfoColor renders a hue and saturation the way the color command takes
  52. // them, so a value read from the keyboard can be written back unchanged.
  53. func formatInfoColor(c infoColor) string {
  54. return fmt.Sprintf("hsv:%d,%d", c.Hue, c.Saturation)
  55. }
  56. // printKeyboardInfoText writes one line per keyboard, with the index --device
  57. // takes, because the index is the only part a user has to act on.
  58. func printKeyboardInfoText(out io.Writer, devices []keyboardLine) error {
  59. if len(devices) == 0 {
  60. fmt.Fprintln(out, "No QMK keyboard found.")
  61. return nil
  62. }
  63. for _, d := range devices {
  64. known := ""
  65. if d.Known {
  66. known = " (known model)"
  67. }
  68. fmt.Fprintf(out, "%d %s 0x%04X/0x%04X%s\n", d.Index, lineName(d), d.VendorID, d.ProductID, known)
  69. }
  70. return nil
  71. }
  72. // keyboardLine is one keyboard as the info command reports it. Its channels are
  73. // deliberately absent: this command does not open the board, so it cannot know
  74. // which ones it has, and reporting the ones a definition names would claim more
  75. // than it knows.
  76. type keyboardLine struct {
  77. Index int `json:"index"`
  78. Path string `json:"path"`
  79. VendorID uint16 `json:"vendorId"`
  80. ProductID uint16 `json:"productId"`
  81. Name string `json:"name"`
  82. Known bool `json:"known"`
  83. }
  84. func lineName(d keyboardLine) string {
  85. if d.Name != "" {
  86. return d.Name
  87. }
  88. return "unknown model"
  89. }
  90. // describeDevices turns discovered devices into what the info command reports.
  91. func describeDevices(devices []device.Device) []keyboardLine {
  92. lines := make([]keyboardLine, 0, len(devices))
  93. for _, d := range devices {
  94. lines = append(lines, keyboardLine{
  95. Index: d.Index,
  96. Path: d.Path,
  97. VendorID: d.VendorID,
  98. ProductID: d.ProductID,
  99. Name: lineName(keyboardLine{Name: d.Name}),
  100. Known: d.Known,
  101. })
  102. }
  103. return lines
  104. }