From ee331d11274d4976d72a43f5f2f089649577e5a4 Mon Sep 17 00:00:00 2001 From: Gabriel Handford Date: Thu, 6 Aug 2020 15:24:47 -0700 Subject: [PATCH 1/2] env: Config dir --- env/paths.go | 85 ++++++++++++++++++++++++++++++++++++++- env/paths_darwin_test.go | 19 +++++++++ env/paths_linux_test.go | 16 ++++++++ env/paths_windows_test.go | 13 ++++++ 4 files changed, 131 insertions(+), 2 deletions(-) diff --git a/env/paths.go b/env/paths.go index e234579..daf317c 100644 --- a/env/paths.go +++ b/env/paths.go @@ -30,7 +30,11 @@ func MustAppPath(opt ...PathOption) string { return path } -// AppPath returns path for a files or directory in an app support directory. +// AppPath returns where to store app (data) files. +// +// Darwin: ~/Library/Application Support +// Windows: %LOCALAPPDATA% (~/AppData/Local) +// Linux: ~/.local/share // // darwin: // env.AppPath(env.Dir("MyApp"), env.File("test.txt"), env.Mkdir()) @@ -104,7 +108,84 @@ func appDir(dirs ...string) (string, error) { } } -// LogsPath returns directory for app files. +// ConfigPath returns where to store config files. +// +// Darwin: ~/Library/Application Support +// Windows: %APPDATA% (~/AppData/Roaming) +// Linux: ~/.config +// +// darwin: +// env.ConfigPath(env.Dir("MyApp"), env.File("test.txt"), env.Mkdir()) +// => "~/Library/Application Support/MyApp/test.txt" +// +// windows: +// env.ConfigPath(env.Dir("MyApp"), env.File("test.txt"), env.Mkdir()) +// => "%APPDATA%/MyApp/test.txt" +// +// linux: +// env.ConfigPath(env.Dir("MyApp"), env.File("test.txt"), env.Mkdir()) +// => "~/.config/MyApp/test.txt" +// +func ConfigPath(opt ...PathOption) (string, error) { + opts, err := newOptions(opt...) + if err != nil { + return "", err + } + dir, err := configDir(opts.Dirs...) + if err != nil { + return "", err + } + if opts.Mkdir { + if err := mkdir(dir); err != nil { + return "", err + } + } + return filepath.Join(dir, opts.File), nil +} + +func configDir(dirs ...string) (string, error) { + switch runtime.GOOS { + case "darwin": + return appDir(dirs...) + case "windows": + dir := os.Getenv("APPDATA") + if dir == "" { + return "", errors.Errorf("APPDATA not set") + } + return filepath.Join(dir, filepath.Join(dirs...)), nil + case "linux": + dir := os.Getenv("XDG_CONFIG_HOME") + if dir != "" { + return filepath.Join(dir, filepath.Join(dirs...)), nil + } + home, err := HomeDir() + if err != nil { + return "", err + } + return filepath.Join(home, ".config"), nil + default: + return "", errors.Errorf("unsupported platform %s", runtime.GOOS) + } +} + +// LogsPath returns directory for log files. +// +// Darwin: ~/Library/Logs +// Windows: %LOCALAPPDATA% (~/AppData/Local) +// Linux: ~/.cache +// +// darwin: +// env.AppPath(env.Dir("MyApp"), env.File("test.txt"), env.Mkdir()) +// => "~/Library/Application Support/MyApp/test.txt" +// +// windows: +// env.AppPath(env.Dir("MyApp"), env.File("test.txt"), env.Mkdir()) +// => "%LOCALAPPDATA%/MyApp/test.txt" +// +// linux: +// env.AppPath(env.Dir("MyApp"), env.File("test.txt"), env.Mkdir()) +// => "~/.cache/MyApp/test.txt" +// func LogsPath(opt ...PathOption) (string, error) { opts, err := newOptions(opt...) if err != nil { diff --git a/env/paths_darwin_test.go b/env/paths_darwin_test.go index df92a9a..d587491 100644 --- a/env/paths_darwin_test.go +++ b/env/paths_darwin_test.go @@ -1,6 +1,8 @@ package env_test import ( + "os" + "path/filepath" "strings" "testing" @@ -12,7 +14,24 @@ func TestDirs(t *testing.T) { appDir, err := env.AppPath(env.Dir("KeysTest")) require.NoError(t, err) require.True(t, strings.HasSuffix(appDir, "/Library/Application Support/KeysTest")) + defer os.RemoveAll(appDir) + logsDir, err := env.LogsPath(env.Dir("KeysTest")) require.NoError(t, err) require.True(t, strings.HasSuffix(logsDir, "/Library/Logs/KeysTest")) + defer os.RemoveAll(logsDir) + exists, err := env.PathExists(logsDir) + require.NoError(t, err) + require.False(t, exists) + + configPath, err := env.ConfigPath(env.Dir("KeysTest"), env.File("test.txt"), env.Mkdir()) + require.NoError(t, err) + require.True(t, strings.HasSuffix(configPath, "/Library/Application Support/KeysTest/test.txt")) + configDir, file := filepath.Split(configPath) + require.True(t, strings.HasSuffix(configDir, "/Library/Application Support/KeysTest/")) + require.Equal(t, "test.txt", file) + defer os.RemoveAll(configDir) + exists, err = env.PathExists(configDir) + require.NoError(t, err) + require.True(t, exists) } diff --git a/env/paths_linux_test.go b/env/paths_linux_test.go index a1c0606..8103979 100644 --- a/env/paths_linux_test.go +++ b/env/paths_linux_test.go @@ -1,6 +1,8 @@ package env_test import ( + "os" + "path/filepath" "strings" "testing" @@ -12,7 +14,21 @@ func TestDirs(t *testing.T) { appDir, err := env.AppPath(env.Dir("KeysTest")) require.NoError(t, err) require.True(t, strings.HasSuffix(appDir, `/.local/share/KeysTest`)) + defer os.RemoveAll(appDir) + logsDir, err := env.LogsPath(env.Dir("KeysTest")) require.NoError(t, err) require.True(t, strings.HasSuffix(logsDir, `/.cache/KeysTest`)) + defer os.RemoveAll(logsDir) + + configPath, err := env.ConfigPath(env.Dir("KeysTest"), env.File("test.txt"), env.Mkdir()) + require.NoError(t, err) + require.True(t, strings.HasSuffix(configPath, "/.config/KeysTest/test.txt")) + configDir, file := filepath.Split(configPath) + require.True(t, strings.HasSuffix(configDir, "/.config/KeysTest/")) + require.Equal(t, "test.txt", file) + defer os.RemoveAll(configDir) + exists, err = env.PathExists(configDir) + require.NoError(t, err) + require.True(t, exists) } diff --git a/env/paths_windows_test.go b/env/paths_windows_test.go index ee16eab..5bae1d3 100644 --- a/env/paths_windows_test.go +++ b/env/paths_windows_test.go @@ -2,6 +2,7 @@ package env_test import ( "os" + "path/filepath" "strings" "testing" @@ -13,10 +14,22 @@ func TestDirs(t *testing.T) { appDir, err := env.AppPath(env.Dir("KeysTest")) require.NoError(t, err) require.True(t, strings.HasSuffix(appDir, `\AppData\Local\KeysTest`)) + logsDir, err := env.LogsPath(env.Dir("KeysTest")) require.NoError(t, err) require.True(t, strings.HasSuffix(logsDir, `\AppData\Local\KeysTest`)) + configPath, err := env.ConfigPath(env.Dir("KeysTest"), env.File("test.txt"), env.Mkdir()) + require.NoError(t, err) + require.True(t, strings.HasSuffix(configPath, `\AppData\Roaming\KeysTest\test.txt`)) + configDir, file := filepath.Split(configPath) + require.True(t, strings.HasSuffix(configDir, `\AppData\Roaming\KeysTest\`)) + require.Equal(t, "test.txt", file) + defer os.RemoveAll(configDir) + exists, err = env.PathExists(configDir) + require.NoError(t, err) + require.True(t, exists) + err = os.Setenv("LOCALAPPDATA", "") require.NoError(t, err) _, err = env.AppPath(env.Dir("KeysTest")) From 6693acfe547da5a15c4adc7b0542cd17a689e49e Mon Sep 17 00:00:00 2001 From: Gabriel Handford Date: Thu, 6 Aug 2020 16:09:22 -0700 Subject: [PATCH 2/2] Tests --- env/paths.go | 78 +++++++++++++++++++++++++++++++-------- env/paths_darwin_test.go | 33 +++++++++++------ env/paths_linux_test.go | 68 ++++++++++++++++++++++++++++------ env/paths_windows_test.go | 22 ++++++++--- 4 files changed, 156 insertions(+), 45 deletions(-) diff --git a/env/paths.go b/env/paths.go index daf317c..e4631b4 100644 --- a/env/paths.go +++ b/env/paths.go @@ -95,14 +95,14 @@ func appDir(dirs ...string) (string, error) { return filepath.Join(dir, filepath.Join(dirs...)), nil case "linux": dir := os.Getenv("XDG_DATA_HOME") - if dir == "" { - home, err := HomeDir() - if err != nil { - return "", err - } - dir = filepath.Join(home, ".local", "share", filepath.Join(dirs...)) + if dir != "" { + return filepath.Join(dir, filepath.Join(dirs...)), nil + } + home, err := HomeDir() + if err != nil { + return "", err } - return dir, nil + return filepath.Join(home, ".local", "share", filepath.Join(dirs...)), nil default: return "", errors.Errorf("unsupported platform %s", runtime.GOOS) } @@ -162,7 +162,7 @@ func configDir(dirs ...string) (string, error) { if err != nil { return "", err } - return filepath.Join(home, ".config"), nil + return filepath.Join(home, ".config", filepath.Join(dirs...)), nil default: return "", errors.Errorf("unsupported platform %s", runtime.GOOS) } @@ -218,15 +218,15 @@ func logsDir(dirs ...string) (string, error) { } return filepath.Join(dir, filepath.Join(dirs...)), nil case "linux": - dir := os.Getenv("XDG_DATA_HOME") - if dir == "" { - home, err := HomeDir() - if err != nil { - return "", err - } - dir = filepath.Join(home, ".cache", filepath.Join(dirs...)) + dir := os.Getenv("XDG_CACHE_HOME") + if dir != "" { + return filepath.Join(dir, filepath.Join(dirs...)), nil + } + home, err := HomeDir() + if err != nil { + return "", err } - return dir, nil + return filepath.Join(home, ".cache", filepath.Join(dirs...)), nil default: return "", errors.Errorf("unsupported platform %s", runtime.GOOS) } @@ -242,3 +242,49 @@ func HomeDir() (string, error) { } return usr.HomeDir, nil } + +// MustHomeDir returns current user home directory. +func MustHomeDir() string { + usr, err := user.Current() + if err != nil { + panic(err) + } + return usr.HomeDir +} + +// All returns all (unique) directories for the environment. +func All(dir ...string) ([]string, error) { + dirs := []string{} + appDir, err := AppPath(Dir(dir...)) + if err != nil { + return nil, err + } + dirs = append(dirs, appDir) + + configDir, err := ConfigPath(Dir(dir...)) + if err != nil { + return nil, err + } + if !contains(dirs, configDir) { + dirs = append(dirs, configDir) + } + + logsDir, err := LogsPath(Dir(dir...)) + if err != nil { + return nil, err + } + if !contains(dirs, logsDir) { + dirs = append(dirs, logsDir) + } + + return dirs, nil +} + +func contains(arr []string, s string) bool { + for _, a := range arr { + if s == a { + return true + } + } + return false +} diff --git a/env/paths_darwin_test.go b/env/paths_darwin_test.go index d587491..80bf777 100644 --- a/env/paths_darwin_test.go +++ b/env/paths_darwin_test.go @@ -3,35 +3,44 @@ package env_test import ( "os" "path/filepath" - "strings" "testing" "github.com/keys-pub/keys/env" "github.com/stretchr/testify/require" ) -func TestDirs(t *testing.T) { - appDir, err := env.AppPath(env.Dir("KeysTest")) +func TestPaths(t *testing.T) { + appDir, err := env.AppPath(env.Dir("KeysEnvTest")) require.NoError(t, err) - require.True(t, strings.HasSuffix(appDir, "/Library/Application Support/KeysTest")) - defer os.RemoveAll(appDir) + require.Equal(t, filepath.Join(env.MustHomeDir(), "/Library/Application Support/KeysEnvTest"), appDir) + exists, err := env.PathExists(appDir) + require.NoError(t, err) + require.False(t, exists) - logsDir, err := env.LogsPath(env.Dir("KeysTest")) + logsDir, err := env.LogsPath(env.Dir("KeysEnvTest")) require.NoError(t, err) - require.True(t, strings.HasSuffix(logsDir, "/Library/Logs/KeysTest")) - defer os.RemoveAll(logsDir) - exists, err := env.PathExists(logsDir) + require.Equal(t, filepath.Join(env.MustHomeDir(), "/Library/Logs/KeysEnvTest"), logsDir) + exists, err = env.PathExists(logsDir) require.NoError(t, err) require.False(t, exists) - configPath, err := env.ConfigPath(env.Dir("KeysTest"), env.File("test.txt"), env.Mkdir()) + configPath, err := env.ConfigPath(env.Dir("KeysEnvTest"), env.File("test.txt"), env.Mkdir()) require.NoError(t, err) - require.True(t, strings.HasSuffix(configPath, "/Library/Application Support/KeysTest/test.txt")) + require.Equal(t, configPath, filepath.Join(env.MustHomeDir(), "/Library/Application Support/KeysEnvTest/test.txt")) configDir, file := filepath.Split(configPath) - require.True(t, strings.HasSuffix(configDir, "/Library/Application Support/KeysTest/")) + require.Equal(t, configDir, filepath.Join(env.MustHomeDir(), "/Library/Application Support/KeysEnvTest")+"/") require.Equal(t, "test.txt", file) defer os.RemoveAll(configDir) exists, err = env.PathExists(configDir) require.NoError(t, err) require.True(t, exists) } + +func TestAllDirs(t *testing.T) { + dirs, err := env.All("KeysEnvTest") + require.NoError(t, err) + require.Equal(t, []string{ + filepath.Join(env.MustHomeDir(), "/Library/Application Support/KeysEnvTest"), + filepath.Join(env.MustHomeDir(), "/Library/Logs/KeysEnvTest"), + }, dirs) +} diff --git a/env/paths_linux_test.go b/env/paths_linux_test.go index 8103979..80a67bc 100644 --- a/env/paths_linux_test.go +++ b/env/paths_linux_test.go @@ -3,32 +3,78 @@ package env_test import ( "os" "path/filepath" - "strings" "testing" "github.com/keys-pub/keys/env" "github.com/stretchr/testify/require" ) -func TestDirs(t *testing.T) { - appDir, err := env.AppPath(env.Dir("KeysTest")) +func TestPaths(t *testing.T) { + appDir, err := env.AppPath(env.Dir("KeysEnvTest")) require.NoError(t, err) - require.True(t, strings.HasSuffix(appDir, `/.local/share/KeysTest`)) - defer os.RemoveAll(appDir) + require.Equal(t, filepath.Join(env.MustHomeDir(), `/.local/share/KeysEnvTest`), appDir) + exists, err := env.PathExists(appDir) + require.NoError(t, err) + require.False(t, exists) - logsDir, err := env.LogsPath(env.Dir("KeysTest")) + logsDir, err := env.LogsPath(env.Dir("KeysEnvTest")) + require.NoError(t, err) + require.Equal(t, filepath.Join(env.MustHomeDir(), `/.cache/KeysEnvTest`), logsDir) + exists, err = env.PathExists(logsDir) require.NoError(t, err) - require.True(t, strings.HasSuffix(logsDir, `/.cache/KeysTest`)) - defer os.RemoveAll(logsDir) + require.False(t, exists) - configPath, err := env.ConfigPath(env.Dir("KeysTest"), env.File("test.txt"), env.Mkdir()) + configPath, err := env.ConfigPath(env.Dir("KeysEnvTest"), env.File("test.txt"), env.Mkdir()) require.NoError(t, err) - require.True(t, strings.HasSuffix(configPath, "/.config/KeysTest/test.txt")) + require.Equal(t, filepath.Join(env.MustHomeDir(), "/.config/KeysEnvTest/test.txt"), configPath) configDir, file := filepath.Split(configPath) - require.True(t, strings.HasSuffix(configDir, "/.config/KeysTest/")) + require.Equal(t, filepath.Join(env.MustHomeDir(), "/.config/KeysEnvTest/")+"/", configDir) require.Equal(t, "test.txt", file) defer os.RemoveAll(configDir) exists, err = env.PathExists(configDir) require.NoError(t, err) require.True(t, exists) } + +func TestAppPathXDG(t *testing.T) { + prev := os.Getenv("XDG_DATA_HOME") + defer func() { os.Setenv("XDG_DATA_HOME", prev) }() + err := os.Setenv("XDG_DATA_HOME", "/test/data") + require.NoError(t, err) + + configPath, err := env.AppPath(env.Dir("KeysEnvTest")) + require.NoError(t, err) + require.Equal(t, "/test/data/KeysEnvTest", configPath) +} + +func TestConfigPathXDG(t *testing.T) { + prev := os.Getenv("XDG_CONFIG_HOME") + defer func() { os.Setenv("XDG_CONFIG_HOME", prev) }() + err := os.Setenv("XDG_CONFIG_HOME", "/test/config") + require.NoError(t, err) + + configPath, err := env.ConfigPath(env.Dir("KeysEnvTest")) + require.NoError(t, err) + require.Equal(t, "/test/config/KeysEnvTest", configPath) +} + +func TestLogsPathXDG(t *testing.T) { + prev := os.Getenv("XDG_CACHE_HOME") + defer func() { os.Setenv("XDG_CACHE_HOME", prev) }() + err := os.Setenv("XDG_CACHE_HOME", "/test/cache") + require.NoError(t, err) + + configPath, err := env.LogsPath(env.Dir("KeysEnvTest")) + require.NoError(t, err) + require.Equal(t, "/test/cache/KeysEnvTest", configPath) +} + +func TestAllDirs(t *testing.T) { + dirs, err := env.All("KeysEnvTest") + require.NoError(t, err) + require.Equal(t, []string{ + filepath.Join(env.MustHomeDir(), "/.local/share/KeysEnvTest"), + filepath.Join(env.MustHomeDir(), "/.config/KeysEnvTest"), + filepath.Join(env.MustHomeDir(), "/.cache/KeysEnvTest"), + }, dirs) +} diff --git a/env/paths_windows_test.go b/env/paths_windows_test.go index 5bae1d3..bd8e59e 100644 --- a/env/paths_windows_test.go +++ b/env/paths_windows_test.go @@ -10,28 +10,38 @@ import ( "github.com/stretchr/testify/require" ) -func TestDirs(t *testing.T) { +func TestPaths(t *testing.T) { appDir, err := env.AppPath(env.Dir("KeysTest")) require.NoError(t, err) require.True(t, strings.HasSuffix(appDir, `\AppData\Local\KeysTest`)) + exists, err := env.PathExists(appDir) + require.NoError(t, err) + require.False(t, exists) logsDir, err := env.LogsPath(env.Dir("KeysTest")) require.NoError(t, err) require.True(t, strings.HasSuffix(logsDir, `\AppData\Local\KeysTest`)) + exists, err = env.PathExists(logsDir) + require.NoError(t, err) + require.False(t, exists) configPath, err := env.ConfigPath(env.Dir("KeysTest"), env.File("test.txt"), env.Mkdir()) require.NoError(t, err) - require.True(t, strings.HasSuffix(configPath, `\AppData\Roaming\KeysTest\test.txt`)) + require.Equal(t, filepath.Join(env.MustHomeDir(), `\AppData\Roaming\KeysTest\test.txt`), configPath) configDir, file := filepath.Split(configPath) - require.True(t, strings.HasSuffix(configDir, `\AppData\Roaming\KeysTest\`)) + require.Equal(t, filepath.Join(env.MustHomeDir(), `\AppData\Roaming\KeysTest`)+`\`, configDir) require.Equal(t, "test.txt", file) defer os.RemoveAll(configDir) exists, err = env.PathExists(configDir) require.NoError(t, err) require.True(t, exists) +} - err = os.Setenv("LOCALAPPDATA", "") +func TestAllDirs(t *testing.T) { + dirs, err := env.All("KeysEnvTest") require.NoError(t, err) - _, err = env.AppPath(env.Dir("KeysTest")) - require.EqualError(t, err, "LOCALAPPDATA not set") + require.Equal(t, []string{ + filepath.Join(env.MustHomeDir(), `\AppData\Local\KeysEnvTest`), + filepath.Join(env.MustHomeDir(), `\AppData\Roaming\KeysEnvTest`), + }, dirs) }