1package config 2 3import ( 4 "fmt" 5 "os" 6 "os/exec" 7 "path/filepath" 8 "runtime" 9 "github.com/BurntSushi/toml" 10 "github.com/pkg/errors" 11) 12 13// Conf is global config variable 14var Conf Config 15 16// Config is a struct of config 17type Config struct { 18 General GeneralConfig `toml:"General"` 19 Gist GistConfig `toml:"Gist"` 20 GitLab GitLabConfig `toml:"GitLab"` 21} 22 23// GeneralConfig is a struct of general config 24type GeneralConfig struct { 25 SnippetFile string `toml:"snippetfile"` 26 Editor string `toml:"editor"` 27 Column int `toml:"column"` 28 SelectCmd string `toml:"selectcmd"` 29 Backend string `toml:"backend"` 30 SortBy string `toml:"sortby"` 31} 32 33// GistConfig is a struct of config for Gist 34type GistConfig struct { 35 FileName string `toml:"file_name"` 36 AccessToken string `toml:"access_token"` 37 GistID string `toml:"gist_id"` 38 Public bool `toml:"public"` 39 AutoSync bool `toml:"auto_sync"` 40} 41 42// GitLabConfig is a struct of config for GitLabSnippet 43type GitLabConfig struct { 44 FileName string `toml:"file_name"` 45 AccessToken string `toml:"access_token"` 46 Url string `toml:"url"` 47 ID string `toml:"id"` 48 Visibility string `toml:"visibility"` 49 AutoSync bool `toml:"auto_sync"` 50 Insecure bool `toml:"skip_ssl"` 51} 52 53// Flag is global flag variable 54var Flag FlagConfig 55 56// FlagConfig is a struct of flag 57type FlagConfig struct { 58 Debug bool 59 Query string 60 FilterTag string 61 Command bool 62 Delimiter string 63 OneLine bool 64 Color bool 65 Tag bool 66} 67 68// Load loads a config toml 69func (cfg *Config) Load(file string) error { 70 _, err := os.Stat(file) 71 if err == nil { 72 _, err := toml.DecodeFile(file, cfg) 73 if err != nil { 74 return err 75 } 76 cfg.General.SnippetFile = expandPath(cfg.General.SnippetFile) 77 return nil 78 } 79 80 if !os.IsNotExist(err) { 81 return err 82 } 83 f, err := os.Create(file) 84 if err != nil { 85 return err 86 } 87 88 dir, err := GetDefaultConfigDir() 89 if err != nil { 90 return errors.Wrap(err, "Failed to get the default config directory") 91 } 92 cfg.General.SnippetFile = filepath.Join(dir, "snippet.toml") 93 _, err = os.Create(cfg.General.SnippetFile) 94 if err != nil { 95 return errors.Wrap(err, "Failed to create a config file") 96 } 97 98 cfg.General.Editor = os.Getenv("EDITOR") 99 if cfg.General.Editor == "" && runtime.GOOS != "windows" { 100 if isCommandAvailable("sensible-editor") { 101 cfg.General.Editor = "sensible-editor" 102 } else { 103 cfg.General.Editor = "vim" 104 } 105 } 106 cfg.General.Column = 40 107 cfg.General.SelectCmd = "fzf" 108 cfg.General.Backend = "gist" 109 110 cfg.Gist.FileName = "pet-snippet.toml" 111 112 cfg.GitLab.FileName = "pet-snippet.toml" 113 cfg.GitLab.Visibility = "private" 114 115 return toml.NewEncoder(f).Encode(cfg) 116} 117 118// GetDefaultConfigDir returns the default config directory 119func GetDefaultConfigDir() (dir string, err error) { 120 if runtime.GOOS == "windows" { 121 dir = os.Getenv("APPDATA") 122 if dir == "" { 123 dir = filepath.Join(os.Getenv("USERPROFILE"), "Application Data", "pet") 124 } 125 dir = filepath.Join(dir, "pet") 126 } else { 127 dir = filepath.Join(os.Getenv("HOME"), ".config", "pet") 128 } 129 if err := os.MkdirAll(dir, 0700); err != nil { 130 return "", fmt.Errorf("cannot create directory: %v", err) 131 } 132 return dir, nil 133} 134 135func expandPath(s string) string { 136 if len(s) >= 2 && s[0] == '~' && os.IsPathSeparator(s[1]) { 137 if runtime.GOOS == "windows" { 138 s = filepath.Join(os.Getenv("USERPROFILE"), s[2:]) 139 } else { 140 s = filepath.Join(os.Getenv("HOME"), s[2:]) 141 } 142 } 143 return os.Expand(s, os.Getenv) 144} 145 146func isCommandAvailable(name string) bool { 147 cmd := exec.Command("/bin/sh", "-c", "command -v "+name) 148 if err := cmd.Run(); err != nil { 149 return false 150 } 151 return true 152} 153