spotted.go 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118
  1. // Package spotted answers what effect names other keyboards' definitions have
  2. // been seen to use for a given effect ID.
  3. //
  4. // It is evidence, not an answer, and the difference is the point. No definition
  5. // file, firmware image, protocol query or version number yields the spelling of
  6. // an effect for a board whose manufacturer never wrote one down, and the README
  7. // says so instead of filling the gap. What can be measured is what the boards
  8. // that did write theirs call the same number — and the measurement this package
  9. // carries shows why that is not a name: of the 122 definitions in VIA's
  10. // collection that file an rgb_matrix effect list, 95 are one manufacturer's, so
  11. // the most frequent spelling of nearly every ID is one house style. Worse, the
  12. // disagreement where it exists is not about spelling. Effect 7 is
  13. // `rainbow_moving_chevron` on 95 boards and `cycle_out_in` on 8, which are
  14. // different effects, not two ways of writing one.
  15. //
  16. // So every name here travels with the number of boards that wrote it and the
  17. // manufacturer behind most of them, and a caller that shows one to a user has to
  18. // show those too. A name without them is a guess wearing a count.
  19. package spotted
  20. import (
  21. _ "embed"
  22. "encoding/json"
  23. "strconv"
  24. "sync"
  25. )
  26. //go:embed spotted.json
  27. var raw []byte
  28. // Candidate is one spelling seen for an effect ID, and how many boards wrote it.
  29. type Candidate struct {
  30. Name string `json:"name"`
  31. Count int `json:"count"`
  32. }
  33. // Observation is what was seen for one effect ID under one value key.
  34. type Observation struct {
  35. // Boards is how many definitions listed this ID at all. It is the sample
  36. // size: a name backed by four boards is worth less than one backed by a
  37. // hundred and a half, and that difference is not visible anywhere else.
  38. Boards int `json:"boards"`
  39. // Candidates are the spellings, most frequent first.
  40. Candidates []Candidate `json:"candidates"`
  41. // TopVendor is the manufacturer behind the most of them and TopVendorCount how
  42. // many. A name whose share is nearly all one vendor is that vendor's
  43. // spelling, which is what this says.
  44. TopVendor string `json:"topVendor,omitempty"`
  45. TopVendorCount int `json:"topVendorCount,omitempty"`
  46. }
  47. // report is the generated measurement. Its shape is written by
  48. // internal/spotted/generate and must not be hand-edited.
  49. type report struct {
  50. Source string `json:"source"`
  51. Commit string `json:"commit"`
  52. Definitions int `json:"definitions"`
  53. // EffectBoards is how many of Definitions carried an effect list with names.
  54. // The gap to Definitions is the size of the problem: the rest either name one
  55. // of VIA's built-in menus or do not light up at all.
  56. EffectBoards int `json:"effectBoards"`
  57. Effects map[string]map[string]Observation `json:"effects"`
  58. }
  59. var (
  60. once sync.Once
  61. loaded *report
  62. parse error
  63. )
  64. func get() (*report, error) {
  65. once.Do(func() {
  66. loaded = &report{}
  67. parse = json.Unmarshal(raw, loaded)
  68. })
  69. return loaded, parse
  70. }
  71. // Candidates returns what has been seen for an effect ID under a VIA value key,
  72. // and whether anything was. An ID nothing was seen for is not an error: most IDs
  73. // above 23 are unobserved, because only a handful of boards go that far.
  74. func Candidates(valueKey string, id int) (Observation, bool) {
  75. rep, err := get()
  76. if err != nil {
  77. return Observation{}, false
  78. }
  79. byID, ok := rep.Effects[valueKey]
  80. if !ok {
  81. return Observation{}, false
  82. }
  83. obs, ok := byID[strconv.Itoa(id)]
  84. return obs, ok
  85. }
  86. // Provenance describes what the measurement is a measurement of, for a caller to
  87. // print: where it came from, which commit, and how much of the collection
  88. // carried an effect list at all.
  89. type Provenance struct {
  90. Source string
  91. Commit string
  92. Definitions int
  93. EffectBoards int
  94. }
  95. // About reports where the measurement came from. A collection that moves means a
  96. // name in it ages, and the commit is what says how far.
  97. func About() (Provenance, bool) {
  98. rep, err := get()
  99. if err != nil {
  100. return Provenance{}, false
  101. }
  102. return Provenance{
  103. Source: rep.Source,
  104. Commit: rep.Commit,
  105. Definitions: rep.Definitions,
  106. EffectBoards: rep.EffectBoards,
  107. }, true
  108. }