datadir.go 3.7 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798
  1. package main
  2. import (
  3. "os"
  4. "path/filepath"
  5. "strings"
  6. intrgb "netdome.biz/paul/qmk-rgb/internal/rgb"
  7. )
  8. // The tool's own files live under one directory per platform, the one
  9. // os.UserConfigDir names, so they are the same on Linux, macOS and Windows and a
  10. // hardcoded ~/.config is not.
  11. //
  12. // There is one such directory and both kinds of file are read from it and
  13. // written to it. There used to be a search: definitions and profiles were looked
  14. // for next to the executable and in the working directory before the user's, and
  15. // that was wrong for both. A definition is written by `keyboard fetch` and read
  16. // by every command, so a search path can hide the file the user just fetched. A
  17. // profile is written to the user's directory, so a search path for reading can
  18. // find a same-named file in a checkout instead and load that one — the same name,
  19. // different content, and which one you get depends on where you stand.
  20. const dataDirName = "qmk-rgb-tool"
  21. // userConfigDir is a seam: os.UserConfigDir is the platform's own answer, and a
  22. // test cannot rely on which platform it runs on.
  23. var userConfigDir = os.UserConfigDir
  24. // The two overrides short-circuit the lookup, which is how the tests point the
  25. // lookup at a temporary directory. Empty means resolve it.
  26. var (
  27. profilesDirOverride string
  28. definitionsDirOverride string
  29. )
  30. // userDataDir is this tool's directory under the platform's configuration
  31. // directory: ~/.config/qmk-rgb-tool on Linux, ~/Library/Application
  32. // Support/qmk-rgb-tool on macOS, %AppData%\qmk-rgb-tool on Windows. A hardcoded
  33. // ~/.config would be wrong on two of the three.
  34. func userDataDir() string {
  35. base, err := userConfigDir()
  36. if err != nil || base == "" {
  37. return dataDirName
  38. }
  39. return filepath.Join(base, dataDirName)
  40. }
  41. // definitionsPath is where the definition files live: the per-user directory,
  42. // and nowhere else. A `keyboard fetch` writes here, so anything else would mean
  43. // the file a fetch produced is not the file the next command reads. The
  44. // definitions built into the binary are consulted after this one, by
  45. // candidateDefinitions in catalog.go.
  46. func definitionsPath() string {
  47. if definitionsDirOverride != "" {
  48. return definitionsDirOverride
  49. }
  50. return filepath.Join(userDataDir(), intrgb.DefinitionsDir)
  51. }
  52. // profilesPath is where the profiles live: the per-user directory, and nowhere
  53. // else. Reading and writing are the same place, so `save lava` followed by
  54. // `load lava` finds what was just written, and a same-named file in a checkout
  55. // cannot be loaded in place of it.
  56. func profilesPath() string {
  57. if profilesDirOverride != "" {
  58. return profilesDirOverride
  59. }
  60. return filepath.Join(userDataDir(), "profiles")
  61. }
  62. // profilesWritePath is where a saved profile goes, which is not profilesPath.
  63. // Writing to either of those would put a file the user did not ask for into a
  64. // $GOPATH/bin that a reinstall replaces, or leave an untracked profiles/lava.json
  65. // in someone's repository. So a save always goes to the per-user directory.
  66. func profilesWritePath() string {
  67. if profilesDirOverride != "" {
  68. return profilesDirOverride
  69. }
  70. return filepath.Join(userDataDir(), "profiles")
  71. }
  72. // ensureDataDir returns a directory and creates it, for the commands that write.
  73. func ensureDataDir(path string) (string, error) {
  74. if err := os.MkdirAll(path, 0o755); err != nil {
  75. return "", err
  76. }
  77. return path, nil
  78. }
  79. // describeDataDir annotates a path so a message that names one says which place
  80. // it is: every directory this tool writes to is under the user's, and a bare
  81. // path does not say that.
  82. func describeDataDir(path string) string {
  83. if user := userDataDir(); path == user || strings.HasPrefix(path, user+string(filepath.Separator)) {
  84. return path + " (your user directory)"
  85. }
  86. return path
  87. }