github.com/wtfutil/wtf@v0.43.0/cfg/config_files.go (about) 1 package cfg 2 3 import ( 4 "errors" 5 "fmt" 6 "os" 7 "os/user" 8 "path/filepath" 9 10 "github.com/olebedev/config" 11 ) 12 13 const ( 14 // XdgConfigDir defines the path to the minimal XDG-compatible configuration directory 15 XdgConfigDir = "~/.config/" 16 17 // WtfConfigDirV1 defines the path to the first version of configuration. Do not use this 18 WtfConfigDirV1 = "~/.wtf/" 19 20 // WtfConfigDirV2 defines the path to the second version of the configuration. Use this. 21 WtfConfigDirV2 = "~/.config/wtf/" 22 23 // WtfConfigFile defines the name of the default config file 24 WtfConfigFile = "config.yml" 25 ) 26 27 /* -------------------- Exported Functions -------------------- */ 28 29 // CreateFile creates the named file in the config directory, if it does not already exist. 30 // If the file exists it does not recreate it. 31 // If successful, returns the absolute path to the file 32 // If unsuccessful, returns an error 33 func CreateFile(fileName string) (string, error) { 34 configDir, err := WtfConfigDir() 35 if err != nil { 36 return "", err 37 } 38 39 filePath := filepath.Join(configDir, fileName) 40 41 // Check if the file already exists; if it does not, create it 42 _, err = os.Stat(filePath) 43 if err != nil { 44 if os.IsNotExist(err) { 45 _, err = os.Create(filePath) 46 if err != nil { 47 return "", err 48 } 49 } else { 50 return "", err 51 } 52 } 53 54 return filePath, nil 55 } 56 57 // Initialize takes care of settings up the initial state of WTF configuration 58 // It ensures necessary directories and files exist 59 func Initialize(hasCustom bool) { 60 if !hasCustom { 61 migrateOldConfig() 62 } 63 64 // These always get created because this is where modules should write any permanent 65 // data they need to persist between runs (i.e.: log, textfile, etc.) 66 createWtfConfigDir() 67 68 if !hasCustom { 69 createWtfConfigFile() 70 chmodConfigFile() 71 } 72 } 73 74 // WtfConfigDir returns the absolute path to the configuration directory 75 func WtfConfigDir() (string, error) { 76 configDir := os.Getenv("XDG_CONFIG_HOME") 77 if configDir == "" { 78 configDir = WtfConfigDirV2 79 } else { 80 configDir += "/wtf/" 81 } 82 configDir, err := expandHomeDir(configDir) 83 if err != nil { 84 return "", err 85 } 86 87 return configDir, nil 88 } 89 90 // LoadWtfConfigFile loads the specified config file 91 func LoadWtfConfigFile(filePath string) *config.Config { 92 absPath, _ := expandHomeDir(filePath) 93 94 cfg, err := config.ParseYamlFile(absPath) 95 if err != nil { 96 displayWtfConfigFileLoadError(absPath, err) 97 os.Exit(1) 98 } 99 100 return cfg 101 } 102 103 /* -------------------- Unexported Functions -------------------- */ 104 105 // chmodConfigFile sets the mode of the config file to r+w for the owner only 106 func chmodConfigFile() { 107 configDir, _ := WtfConfigDir() 108 relPath := filepath.Join(configDir, WtfConfigFile) 109 absPath, _ := expandHomeDir(relPath) 110 111 _, err := os.Stat(absPath) 112 if err != nil && os.IsNotExist(err) { 113 return 114 } 115 116 err = os.Chmod(absPath, 0600) 117 if err != nil { 118 return 119 } 120 } 121 122 // createWtfConfigDir creates the necessary directories for storing the default config file 123 // If ~/.config/wtf is missing, it will try to create it 124 func createWtfConfigDir() { 125 wtfConfigDir, _ := WtfConfigDir() 126 127 if _, err := os.Stat(wtfConfigDir); os.IsNotExist(err) { 128 err := os.MkdirAll(wtfConfigDir, os.ModePerm) 129 if err != nil { 130 displayWtfConfigDirCreateError(err) 131 os.Exit(1) 132 } 133 } 134 } 135 136 // createWtfConfigFile creates a simple config file in the config directory if 137 // one does not already exist 138 func createWtfConfigFile() { 139 filePath, err := CreateFile(WtfConfigFile) 140 if err != nil { 141 displayDefaultConfigCreateError(err) 142 os.Exit(1) 143 } 144 145 // If the file is empty, write to it 146 file, _ := os.Stat(filePath) 147 148 if file.Size() == 0 { 149 if os.WriteFile(filePath, []byte(defaultConfigFile), 0600) != nil { 150 displayDefaultConfigWriteError(err) 151 os.Exit(1) 152 } 153 } 154 } 155 156 // Expand expands the path to include the home directory if the path 157 // is prefixed with `~`. If it isn't prefixed with `~`, the path is 158 // returned as-is. 159 func expandHomeDir(path string) (string, error) { 160 if path == "" { 161 return path, nil 162 } 163 164 if path[0] != '~' { 165 return path, nil 166 } 167 168 if len(path) > 1 && path[1] != '/' && path[1] != '\\' { 169 return "", errors.New("cannot expand user-specific home dir") 170 } 171 172 dir, err := home() 173 if err != nil { 174 return "", err 175 } 176 177 return filepath.Join(dir, path[1:]), nil 178 } 179 180 // Dir returns the home directory for the executing user. 181 // An error is returned if a home directory cannot be detected. 182 func home() (string, error) { 183 currentUser, err := user.Current() 184 if err != nil { 185 return "", err 186 } 187 if currentUser.HomeDir == "" { 188 return "", errors.New("cannot find user-specific home dir") 189 } 190 191 return currentUser.HomeDir, nil 192 } 193 194 // migrateOldConfig copies any existing configuration from the old location 195 // to the new, XDG-compatible location 196 func migrateOldConfig() { 197 srcDir, _ := expandHomeDir(WtfConfigDirV1) 198 destDir, _ := WtfConfigDir() 199 200 // If the old config directory doesn't exist, do not move 201 if _, err := os.Stat(srcDir); os.IsNotExist(err) { 202 return 203 } 204 205 // If the new config directory already exists, do not move 206 if _, err := os.Stat(destDir); err == nil { 207 return 208 } 209 210 // Time to move 211 err := Copy(srcDir, destDir) 212 if err != nil { 213 panic(err) 214 } 215 216 // Delete the old directory if the new one exists 217 if _, err := os.Stat(destDir); err == nil { 218 err := os.RemoveAll(srcDir) 219 if err != nil { 220 fmt.Println(err) 221 } 222 } 223 }