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