Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
159 changes: 143 additions & 16 deletions env/paths.go
Original file line number Diff line number Diff line change
Expand Up @@ -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())
Expand Down Expand Up @@ -91,20 +95,97 @@ func appDir(dirs ...string) (string, error) {
return filepath.Join(dir, filepath.Join(dirs...)), nil
case "linux":
dir := os.Getenv("XDG_DATA_HOME")
if dir != "" {
return filepath.Join(dir, filepath.Join(dirs...)), nil
}
home, err := HomeDir()
if err != nil {
return "", err
}
return filepath.Join(home, ".local", "share", filepath.Join(dirs...)), nil
default:
return "", errors.Errorf("unsupported platform %s", runtime.GOOS)
}
}

// 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 == "" {
home, err := HomeDir()
if err != nil {
return "", err
}
dir = filepath.Join(home, ".local", "share", filepath.Join(dirs...))
return "", errors.Errorf("APPDATA not set")
}
return dir, nil
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", filepath.Join(dirs...)), nil
default:
return "", errors.Errorf("unsupported platform %s", runtime.GOOS)
}
}

// LogsPath returns directory for app files.
// 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 {
Expand Down Expand Up @@ -137,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
}
return dir, nil
home, err := HomeDir()
if err != nil {
return "", err
}
return filepath.Join(home, ".cache", filepath.Join(dirs...)), nil
default:
return "", errors.Errorf("unsupported platform %s", runtime.GOOS)
}
Expand All @@ -161,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
}
40 changes: 34 additions & 6 deletions env/paths_darwin_test.go
Original file line number Diff line number Diff line change
@@ -1,18 +1,46 @@
package env_test

import (
"strings"
"os"
"path/filepath"
"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"))
logsDir, err := env.LogsPath(env.Dir("KeysTest"))
require.Equal(t, filepath.Join(env.MustHomeDir(), "/Library/Application Support/KeysEnvTest"), appDir)
exists, err := env.PathExists(appDir)
require.NoError(t, err)
require.True(t, strings.HasSuffix(logsDir, "/Library/Logs/KeysTest"))
require.False(t, exists)

logsDir, err := env.LogsPath(env.Dir("KeysEnvTest"))
require.NoError(t, err)
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("KeysEnvTest"), env.File("test.txt"), env.Mkdir())
require.NoError(t, err)
require.Equal(t, configPath, filepath.Join(env.MustHomeDir(), "/Library/Application Support/KeysEnvTest/test.txt"))
configDir, file := filepath.Split(configPath)
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)
}
74 changes: 68 additions & 6 deletions env/paths_linux_test.go
Original file line number Diff line number Diff line change
@@ -1,18 +1,80 @@
package env_test

import (
"strings"
"os"
"path/filepath"
"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`))
logsDir, err := env.LogsPath(env.Dir("KeysTest"))
require.Equal(t, filepath.Join(env.MustHomeDir(), `/.local/share/KeysEnvTest`), appDir)
exists, err := env.PathExists(appDir)
require.NoError(t, err)
require.True(t, strings.HasSuffix(logsDir, `/.cache/KeysTest`))
require.False(t, exists)

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.False(t, exists)

configPath, err := env.ConfigPath(env.Dir("KeysEnvTest"), env.File("test.txt"), env.Mkdir())
require.NoError(t, err)
require.Equal(t, filepath.Join(env.MustHomeDir(), "/.config/KeysEnvTest/test.txt"), configPath)
configDir, file := filepath.Split(configPath)
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)
}
31 changes: 27 additions & 4 deletions env/paths_windows_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,46 @@ 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) {
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.Equal(t, filepath.Join(env.MustHomeDir(), `\AppData\Roaming\KeysTest\test.txt`), configPath)
configDir, file := filepath.Split(configPath)
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)
}