├── LICENSE ├── README.rst ├── config.go ├── config_darwin.go ├── config_windows.go ├── config_xdg.go └── example_test.go /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 shibukawa 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | configdir for Golang 2 | ===================== 3 | 4 | Multi platform library of configuration directory for Golang. 5 | 6 | This library helps to get regular directories for configuration files or cache files that matches target operationg system's convention. 7 | 8 | It assumes the following folders are standard paths of each environment: 9 | 10 | .. list-table:: 11 | :header-rows: 1 12 | 13 | - * 14 | * Windows: 15 | * Linux/BSDs: 16 | * MacOSX: 17 | - * System level configuration folder 18 | * ``%PROGRAMDATA%`` (``C:\\ProgramData``) 19 | * ``${XDG_CONFIG_DIRS}`` (``/etc/xdg``) 20 | * ``/Library/Application Support`` 21 | - * User level configuration folder 22 | * ``%APPDATA%`` (``C:\\Users\\\\AppData\\Roaming``) 23 | * ``${XDG_CONFIG_HOME}`` (``${HOME}/.config``) 24 | * ``${HOME}/Library/Application Support`` 25 | - * User wide cache folder 26 | * ``%LOCALAPPDATA%`` ``(C:\\Users\\\\AppData\\Local)`` 27 | * ``${XDG_CACHE_HOME}`` (``${HOME}/.cache``) 28 | * ``${HOME}/Library/Caches`` 29 | 30 | Examples 31 | ------------ 32 | 33 | Getting Configuration 34 | ~~~~~~~~~~~~~~~~~~~~~~~~ 35 | 36 | ``configdir.ConfigDir.QueryFolderContainsFile()`` searches files in the following order: 37 | 38 | * Local path (if you add the path via LocalPath parameter) 39 | * User level configuration folder(e.g. ``$HOME/.config///setting.json`` in Linux) 40 | * System level configuration folder(e.g. ``/etc/xdg///setting.json`` in Linux) 41 | 42 | ``configdir.Config`` provides some convenient methods(``ReadFile``, ``WriteFile`` and so on). 43 | 44 | .. code-block:: go 45 | 46 | var config Config 47 | 48 | configDirs := configdir.New("vendor-name", "application-name") 49 | // optional: local path has the highest priority 50 | configDirs.LocalPath, _ = filepath.Abs(".") 51 | folder := configDirs.QueryFolderContainsFile("setting.json") 52 | if folder != nil { 53 | data, _ := folder.ReadFile("setting.json") 54 | json.Unmarshal(data, &config) 55 | } else { 56 | config = DefaultConfig 57 | } 58 | 59 | Write Configuration 60 | ~~~~~~~~~~~~~~~~~~~~~~ 61 | 62 | When storing configuration, get configuration folder by using ``configdir.ConfigDir.QueryFolders()`` method. 63 | 64 | .. code-block:: go 65 | 66 | configDirs := configdir.New("vendor-name", "application-name") 67 | 68 | var config Config 69 | data, _ := json.Marshal(&config) 70 | 71 | // Stores to local folder 72 | folders := configDirs.QueryFolders(configdir.Local) 73 | folders[0].WriteFile("setting.json", data) 74 | 75 | // Stores to user folder 76 | folders = configDirs.QueryFolders(configdir.Global) 77 | folders[0].WriteFile("setting.json", data) 78 | 79 | // Stores to system folder 80 | folders = configDirs.QueryFolders(configdir.System) 81 | folders[0].WriteFile("setting.json", data) 82 | 83 | Getting Cache Folder 84 | ~~~~~~~~~~~~~~~~~~~~~~ 85 | 86 | It is similar to the above example, but returns cache folder. 87 | 88 | .. code-block:: go 89 | 90 | configDirs := configdir.New("vendor-name", "application-name") 91 | cache := configDirs.QueryCacheFolder() 92 | 93 | resp, err := http.Get("http://examples.com/sdk.zip") 94 | if err != nil { 95 | log.Fatal(err) 96 | } 97 | defer resp.Body.Close() 98 | body, err := ioutil.ReadAll(resp.Body) 99 | 100 | cache.WriteFile("sdk.zip", body) 101 | 102 | Document 103 | ------------ 104 | 105 | https://godoc.org/github.com/shibukawa/configdir 106 | 107 | License 108 | ------------ 109 | 110 | MIT 111 | 112 | -------------------------------------------------------------------------------- /config.go: -------------------------------------------------------------------------------- 1 | // configdir provides access to configuration folder in each platforms. 2 | // 3 | // System wide configuration folders: 4 | // 5 | // - Windows: %PROGRAMDATA% (C:\ProgramData) 6 | // - Linux/BSDs: ${XDG_CONFIG_DIRS} (/etc/xdg) 7 | // - MacOSX: "/Library/Application Support" 8 | // 9 | // User wide configuration folders: 10 | // 11 | // - Windows: %APPDATA% (C:\Users\\AppData\Roaming) 12 | // - Linux/BSDs: ${XDG_CONFIG_HOME} (${HOME}/.config) 13 | // - MacOSX: "${HOME}/Library/Application Support" 14 | // 15 | // User wide cache folders: 16 | // 17 | // - Windows: %LOCALAPPDATA% (C:\Users\\AppData\Local) 18 | // - Linux/BSDs: ${XDG_CACHE_HOME} (${HOME}/.cache) 19 | // - MacOSX: "${HOME}/Library/Caches" 20 | // 21 | // configdir returns paths inside the above folders. 22 | 23 | package configdir 24 | 25 | import ( 26 | "io/ioutil" 27 | "os" 28 | "path/filepath" 29 | ) 30 | 31 | type ConfigType int 32 | 33 | const ( 34 | System ConfigType = iota 35 | Global 36 | All 37 | Existing 38 | Local 39 | Cache 40 | ) 41 | 42 | // Config represents each folder 43 | type Config struct { 44 | Path string 45 | Type ConfigType 46 | } 47 | 48 | func (c Config) Open(fileName string) (*os.File, error) { 49 | return os.Open(filepath.Join(c.Path, fileName)) 50 | } 51 | 52 | func (c Config) Create(fileName string) (*os.File, error) { 53 | err := c.CreateParentDir(fileName) 54 | if err != nil { 55 | return nil, err 56 | } 57 | return os.Create(filepath.Join(c.Path, fileName)) 58 | } 59 | 60 | func (c Config) ReadFile(fileName string) ([]byte, error) { 61 | return ioutil.ReadFile(filepath.Join(c.Path, fileName)) 62 | } 63 | 64 | // CreateParentDir creates the parent directory of fileName inside c. fileName 65 | // is a relative path inside c, containing zero or more path separators. 66 | func (c Config) CreateParentDir(fileName string) error { 67 | return os.MkdirAll(filepath.Dir(filepath.Join(c.Path, fileName)), 0755) 68 | } 69 | 70 | func (c Config) WriteFile(fileName string, data []byte) error { 71 | err := c.CreateParentDir(fileName) 72 | if err != nil { 73 | return err 74 | } 75 | return ioutil.WriteFile(filepath.Join(c.Path, fileName), data, 0644) 76 | } 77 | 78 | func (c Config) MkdirAll() error { 79 | return os.MkdirAll(c.Path, 0755) 80 | } 81 | 82 | func (c Config) Exists(fileName string) bool { 83 | _, err := os.Stat(filepath.Join(c.Path, fileName)) 84 | return !os.IsNotExist(err) 85 | } 86 | 87 | // ConfigDir keeps setting for querying folders. 88 | type ConfigDir struct { 89 | VendorName string 90 | ApplicationName string 91 | LocalPath string 92 | } 93 | 94 | func New(vendorName, applicationName string) ConfigDir { 95 | return ConfigDir{ 96 | VendorName: vendorName, 97 | ApplicationName: applicationName, 98 | } 99 | } 100 | 101 | func (c ConfigDir) joinPath(root string) string { 102 | if c.VendorName != "" && hasVendorName { 103 | return filepath.Join(root, c.VendorName, c.ApplicationName) 104 | } 105 | return filepath.Join(root, c.ApplicationName) 106 | } 107 | 108 | func (c ConfigDir) QueryFolders(configType ConfigType) []*Config { 109 | if configType == Cache { 110 | return []*Config{c.QueryCacheFolder()} 111 | } 112 | var result []*Config 113 | if c.LocalPath != "" && configType != System && configType != Global { 114 | result = append(result, &Config{ 115 | Path: c.LocalPath, 116 | Type: Local, 117 | }) 118 | } 119 | if configType != System && configType != Local { 120 | result = append(result, &Config{ 121 | Path: c.joinPath(globalSettingFolder), 122 | Type: Global, 123 | }) 124 | } 125 | if configType != Global && configType != Local { 126 | for _, root := range systemSettingFolders { 127 | result = append(result, &Config{ 128 | Path: c.joinPath(root), 129 | Type: System, 130 | }) 131 | } 132 | } 133 | if configType != Existing { 134 | return result 135 | } 136 | var existing []*Config 137 | for _, entry := range result { 138 | if _, err := os.Stat(entry.Path); !os.IsNotExist(err) { 139 | existing = append(existing, entry) 140 | } 141 | } 142 | return existing 143 | } 144 | 145 | func (c ConfigDir) QueryFolderContainsFile(fileName string) *Config { 146 | configs := c.QueryFolders(Existing) 147 | for _, config := range configs { 148 | if _, err := os.Stat(filepath.Join(config.Path, fileName)); !os.IsNotExist(err) { 149 | return config 150 | } 151 | } 152 | return nil 153 | } 154 | 155 | func (c ConfigDir) QueryCacheFolder() *Config { 156 | return &Config{ 157 | Path: c.joinPath(cacheFolder), 158 | Type: Cache, 159 | } 160 | } 161 | -------------------------------------------------------------------------------- /config_darwin.go: -------------------------------------------------------------------------------- 1 | package configdir 2 | 3 | import "os" 4 | 5 | var hasVendorName = true 6 | var systemSettingFolders = []string{"/Library/Application Support"} 7 | var globalSettingFolder = os.Getenv("HOME") + "/Library/Application Support" 8 | var cacheFolder = os.Getenv("HOME") + "/Library/Caches" 9 | -------------------------------------------------------------------------------- /config_windows.go: -------------------------------------------------------------------------------- 1 | package configdir 2 | 3 | import "os" 4 | 5 | var hasVendorName = true 6 | var systemSettingFolders = []string{os.Getenv("PROGRAMDATA")} 7 | var globalSettingFolder = os.Getenv("APPDATA") 8 | var cacheFolder = os.Getenv("LOCALAPPDATA") 9 | -------------------------------------------------------------------------------- /config_xdg.go: -------------------------------------------------------------------------------- 1 | // +build !windows,!darwin 2 | 3 | package configdir 4 | 5 | import ( 6 | "os" 7 | "path/filepath" 8 | "strings" 9 | ) 10 | 11 | // https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html 12 | 13 | var hasVendorName = true 14 | var systemSettingFolders []string 15 | var globalSettingFolder string 16 | var cacheFolder string 17 | 18 | func init() { 19 | if os.Getenv("XDG_CONFIG_HOME") != "" { 20 | globalSettingFolder = os.Getenv("XDG_CONFIG_HOME") 21 | } else { 22 | globalSettingFolder = filepath.Join(os.Getenv("HOME"), ".config") 23 | } 24 | if os.Getenv("XDG_CONFIG_DIRS") != "" { 25 | systemSettingFolders = strings.Split(os.Getenv("XDG_CONFIG_DIRS"), ":") 26 | } else { 27 | systemSettingFolders = []string{"/etc/xdg"} 28 | } 29 | if os.Getenv("XDG_CACHE_HOME") != "" { 30 | cacheFolder = os.Getenv("XDG_CACHE_HOME") 31 | } else { 32 | cacheFolder = filepath.Join(os.Getenv("HOME"), ".cache") 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /example_test.go: -------------------------------------------------------------------------------- 1 | package configdir_test 2 | 3 | import ( 4 | "encoding/json" 5 | "github.com/shibukawa/configdir" 6 | "io/ioutil" 7 | "log" 8 | "net/http" 9 | "path/filepath" 10 | ) 11 | 12 | type Config struct { 13 | UserName string `json:"user-name"` 14 | } 15 | 16 | var DefaultConfig = Config{ 17 | UserName: "baron", // Do you remember BeOS? 18 | } 19 | 20 | // Sample for reading configuration 21 | func ExampleConfigDir() { 22 | 23 | var config Config 24 | 25 | configDirs := configdir.New("vendor-name", "application-name") 26 | // optional: local path has the highest priority 27 | configDirs.LocalPath, _ = filepath.Abs(".") 28 | folder := configDirs.QueryFolderContainsFile("setting.json") 29 | if folder != nil { 30 | data, _ := folder.ReadFile("setting.json") 31 | json.Unmarshal(data, &config) 32 | } else { 33 | config = DefaultConfig 34 | } 35 | } 36 | 37 | // Sample for reading configuration 38 | func ExampleConfigDir_QueryFolders() { 39 | configDirs := configdir.New("vendor-name", "application-name") 40 | 41 | var config Config 42 | data, _ := json.Marshal(&config) 43 | 44 | // Stores to local folder 45 | folders := configDirs.QueryFolders(configdir.Local) 46 | folders[0].WriteFile("setting.json", data) 47 | 48 | // Stores to user folder 49 | folders = configDirs.QueryFolders(configdir.Global) 50 | folders[0].WriteFile("setting.json", data) 51 | 52 | // Stores to system folder 53 | folders = configDirs.QueryFolders(configdir.System) 54 | folders[0].WriteFile("setting.json", data) 55 | } 56 | 57 | // Sample for getting cache folder 58 | func ExampleConfigDir_QueryCacheFolder() { 59 | configDirs := configdir.New("vendor-name", "application-name") 60 | cache := configDirs.QueryCacheFolder() 61 | 62 | resp, err := http.Get("http://examples.com/sdk.zip") 63 | if err != nil { 64 | log.Fatal(err) 65 | } 66 | defer resp.Body.Close() 67 | body, err := ioutil.ReadAll(resp.Body) 68 | 69 | cache.WriteFile("sdk.zip", body) 70 | } 71 | --------------------------------------------------------------------------------