├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── appveyor.yml ├── go.mod ├── go.sum ├── xdg.go ├── xdg_bsd.go ├── xdg_bsd_test.go ├── xdg_darwin.go ├── xdg_darwin_test.go ├── xdg_linux.go ├── xdg_linux_test.go ├── xdg_test.go ├── xdg_windows.go └── xdg_windows_test.go /.gitignore: -------------------------------------------------------------------------------- 1 | *.test 2 | *.out 3 | .DS_STORE 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | arch: 3 | - amd64 4 | - ppc64le 5 | go: 6 | - 1.13.x 7 | os: 8 | - linux 9 | - osx 10 | before_install: 11 | - go get -t -v ./... 12 | script: 13 | - go test -v -race -covermode=atomic -coverprofile=coverage.txt 14 | after_success: 15 | - bash <(curl -s https://codecov.io/bash) 16 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2017, OpenPeeDeeP 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | * Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # XDG [![Build status](https://ci.appveyor.com/api/projects/status/9eoupq9jgsu2p0jv?svg=true)](https://ci.appveyor.com/project/dixonwille/xdg) [![Build Status](https://travis-ci.org/OpenPeeDeeP/xdg.svg?branch=master)](https://travis-ci.org/OpenPeeDeeP/xdg) [![Go Report Card](https://goreportcard.com/badge/github.com/OpenPeeDeeP/xdg)](https://goreportcard.com/report/github.com/OpenPeeDeeP/xdg) [![GoDoc](https://godoc.org/github.com/OpenPeeDeeP/xdg?status.svg)](https://godoc.org/github.com/OpenPeeDeeP/xdg) [![codecov](https://codecov.io/gh/OpenPeeDeeP/xdg/branch/master/graph/badge.svg)](https://codecov.io/gh/OpenPeeDeeP/xdg) 2 | 3 | A cross platform package that tries to follow [XDG Standard](https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html) when possible. Since XDG is linux specific, I am only able to follow standards to the T on linux. But for the other operating systems I am finding similar best practice locations for the files. 4 | 5 | ## Locations Per OS 6 | 7 | The following table shows what is used if the envrionment variable is not set. If the variable is set then this package uses that. Linux follows the default standards. Mac does when it comes to the home directory but for system wide it uses the standard `/Library/Application Support`. As for Windows, the variable defaults are just other environment variables set up by the operation system. 8 | 9 | > When creating `XDG` application the `Vendor` and `Application` names are appeneded to the end of the path to keep projects unique. 10 | 11 | | | Linux(and BSD) | Mac | Windows | 12 | | ---: | :---: | :---: | :---: | 13 | | `XDG_DATA_DIRS` | [`/usr/local/share`, `/usr/share`] | [`/Library/Application Support`] | `%PROGRAMDATA%` | 14 | | `XDG_DATA_HOME` | `~/.local/share` | `~/Library/Application Support` | `%APPDATA%` | 15 | | `XDG_CONFIG_DIRS` | [`/etc/xdg`] | [`/Library/Application Support`] | `%PROGRAMDATA%` | 16 | | `XDG_CONFIG_HOME` | `~/.config` | `~/Library/Application Support` | `%APPDATA%` | 17 | | `XDG_CACHE_HOME` | `~/.cache` | `~/Library/Caches` | `%LOCALAPPDATA%` | 18 | 19 | ## Notes 20 | 21 | - This package does not merge files if they exist across different directories. 22 | - The `Query` methods search through the system variables, `DIRS`, first (when using environment variables first in the variable has presidence). It then checks home variables, `HOME`. 23 | - This package will not create any directories for you. In the standard, it states the following: 24 | 25 | > If, when attempting to write a file, the destination directory is non-existant an attempt should be made to create it with permission `0700`. If the destination directory exists already the permissions should not be changed. The application should be prepared to handle the case where the file could not be written, either because the directory was non-existant and could not be created, or for any other reason. In such case it may chose to present an error message to the user. 26 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | version: 0.0.1_{build} 2 | build: off 3 | platform: x64 4 | clone_folder: c:\gopath\src\github.com\OpenPeeDeeP\xdg 5 | environment: 6 | GOPATH: c:\gopath 7 | stack: go 1.11 8 | install: 9 | - go get -t -v ./... 10 | - cinst codecov 11 | before_test: 12 | - go vet ./... 13 | test_script: 14 | - go test -v -race -covermode=atomic -coverprofile=coverage.txt 15 | on_success: 16 | - codecov -f coverage.txt 17 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/OpenPeeDeeP/xdg 2 | 3 | require ( 4 | github.com/davecgh/go-spew v1.1.1 // indirect 5 | github.com/pmezard/go-difflib v1.0.0 // indirect 6 | github.com/stretchr/objx v0.1.1 // indirect 7 | github.com/stretchr/testify v1.2.2 8 | ) 9 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 2 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 3 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 4 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 5 | github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A= 6 | github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 7 | github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= 8 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 9 | -------------------------------------------------------------------------------- /xdg.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017, OpenPeeDeeP. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Package xdg impelements the XDG standard for application file locations. 6 | package xdg 7 | 8 | import ( 9 | "os" 10 | "path/filepath" 11 | "strings" 12 | ) 13 | 14 | var defaulter xdgDefaulter = new(osDefaulter) 15 | 16 | type xdgDefaulter interface { 17 | defaultDataHome() string 18 | defaultDataDirs() []string 19 | defaultConfigHome() string 20 | defaultConfigDirs() []string 21 | defaultCacheHome() string 22 | } 23 | 24 | type osDefaulter struct { 25 | } 26 | 27 | //This method is used in the testing suit 28 | // nolint: deadcode 29 | func setDefaulter(def xdgDefaulter) { 30 | defaulter = def 31 | } 32 | 33 | // XDG is information about the currently running application 34 | type XDG struct { 35 | Vendor string 36 | Application string 37 | } 38 | 39 | // New returns an instance of XDG that is used to grab files for application use 40 | func New(vendor, application string) *XDG { 41 | return &XDG{ 42 | Vendor: vendor, 43 | Application: application, 44 | } 45 | } 46 | 47 | // DataHome returns the location that should be used for user specific data files for this specific application 48 | func (x *XDG) DataHome() string { 49 | return filepath.Join(DataHome(), x.Vendor, x.Application) 50 | } 51 | 52 | // DataDirs returns a list of locations that should be used for system wide data files for this specific application 53 | func (x *XDG) DataDirs() []string { 54 | dataDirs := DataDirs() 55 | for i, dir := range dataDirs { 56 | dataDirs[i] = filepath.Join(dir, x.Vendor, x.Application) 57 | } 58 | return dataDirs 59 | } 60 | 61 | // ConfigHome returns the location that should be used for user specific config files for this specific application 62 | func (x *XDG) ConfigHome() string { 63 | return filepath.Join(ConfigHome(), x.Vendor, x.Application) 64 | } 65 | 66 | // ConfigDirs returns a list of locations that should be used for system wide config files for this specific application 67 | func (x *XDG) ConfigDirs() []string { 68 | configDirs := ConfigDirs() 69 | for i, dir := range configDirs { 70 | configDirs[i] = filepath.Join(dir, x.Vendor, x.Application) 71 | } 72 | return configDirs 73 | } 74 | 75 | // CacheHome returns the location that should be used for application cache files for this specific application 76 | func (x *XDG) CacheHome() string { 77 | return filepath.Join(CacheHome(), x.Vendor, x.Application) 78 | } 79 | 80 | // QueryData looks for the given filename in XDG paths for data files. 81 | // Returns an empty string if one was not found. 82 | func (x *XDG) QueryData(filename string) string { 83 | dirs := x.DataDirs() 84 | dirs = append([]string{x.DataHome()}, dirs...) 85 | return returnExist(filename, dirs) 86 | } 87 | 88 | // QueryConfig looks for the given filename in XDG paths for config files. 89 | // Returns an empty string if one was not found. 90 | func (x *XDG) QueryConfig(filename string) string { 91 | dirs := x.ConfigDirs() 92 | dirs = append([]string{x.ConfigHome()}, dirs...) 93 | return returnExist(filename, dirs) 94 | } 95 | 96 | // QueryCache looks for the given filename in XDG paths for cache files. 97 | // Returns an empty string if one was not found. 98 | func (x *XDG) QueryCache(filename string) string { 99 | return returnExist(filename, []string{x.CacheHome()}) 100 | } 101 | 102 | func returnExist(filename string, dirs []string) string { 103 | for _, dir := range dirs { 104 | _, err := os.Stat(filepath.Join(dir, filename)) 105 | if (err != nil && os.IsExist(err)) || err == nil { 106 | return filepath.Join(dir, filename) 107 | } 108 | } 109 | return "" 110 | } 111 | 112 | // DataHome returns the location that should be used for user specific data files 113 | func DataHome() string { 114 | dataHome := os.Getenv("XDG_DATA_HOME") 115 | if dataHome == "" { 116 | dataHome = defaulter.defaultDataHome() 117 | } 118 | return dataHome 119 | } 120 | 121 | // DataDirs returns a list of locations that should be used for system wide data files 122 | func DataDirs() []string { 123 | var dataDirs []string 124 | dataDirsStr := os.Getenv("XDG_DATA_DIRS") 125 | if dataDirsStr != "" { 126 | dataDirs = strings.Split(dataDirsStr, string(os.PathListSeparator)) 127 | } 128 | if len(dataDirs) == 0 { 129 | dataDirs = defaulter.defaultDataDirs() 130 | } 131 | return dataDirs 132 | } 133 | 134 | // ConfigHome returns the location that should be used for user specific config files 135 | func ConfigHome() string { 136 | configHome := os.Getenv("XDG_CONFIG_HOME") 137 | if configHome == "" { 138 | configHome = defaulter.defaultConfigHome() 139 | } 140 | return configHome 141 | } 142 | 143 | // ConfigDirs returns a list of locations that should be used for system wide config files 144 | func ConfigDirs() []string { 145 | var configDirs []string 146 | configDirsStr := os.Getenv("XDG_CONFIG_DIRS") 147 | if configDirsStr != "" { 148 | configDirs = strings.Split(configDirsStr, string(os.PathListSeparator)) 149 | } 150 | if len(configDirs) == 0 { 151 | configDirs = defaulter.defaultConfigDirs() 152 | } 153 | return configDirs 154 | } 155 | 156 | // CacheHome returns the location that should be used for application cache files 157 | func CacheHome() string { 158 | cacheHome := os.Getenv("XDG_CACHE_HOME") 159 | if cacheHome == "" { 160 | cacheHome = defaulter.defaultCacheHome() 161 | } 162 | return cacheHome 163 | } 164 | -------------------------------------------------------------------------------- /xdg_bsd.go: -------------------------------------------------------------------------------- 1 | // +build freebsd openbsd netbsd 2 | 3 | // Copyright (c) 2017, OpenPeeDeeP. All rights reserved. 4 | // Use of this source code is governed by a BSD-style 5 | // license that can be found in the LICENSE file. 6 | 7 | package xdg 8 | 9 | import ( 10 | "os" 11 | "path/filepath" 12 | ) 13 | 14 | func (o *osDefaulter) defaultDataHome() string { 15 | return filepath.Join(os.Getenv("HOME"), ".local", "share") 16 | } 17 | 18 | func (o *osDefaulter) defaultDataDirs() []string { 19 | return []string{"/usr/local/share/", "/usr/share/"} 20 | } 21 | 22 | func (o *osDefaulter) defaultConfigHome() string { 23 | return filepath.Join(os.Getenv("HOME"), ".config") 24 | } 25 | 26 | func (o *osDefaulter) defaultConfigDirs() []string { 27 | return []string{"/etc/xdg"} 28 | } 29 | 30 | func (o *osDefaulter) defaultCacheHome() string { 31 | return filepath.Join(os.Getenv("HOME"), ".cache") 32 | } 33 | -------------------------------------------------------------------------------- /xdg_bsd_test.go: -------------------------------------------------------------------------------- 1 | // +build freebsd openbsd netbsd 2 | 3 | // Copyright (c) 2017, OpenPeeDeeP. All rights reserved. 4 | // Use of this source code is governed by a BSD-style 5 | // license that can be found in the LICENSE file. 6 | 7 | package xdg 8 | 9 | import ( 10 | "os" 11 | "testing" 12 | 13 | "github.com/stretchr/testify/assert" 14 | ) 15 | 16 | func TestDefaultDataHome(t *testing.T) { 17 | setDefaulter(new(osDefaulter)) 18 | assert := assert.New(t) 19 | homeDir := "/some/path" 20 | expected := homeDir + "/.local/share" 21 | os.Setenv("HOME", homeDir) // nolint: errcheck 22 | 23 | actual := defaulter.defaultDataHome() 24 | assert.Equal(expected, actual) 25 | } 26 | 27 | func TestDefaultDataDirs(t *testing.T) { 28 | setDefaulter(new(osDefaulter)) 29 | assert := assert.New(t) 30 | expected := []string{"/usr/local/share/", "/usr/share/"} 31 | 32 | actual := defaulter.defaultDataDirs() 33 | assert.Equal(expected, actual) 34 | } 35 | 36 | func TestDefaultConfigHome(t *testing.T) { 37 | setDefaulter(new(osDefaulter)) 38 | assert := assert.New(t) 39 | homeDir := "/some/path" 40 | expected := homeDir + "/.config" 41 | os.Setenv("HOME", homeDir) // nolint: errcheck 42 | 43 | actual := defaulter.defaultConfigHome() 44 | assert.Equal(expected, actual) 45 | } 46 | 47 | func TestDefaultConfigDirs(t *testing.T) { 48 | setDefaulter(new(osDefaulter)) 49 | assert := assert.New(t) 50 | expected := []string{"/etc/xdg"} 51 | 52 | actual := defaulter.defaultConfigDirs() 53 | assert.Equal(expected, actual) 54 | } 55 | 56 | func TestDefaultCacheHome(t *testing.T) { 57 | setDefaulter(new(osDefaulter)) 58 | assert := assert.New(t) 59 | homeDir := "/some/path" 60 | expected := homeDir + "/.cache" 61 | os.Setenv("HOME", homeDir) // nolint: errcheck 62 | 63 | actual := defaulter.defaultCacheHome() 64 | assert.Equal(expected, actual) 65 | } 66 | -------------------------------------------------------------------------------- /xdg_darwin.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017, OpenPeeDeeP. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package xdg 6 | 7 | import ( 8 | "os" 9 | "path/filepath" 10 | ) 11 | 12 | func (o *osDefaulter) defaultDataHome() string { 13 | return filepath.Join(os.Getenv("HOME"), "Library", "Application Support") 14 | } 15 | 16 | func (o *osDefaulter) defaultDataDirs() []string { 17 | return []string{filepath.Join("/Library", "Application Support")} 18 | } 19 | 20 | func (o *osDefaulter) defaultConfigHome() string { 21 | return filepath.Join(os.Getenv("HOME"), "Library", "Application Support") 22 | } 23 | 24 | func (o *osDefaulter) defaultConfigDirs() []string { 25 | return []string{filepath.Join("/Library", "Application Support")} 26 | } 27 | 28 | func (o *osDefaulter) defaultCacheHome() string { 29 | return filepath.Join(os.Getenv("HOME"), "Library", "Caches") 30 | } 31 | -------------------------------------------------------------------------------- /xdg_darwin_test.go: -------------------------------------------------------------------------------- 1 | // +build darwin 2 | 3 | // Copyright (c) 2017, OpenPeeDeeP. All rights reserved. 4 | // Use of this source code is governed by a BSD-style 5 | // license that can be found in the LICENSE file. 6 | 7 | package xdg 8 | 9 | import ( 10 | "os" 11 | "testing" 12 | 13 | "github.com/stretchr/testify/assert" 14 | ) 15 | 16 | func TestDefaultDataHome(t *testing.T) { 17 | setDefaulter(new(osDefaulter)) 18 | assert := assert.New(t) 19 | homeDir := "/some/path" 20 | expected := homeDir + "/Library/Application Support" 21 | os.Setenv("HOME", homeDir) // nolint: errcheck 22 | 23 | actual := defaulter.defaultDataHome() 24 | assert.Equal(expected, actual) 25 | } 26 | 27 | func TestDefaultDataDirs(t *testing.T) { 28 | setDefaulter(new(osDefaulter)) 29 | assert := assert.New(t) 30 | expected := []string{"/Library/Application Support"} 31 | 32 | actual := defaulter.defaultDataDirs() 33 | assert.Equal(expected, actual) 34 | } 35 | 36 | func TestDefaultConfigHome(t *testing.T) { 37 | setDefaulter(new(osDefaulter)) 38 | assert := assert.New(t) 39 | homeDir := "/some/path" 40 | expected := homeDir + "/Library/Application Support" 41 | os.Setenv("HOME", homeDir) // nolint: errcheck 42 | 43 | actual := defaulter.defaultConfigHome() 44 | assert.Equal(expected, actual) 45 | } 46 | 47 | func TestDefaultConfigDirs(t *testing.T) { 48 | setDefaulter(new(osDefaulter)) 49 | assert := assert.New(t) 50 | expected := []string{"/Library/Application Support"} 51 | 52 | actual := defaulter.defaultConfigDirs() 53 | assert.Equal(expected, actual) 54 | } 55 | 56 | func TestDefaultCacheHome(t *testing.T) { 57 | setDefaulter(new(osDefaulter)) 58 | assert := assert.New(t) 59 | homeDir := "/some/path" 60 | expected := homeDir + "/Library/Caches" 61 | os.Setenv("HOME", homeDir) // nolint: errcheck 62 | 63 | actual := defaulter.defaultCacheHome() 64 | assert.Equal(expected, actual) 65 | } 66 | -------------------------------------------------------------------------------- /xdg_linux.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017, OpenPeeDeeP. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package xdg 6 | 7 | import ( 8 | "os" 9 | "path/filepath" 10 | ) 11 | 12 | func (o *osDefaulter) defaultDataHome() string { 13 | return filepath.Join(os.Getenv("HOME"), ".local", "share") 14 | } 15 | 16 | func (o *osDefaulter) defaultDataDirs() []string { 17 | return []string{"/usr/local/share/", "/usr/share/"} 18 | } 19 | 20 | func (o *osDefaulter) defaultConfigHome() string { 21 | return filepath.Join(os.Getenv("HOME"), ".config") 22 | } 23 | 24 | func (o *osDefaulter) defaultConfigDirs() []string { 25 | return []string{"/etc/xdg"} 26 | } 27 | 28 | func (o *osDefaulter) defaultCacheHome() string { 29 | return filepath.Join(os.Getenv("HOME"), ".cache") 30 | } 31 | -------------------------------------------------------------------------------- /xdg_linux_test.go: -------------------------------------------------------------------------------- 1 | // +build linux 2 | 3 | // Copyright (c) 2017, OpenPeeDeeP. All rights reserved. 4 | // Use of this source code is governed by a BSD-style 5 | // license that can be found in the LICENSE file. 6 | 7 | package xdg 8 | 9 | import ( 10 | "os" 11 | "testing" 12 | 13 | "github.com/stretchr/testify/assert" 14 | ) 15 | 16 | func TestDefaultDataHome(t *testing.T) { 17 | setDefaulter(new(osDefaulter)) 18 | assert := assert.New(t) 19 | homeDir := "/some/path" 20 | expected := homeDir + "/.local/share" 21 | os.Setenv("HOME", homeDir) // nolint: errcheck 22 | 23 | actual := defaulter.defaultDataHome() 24 | assert.Equal(expected, actual) 25 | } 26 | 27 | func TestDefaultDataDirs(t *testing.T) { 28 | setDefaulter(new(osDefaulter)) 29 | assert := assert.New(t) 30 | expected := []string{"/usr/local/share/", "/usr/share/"} 31 | 32 | actual := defaulter.defaultDataDirs() 33 | assert.Equal(expected, actual) 34 | } 35 | 36 | func TestDefaultConfigHome(t *testing.T) { 37 | setDefaulter(new(osDefaulter)) 38 | assert := assert.New(t) 39 | homeDir := "/some/path" 40 | expected := homeDir + "/.config" 41 | os.Setenv("HOME", homeDir) // nolint: errcheck 42 | 43 | actual := defaulter.defaultConfigHome() 44 | assert.Equal(expected, actual) 45 | } 46 | 47 | func TestDefaultConfigDirs(t *testing.T) { 48 | setDefaulter(new(osDefaulter)) 49 | assert := assert.New(t) 50 | expected := []string{"/etc/xdg"} 51 | 52 | actual := defaulter.defaultConfigDirs() 53 | assert.Equal(expected, actual) 54 | } 55 | 56 | func TestDefaultCacheHome(t *testing.T) { 57 | setDefaulter(new(osDefaulter)) 58 | assert := assert.New(t) 59 | homeDir := "/some/path" 60 | expected := homeDir + "/.cache" 61 | os.Setenv("HOME", homeDir) // nolint: errcheck 62 | 63 | actual := defaulter.defaultCacheHome() 64 | assert.Equal(expected, actual) 65 | } 66 | -------------------------------------------------------------------------------- /xdg_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017, OpenPeeDeeP. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package xdg 6 | 7 | import ( 8 | "fmt" 9 | "os" 10 | "path/filepath" 11 | "strings" 12 | "testing" 13 | 14 | "github.com/stretchr/testify/assert" 15 | "github.com/stretchr/testify/mock" 16 | ) 17 | 18 | type mockDefaulter struct { 19 | mock.Mock 20 | } 21 | 22 | func (m *mockDefaulter) defaultDataHome() string { 23 | args := m.Called() 24 | return args.String(0) 25 | } 26 | func (m *mockDefaulter) defaultDataDirs() []string { 27 | args := m.Called() 28 | return args.Get(0).([]string) 29 | } 30 | func (m *mockDefaulter) defaultConfigHome() string { 31 | args := m.Called() 32 | return args.String(0) 33 | } 34 | func (m *mockDefaulter) defaultConfigDirs() []string { 35 | args := m.Called() 36 | return args.Get(0).([]string) 37 | } 38 | func (m *mockDefaulter) defaultCacheHome() string { 39 | args := m.Called() 40 | return args.String(0) 41 | } 42 | 43 | const ( 44 | MDataHome = iota 45 | MDataDirs 46 | MConfigHome 47 | MConfigDirs 48 | MCacheHome 49 | ) 50 | 51 | var getterTestCases = []getterTestCase{ 52 | {"DataHome Without", "defaultDataHome", filepath.Clean("/some/path"), true, "XDG_DATA_HOME", "", MDataHome, nil, filepath.Clean("/some/path")}, 53 | {"DataDirs Without", "defaultDataDirs", []string{filepath.Clean("/some/path"), filepath.Clean("/some/other/path")}, true, "XDG_DATA_DIRS", "", MDataDirs, nil, []string{filepath.Clean("/some/path"), filepath.Clean("/some/other/path")}}, 54 | {"ConfigHome Without", "defaultConfigHome", filepath.Clean("/some/path"), true, "XDG_CONFIG_HOME", "", MConfigHome, nil, filepath.Clean("/some/path")}, 55 | {"ConfigDirs Without", "defaultConfigDirs", []string{filepath.Clean("/some/path"), filepath.Clean("/some/other/path")}, true, "XDG_CONFIG_DIRS", "", MConfigDirs, nil, []string{filepath.Clean("/some/path"), filepath.Clean("/some/other/path")}}, 56 | {"CacheHome Without", "defaultCacheHome", filepath.Clean("/some/path"), true, "XDG_CACHE_HOME", "", MCacheHome, nil, filepath.Clean("/some/path")}, 57 | 58 | {"DataHome With", "defaultDataHome", filepath.Clean("/wrong/path"), false, "XDG_DATA_HOME", filepath.Clean("/some/path"), MDataHome, nil, filepath.Clean("/some/path")}, 59 | {"DataDirs With", "defaultDataDirs", []string{filepath.Clean("/wrong/path"), filepath.Clean("/some/other/wrong")}, false, "XDG_DATA_DIRS", strings.Join([]string{filepath.Clean("/some/path"), filepath.Clean("/some/other/path")}, string(os.PathListSeparator)), MDataDirs, nil, []string{filepath.Clean("/some/path"), filepath.Clean("/some/other/path")}}, 60 | {"ConfigHome With", "defaultConfigHome", filepath.Clean("/wrong/path"), false, "XDG_CONFIG_HOME", filepath.Clean("/some/path"), MConfigHome, nil, filepath.Clean("/some/path")}, 61 | {"ConfigDirs With", "defaultConfigDirs", []string{filepath.Clean("/wrong/path"), filepath.Clean("/some/other/wrong")}, false, "XDG_CONFIG_DIRS", strings.Join([]string{filepath.Clean("/some/path"), filepath.Clean("/some/other/path")}, string(os.PathListSeparator)), MConfigDirs, nil, []string{filepath.Clean("/some/path"), filepath.Clean("/some/other/path")}}, 62 | {"CacheHome With", "defaultCacheHome", filepath.Clean("/wrong/path"), false, "XDG_CACHE_HOME", filepath.Clean("/some/path"), MCacheHome, nil, filepath.Clean("/some/path")}, 63 | 64 | {"DataHome App Without", "defaultDataHome", filepath.Clean("/some/path"), true, "XDG_DATA_HOME", "", MDataHome, New("OpenPeeDeeP", "XDG"), filepath.Clean("/some/path/OpenPeeDeeP/XDG")}, 65 | {"DataDirs App Without", "defaultDataDirs", []string{filepath.Clean("/some/path"), filepath.Clean("/some/other/path")}, true, "XDG_DATA_DIRS", "", MDataDirs, New("OpenPeeDeeP", "XDG"), []string{filepath.Clean("/some/path/OpenPeeDeeP/XDG"), filepath.Clean("/some/other/path/OpenPeeDeeP/XDG")}}, 66 | {"ConfigHome App Without", "defaultConfigHome", filepath.Clean("/some/path"), true, "XDG_CONFIG_HOME", "", MConfigHome, New("OpenPeeDeeP", "XDG"), filepath.Clean("/some/path/OpenPeeDeeP/XDG")}, 67 | {"ConfigDirs App Without", "defaultConfigDirs", []string{filepath.Clean("/some/path"), filepath.Clean("/some/other/path")}, true, "XDG_CONFIG_DIRS", "", MConfigDirs, New("OpenPeeDeeP", "XDG"), []string{filepath.Clean("/some/path/OpenPeeDeeP/XDG"), filepath.Clean("/some/other/path/OpenPeeDeeP/XDG")}}, 68 | {"CacheHome App Without", "defaultCacheHome", filepath.Clean("/some/path"), true, "XDG_CACHE_HOME", "", MCacheHome, New("OpenPeeDeeP", "XDG"), filepath.Clean("/some/path/OpenPeeDeeP/XDG")}, 69 | 70 | {"DataHome App With", "defaultDataHome", filepath.Clean("/wrong/path"), false, "XDG_DATA_HOME", filepath.Clean("/some/path"), MDataHome, New("OpenPeeDeeP", "XDG"), filepath.Clean("/some/path/OpenPeeDeeP/XDG")}, 71 | {"DataDirs App With", "defaultDataDirs", []string{filepath.Clean("/wrong/path"), filepath.Clean("/some/other/wrong")}, false, "XDG_DATA_DIRS", strings.Join([]string{filepath.Clean("/some/path"), filepath.Clean("/some/other/path")}, string(os.PathListSeparator)), MDataDirs, New("OpenPeeDeeP", "XDG"), []string{filepath.Clean("/some/path/OpenPeeDeeP/XDG"), filepath.Clean("/some/other/path/OpenPeeDeeP/XDG")}}, 72 | {"ConfigHome App With", "defaultConfigHome", filepath.Clean("/wrong/path"), false, "XDG_CONFIG_HOME", filepath.Clean("/some/path"), MConfigHome, New("OpenPeeDeeP", "XDG"), filepath.Clean("/some/path/OpenPeeDeeP/XDG")}, 73 | {"ConfigDirs App With", "defaultConfigDirs", []string{filepath.Clean("/wrong/path"), filepath.Clean("/some/other/wrong")}, false, "XDG_CONFIG_DIRS", strings.Join([]string{filepath.Clean("/some/path"), filepath.Clean("/some/other/path")}, string(os.PathListSeparator)), MConfigDirs, New("OpenPeeDeeP", "XDG"), []string{filepath.Clean("/some/path/OpenPeeDeeP/XDG"), filepath.Clean("/some/other/path/OpenPeeDeeP/XDG")}}, 74 | {"CacheHome App With", "defaultCacheHome", filepath.Clean("/wrong/path"), false, "XDG_CACHE_HOME", filepath.Clean("/some/path"), MCacheHome, New("OpenPeeDeeP", "XDG"), filepath.Clean("/some/path/OpenPeeDeeP/XDG")}, 75 | } 76 | 77 | type getterTestCase struct { 78 | name string 79 | mokedMethod string 80 | mockedReturn interface{} 81 | calledMocked bool 82 | env string 83 | envVal string 84 | method int 85 | xdgApp *XDG 86 | expected interface{} 87 | } 88 | 89 | func TestXDG_Getters(t *testing.T) { 90 | for _, tc := range getterTestCases { 91 | t.Run(tc.name, func(t *testing.T) { 92 | assert := assert.New(t) 93 | mockDef := new(mockDefaulter) 94 | mockDef.On(tc.mokedMethod).Return(tc.mockedReturn) 95 | setDefaulter(mockDef) 96 | os.Setenv(tc.env, tc.envVal) // nolint: errcheck 97 | 98 | actual := computeActual(tc) 99 | 100 | if tc.calledMocked { 101 | mockDef.AssertExpectations(t) 102 | } else { 103 | mockDef.AssertNotCalled(t, tc.mokedMethod) 104 | } 105 | assert.Equal(tc.expected, actual) 106 | }) 107 | } 108 | } 109 | 110 | // nolint: gocyclo 111 | func computeActual(tc getterTestCase) interface{} { 112 | var actual interface{} 113 | switch tc.method { 114 | case MDataHome: 115 | if tc.xdgApp != nil { 116 | actual = tc.xdgApp.DataHome() 117 | } else { 118 | actual = DataHome() 119 | } 120 | case MDataDirs: 121 | if tc.xdgApp != nil { 122 | actual = tc.xdgApp.DataDirs() 123 | } else { 124 | actual = DataDirs() 125 | } 126 | case MConfigHome: 127 | if tc.xdgApp != nil { 128 | actual = tc.xdgApp.ConfigHome() 129 | } else { 130 | actual = ConfigHome() 131 | } 132 | case MConfigDirs: 133 | if tc.xdgApp != nil { 134 | actual = tc.xdgApp.ConfigDirs() 135 | } else { 136 | actual = ConfigDirs() 137 | } 138 | case MCacheHome: 139 | if tc.xdgApp != nil { 140 | actual = tc.xdgApp.CacheHome() 141 | } else { 142 | actual = CacheHome() 143 | } 144 | } 145 | return actual 146 | } 147 | 148 | const ( 149 | QData = iota 150 | QConfig 151 | QCache 152 | ) 153 | 154 | var ( 155 | root = "testingFolder" 156 | fileTypes = []string{"data", "config", "cache"} 157 | fileLoc = []string{"home", "dirs"} 158 | ) 159 | 160 | type queryTestCase struct { 161 | name string 162 | xdgApp *XDG 163 | queryType int 164 | filename string 165 | expected string 166 | } 167 | 168 | var queryTestCases = []queryTestCase{ 169 | {"Data Dirs", New("OpenPeeDeeP", "XDG"), QData, "XDG_DATA_DIRS.txt", filepath.Clean("/data/dirs/OpenPeeDeeP/XDG/XDG_DATA_DIRS.txt")}, 170 | {"Data Home", New("OpenPeeDeeP", "XDG"), QData, "XDG_DATA_HOME.txt", filepath.Clean("/data/home/OpenPeeDeeP/XDG/XDG_DATA_HOME.txt")}, 171 | {"Data DNE", New("OpenPeeDeeP", "XDG"), QData, "XDG_CONFIG_HOME.txt", ""}, 172 | 173 | {"Config Dirs", New("OpenPeeDeeP", "XDG"), QConfig, "XDG_CONFIG_DIRS.txt", filepath.Clean("/config/dirs/OpenPeeDeeP/XDG/XDG_CONFIG_DIRS.txt")}, 174 | {"Config Home", New("OpenPeeDeeP", "XDG"), QConfig, "XDG_CONFIG_HOME.txt", filepath.Clean("/config/home/OpenPeeDeeP/XDG/XDG_CONFIG_HOME.txt")}, 175 | {"Config DNE", New("OpenPeeDeeP", "XDG"), QConfig, "XDG_DATA_HOME.txt", ""}, 176 | 177 | {"Cache Home", New("OpenPeeDeeP", "XDG"), QCache, "XDG_CACHE_HOME.txt", filepath.Clean("/cache/home/OpenPeeDeeP/XDG/XDG_CACHE_HOME.txt")}, 178 | {"Cache DNE", New("OpenPeeDeeP", "XDG"), QCache, "XDG_CACHE_DIRS.txt", ""}, 179 | } 180 | 181 | func TestXDG_Query(t *testing.T) { 182 | for _, tc := range queryTestCases { 183 | t.Run(tc.name, func(t *testing.T) { 184 | defer teardownQueryData() //nolint: errcheck 185 | standupQueryData(tc) //nolint: errcheck 186 | assert := assert.New(t) 187 | actual := computeQuery(tc) 188 | assert.Equal(tc.expected, actual) 189 | }) 190 | } 191 | } 192 | 193 | func computeQuery(tc queryTestCase) string { 194 | var actual string 195 | switch tc.queryType { 196 | case QData: 197 | actual = tc.xdgApp.QueryData(tc.filename) 198 | case QCache: 199 | actual = tc.xdgApp.QueryCache(tc.filename) 200 | case QConfig: 201 | actual = tc.xdgApp.QueryConfig(tc.filename) 202 | } 203 | rootAbs, _ := filepath.Abs(root) 204 | actual = strings.Replace(actual, rootAbs, "", 1) 205 | return actual 206 | } 207 | 208 | func standupQueryData(tc queryTestCase) error { 209 | for _, t := range fileTypes { 210 | for _, l := range fileLoc { 211 | path, err := filepath.Abs(filepath.Join(root, t, l)) 212 | if err != nil { 213 | return err 214 | } 215 | if err = os.MkdirAll(filepath.Join(path, tc.xdgApp.Vendor, tc.xdgApp.Application), 0777); err != nil { 216 | return err 217 | } 218 | envVar := fmt.Sprintf("XDG_%s_%s", strings.ToUpper(t), strings.ToUpper(l)) 219 | if err = os.Setenv(envVar, path); err != nil { 220 | return err 221 | } 222 | file, err := os.OpenFile(filepath.Join(path, tc.xdgApp.Vendor, tc.xdgApp.Application, envVar+".txt"), os.O_CREATE|os.O_RDONLY, 0666) 223 | if err != nil { 224 | return err 225 | } 226 | defer file.Close() //nolint: errcheck 227 | } 228 | } 229 | return nil 230 | } 231 | 232 | func teardownQueryData() error { 233 | return os.RemoveAll(root) 234 | } 235 | -------------------------------------------------------------------------------- /xdg_windows.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017, OpenPeeDeeP. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package xdg 6 | 7 | import "os" 8 | 9 | func (o *osDefaulter) defaultDataHome() string { 10 | return os.Getenv("APPDATA") 11 | } 12 | 13 | func (o *osDefaulter) defaultDataDirs() []string { 14 | return []string{os.Getenv("PROGRAMDATA")} 15 | } 16 | 17 | func (o *osDefaulter) defaultConfigHome() string { 18 | return os.Getenv("APPDATA") 19 | } 20 | 21 | func (o *osDefaulter) defaultConfigDirs() []string { 22 | return []string{os.Getenv("PROGRAMDATA")} 23 | } 24 | 25 | func (o *osDefaulter) defaultCacheHome() string { 26 | return os.Getenv("LOCALAPPDATA") 27 | } 28 | -------------------------------------------------------------------------------- /xdg_windows_test.go: -------------------------------------------------------------------------------- 1 | // +build windows 2 | 3 | // Copyright (c) 2017, OpenPeeDeeP. All rights reserved. 4 | // Use of this source code is governed by a BSD-style 5 | // license that can be found in the LICENSE file. 6 | 7 | package xdg 8 | 9 | import ( 10 | "os" 11 | "testing" 12 | 13 | "github.com/stretchr/testify/assert" 14 | ) 15 | 16 | func TestDefaultDataHome(t *testing.T) { 17 | setDefaulter(new(osDefaulter)) 18 | assert := assert.New(t) 19 | appData := "/some/path" 20 | expected := appData 21 | os.Setenv("APPDATA", appData) // nolint: errcheck 22 | 23 | actual := defaulter.defaultDataHome() 24 | assert.Equal(expected, actual) 25 | } 26 | 27 | func TestDefaultDataDirs(t *testing.T) { 28 | setDefaulter(new(osDefaulter)) 29 | assert := assert.New(t) 30 | programData := "/some/path" 31 | expected := []string{programData} 32 | os.Setenv("PROGRAMDATA", programData) // nolint: errcheck 33 | 34 | actual := defaulter.defaultDataDirs() 35 | assert.Equal(expected, actual) 36 | } 37 | 38 | func TestDefaultConfigHome(t *testing.T) { 39 | setDefaulter(new(osDefaulter)) 40 | assert := assert.New(t) 41 | appData := "/some/path" 42 | expected := appData 43 | os.Setenv("APPDATA", appData) // nolint: errcheck 44 | 45 | actual := defaulter.defaultConfigHome() 46 | assert.Equal(expected, actual) 47 | } 48 | 49 | func TestDefaultConfigDirs(t *testing.T) { 50 | setDefaulter(new(osDefaulter)) 51 | assert := assert.New(t) 52 | programData := "/some/path" 53 | expected := []string{programData} 54 | os.Setenv("PROGRAMDATA", programData) // nolint: errcheck 55 | 56 | actual := defaulter.defaultConfigDirs() 57 | assert.Equal(expected, actual) 58 | } 59 | 60 | func TestDefaultCacheHome(t *testing.T) { 61 | setDefaulter(new(osDefaulter)) 62 | assert := assert.New(t) 63 | appData := "/some/path" 64 | expected := appData 65 | os.Setenv("LOCALAPPDATA", appData) // nolint: errcheck 66 | 67 | actual := defaulter.defaultCacheHome() 68 | assert.Equal(expected, actual) 69 | } 70 | --------------------------------------------------------------------------------