os: add UserCacheDir and UserConfigDir

This commit is contained in:
Jake Bailey
2026-04-14 10:00:29 -07:00
committed by Ron Evans
parent 0e84235f9a
commit eabd1ddce0
2 changed files with 138 additions and 0 deletions
+102
View File
@@ -400,6 +400,108 @@ func UserHomeDir() (string, error) {
return "", errors.New(enverr + " is not defined")
}
// UserCacheDir returns the default root directory to use for user-specific
// cached data. Users should create their own application-specific subdirectory
// within this one and use that.
//
// On Unix systems, it returns $XDG_CACHE_HOME as specified by
// https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html if
// non-empty, else $HOME/.cache.
// On Darwin, it returns $HOME/Library/Caches.
// On Windows, it returns %LocalAppData%.
// On Plan 9, it returns $home/lib/cache.
//
// If the location cannot be determined (for example, $HOME is not defined),
// then it will return an error.
func UserCacheDir() (string, error) {
var dir string
switch runtime.GOOS {
case "windows":
dir = Getenv("LocalAppData")
if dir == "" {
return "", errors.New("%LocalAppData% is not defined")
}
case "darwin", "ios":
dir = Getenv("HOME")
if dir == "" {
return "", errors.New("$HOME is not defined")
}
dir += "/Library/Caches"
case "plan9":
dir = Getenv("home")
if dir == "" {
return "", errors.New("$home is not defined")
}
dir += "/lib/cache"
default: // Unix
dir = Getenv("XDG_CACHE_HOME")
if dir == "" {
dir = Getenv("HOME")
if dir == "" {
return "", errors.New("neither $XDG_CACHE_HOME nor $HOME are defined")
}
dir += "/.cache"
}
}
return dir, nil
}
// UserConfigDir returns the default root directory to use for user-specific
// configuration data. Users should create their own application-specific
// subdirectory within this one and use that.
//
// On Unix systems, it returns $XDG_CONFIG_HOME as specified by
// https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html if
// non-empty, else $HOME/.config.
// On Darwin, it returns $HOME/Library/Application Support.
// On Windows, it returns %AppData%.
// On Plan 9, it returns $home/lib.
//
// If the location cannot be determined (for example, $HOME is not defined),
// then it will return an error.
func UserConfigDir() (string, error) {
var dir string
switch runtime.GOOS {
case "windows":
dir = Getenv("AppData")
if dir == "" {
return "", errors.New("%AppData% is not defined")
}
case "darwin", "ios":
dir = Getenv("HOME")
if dir == "" {
return "", errors.New("$HOME is not defined")
}
dir += "/Library/Application Support"
case "plan9":
dir = Getenv("home")
if dir == "" {
return "", errors.New("$home is not defined")
}
dir += "/lib"
default: // Unix
dir = Getenv("XDG_CONFIG_HOME")
if dir == "" {
dir = Getenv("HOME")
if dir == "" {
return "", errors.New("neither $XDG_CONFIG_HOME nor $HOME are defined")
}
dir += "/.config"
}
}
return dir, nil
}
type (
FileMode = fs.FileMode
FileInfo = fs.FileInfo
+36
View File
@@ -270,6 +270,42 @@ func TestUserHomeDir(t *testing.T) {
}
}
func TestUserCacheDir(t *testing.T) {
dir, err := UserCacheDir()
if dir == "" && err == nil {
t.Fatal("UserCacheDir returned an empty string but no error")
}
if err != nil {
t.Logf("UserCacheDir failed: %v", err)
return
}
fi, err := Stat(dir)
if err != nil {
t.Fatal(err)
}
if !fi.IsDir() {
t.Fatalf("dir %s is not directory; type = %v", dir, fi.Mode())
}
}
func TestUserConfigDir(t *testing.T) {
dir, err := UserConfigDir()
if dir == "" && err == nil {
t.Fatal("UserConfigDir returned an empty string but no error")
}
if err != nil {
t.Logf("UserConfigDir failed: %v", err)
return
}
fi, err := Stat(dir)
if err != nil {
t.Fatal(err)
}
if !fi.IsDir() {
t.Fatalf("dir %s is not directory; type = %v", dir, fi.Mode())
}
}
func TestDirFS(t *testing.T) {
if runtime.GOOS == "windows" {
t.Log("TODO: implement Readdir for Windows")