diff --git a/config.go b/config.go new file mode 100644 index 0000000..30d7089 --- /dev/null +++ b/config.go @@ -0,0 +1,132 @@ +// Package userConfig eases the use of config files in a user's home directory +package userConfig + +import ( + "bufio" + "errors" + "fmt" + "os" + "strings" + + "launchpad.net/go-xdg" +) + +// Config is a stuct for managing the config +type Config struct { + name string + generalConfig *ConfigFile + // ConfigFiles are files that have key/value pairs + ConfigFiles []ConfigFile + // RawFiles are other files (dbs, etc.) + RawFiles map[string]string +} + +// NewConfig generates a Config struct +func NewConfig(name string) (*Config, error) { + c := &Config{name: name} + if err := c.Load(); err != nil { + return nil, err + } + return c, nil +} + +// Set at the config level sets a value in the .conf file +func (c *Config) Set(k, v string) error { + return c.generalConfig.Set(k, v) +} + +// Get at the config level retrieves a value from the .conf file +func (c *Config) Get(k string) string { + return c.generalConfig.Get(k) +} + +// Load loads config files into the config +func (c *Config) Load() error { + var err error + // Clear the data, we're reloading + c.ConfigFiles = c.ConfigFiles[:0] + c.RawFiles = make(map[string]string) + var conf generalConfig + c.data = conf + + if strings.TrimSpace(c.name) == "" { + return errors.New("Invalid Config Name: " + c.name) + } + + var cfgPath string + cfgPath = xdg.Config.Dirs()[0] + if cfgPath != "" { + cfgPath = cfgPath + "/" + c.name + if err = c.verifyOrCreateDirectory(cfgPath); err != nil { + return err + } + // We always have a .conf file + cfgPath = cfgPath + "/" + c.name + ".conf" + } + // Load general config + if c.generalConfig, err = NewConfigFile(c.name, cfgPath, c.data); err != nil { + return err + } + + return nil +} + +// Save writes the config to file(s) +func (c *Config) Save() error { + var err error + c.generalConfig.Save() + + var cfgPath string + var configLines []string + //configLines = append(configLines, "server="+client.ServerAddr) + //configLines = append(configLines, "key="+client.ServerKey) + cfgPath = os.Getenv("HOME") + if cfgPath != "" { + cfgPath = cfgPath + "/.config" + if err := c.verifyOrCreateDirectory(cfgPath); err != nil { + return err + } + cfgPath = cfgPath + "/" + c.name + } + if cfgPath != "" { + file, err := os.Create(cfgPath) + if err != nil { + // Couldn't load config even though one was specified + return err + } + defer file.Close() + + w := bufio.NewWriter(file) + for _, line := range configLines { + fmt.Fprintln(w, line) + } + if err = w.Flush(); err != nil { + return err + } + } + return nil +} + +// verifyOrCreateDirectory is a helper function for building an +// individual directory +func (c *Config) verifyOrCreateDirectory(path string) error { + var tstDir *os.File + var tstDirInfo os.FileInfo + var err error + if tstDir, err = os.Open(path); err != nil { + if err = os.Mkdir(path, 0755); err != nil { + return err + } + if tstDir, err = os.Open(path); err != nil { + return err + } + } + if tstDirInfo, err = tstDir.Stat(); err != nil { + return err + } + if !tstDirInfo.IsDir() { + return errors.New(path + " exists and is not a directory") + } + // We were able to open the path and it was a directory + return nil +} \ No newline at end of file diff --git a/config_file.go b/config_file.go new file mode 100644 index 0000000..217b7f6 --- /dev/null +++ b/config_file.go @@ -0,0 +1,112 @@ +// Package userConfig eases the use of config files in a user's home directory +package userConfig + +import ( + "bufio" + "bytes" + "errors" + "fmt" + "io/ioutil" + "os" + "strings" + + "github.com/BurntSushi/toml" +) + +type generalConfig struct { + ConfigFiles []string `toml:"additional_config"` + RawFiles []string `toml:"raw_files"` + Values map[string]string `toml:"general"` +} + +// ConfigFile is a file that has key/value pairs in the config +type ConfigFile struct { + Name string + Path string + data interface{} +} + +// NewConfigFile generates a Config struct +func NewConfigFile(name, path string, dt interface{}) (*ConfigFile, error) { + cf := &ConfigFile{Name: name, Path: path, data: dt} + if err := cf.Load(); err != nil { + return nil, err + } + return cf, nil +} + +// Set sets a key/value pair in cf, if unable to save, revert to old value +// (and return the error) +func (cf *ConfigFile) Set(k, v string) error { + oldVal := cf.values[k] + cf.values[k] = v + if err := cf.Save(); err != nil { + cf.values[k] = oldVal + } + return err +} + +// Get gets a key/value pair from cf +func (cf *ConfigFile) Get(k string) string { + return cf.values[k] +} + +// Load loads config files into the config +func (cf *ConfigFile) Load() error { + if strings.TrimSpace(cf.Name) == "" && strings.TrimSpace(cf.Path) { + return errors.New("Invalid ConfigFile Name: " + cf.Path + "/" + cf.Name) + } + + // Config files end with .conf + cfgPath := cf.Path + "/" + cf.Name + ".conf" + tomlData, err := ioutil.ReadFile(cfgPath) + if err != nil { + return err + } + if _, err := toml.Decode(tomlData, &cf.data); err != nil { + return err + } + return nil +} + +// Save writes the config to file(s) +func (cf *ConfigFile) Save() error { + if strings.TrimSpace(cf.Name) == "" && strings.TrimSpace(cf.Path) { + return errors.New("Invalid ConfigFile Name: " + cf.Path + "/" + cf.Name + ".conf") + } + + filePath := cf.path + "/" + cf.Name + ".conf" + var err error + buf := new(bytes.Buffer) + enc := toml.NewEncoder(buf).Encode(data) + err = ioutil.WriteFile(filePath, buf, 0644) + if err != nil { + return err + } + + cfgPath = os.Getenv("HOME") + if cfgPath != "" { + cfgPath = cfgPath + "/.config" + if err := c.verifyOrCreateDirectory(cfgPath); err != nil { + return err + } + cfgPath = cfgPath + "/" + c.name + } + if cfgPath != "" { + file, err := os.Create(cfgPath) + if err != nil { + // Couldn't load config even though one was specified + return err + } + defer file.Close() + + w := bufio.NewWriter(file) + for _, line := range configLines { + fmt.Fprintln(w, line) + } + if err = w.Flush(); err != nil { + return err + } + } + return nil +} \ No newline at end of file