From 48e0b61d8cc0a63aecff14f774ff3faf696df021 Mon Sep 17 00:00:00 2001 From: g3rk6 Date: Tue, 8 Sep 2015 03:25:36 -0400 Subject: [PATCH 1/8] Added method to write into TOML file. --- viper.go | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/viper.go b/viper.go index 389a58e..270abcc 100644 --- a/viper.go +++ b/viper.go @@ -20,6 +20,7 @@ package viper import ( + "bufio" "bytes" "fmt" "io" @@ -31,6 +32,7 @@ import ( "strings" "time" + "github.com/BurntSushi/toml" "github.com/kr/pretty" "github.com/mitchellh/mapstructure" "github.com/spf13/cast" @@ -874,6 +876,32 @@ func (v *Viper) InConfig(key string) bool { return exists } +// Save configuration to file +func SaveConfig() error { return v.SaveConfig() } +func (v *Viper) SaveConfig() error { + + jww.INFO.Println("Attempting to write config into the file") + if !stringInSlice(v.getConfigType(), SupportedExts) { + return UnsupportedConfigError(v.getConfigType()) + } + + f, err := os.Create(v.getConfigFile()) + defer f.Close() + + if err != nil { + return err + } + + w := bufio.NewWriter(f) + + if err := toml.NewEncoder(w).Encode(v.AllSettings()); err != nil { + jww.FATAL.Println("Panic while writing into the file") + } + w.Flush() + + return nil +} + // Set the default value for this key. // Default only used when no value is provided by the user via flag, config or ENV. func SetDefault(key string, value interface{}) { v.SetDefault(key, value) } From d5c009456e5a320255bbfb753b12714be252a3b4 Mon Sep 17 00:00:00 2001 From: g3rk6 Date: Sun, 20 Sep 2015 02:24:06 -0400 Subject: [PATCH 2/8] Added functionality to export configuration based on config type. The feature supports JSON and TOML. --- viper.go | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/viper.go b/viper.go index 270abcc..7c542af 100644 --- a/viper.go +++ b/viper.go @@ -22,6 +22,7 @@ package viper import ( "bufio" "bytes" + "encoding/json" "fmt" "io" "io/ioutil" @@ -880,7 +881,7 @@ func (v *Viper) InConfig(key string) bool { func SaveConfig() error { return v.SaveConfig() } func (v *Viper) SaveConfig() error { - jww.INFO.Println("Attempting to write config into the file") + jww.INFO.Println("Attempting to write config into the file.") if !stringInSlice(v.getConfigType(), SupportedExts) { return UnsupportedConfigError(v.getConfigType()) } @@ -892,12 +893,23 @@ func (v *Viper) SaveConfig() error { return err } - w := bufio.NewWriter(f) + switch v.getConfigType() { + case "json": - if err := toml.NewEncoder(w).Encode(v.AllSettings()); err != nil { - jww.FATAL.Println("Panic while writing into the file") + b, err := json.MarshalIndent(v.AllSettings(), "", " ") + if err != nil { + jww.FATAL.Println("Panic while encoding into JSON format.") + } + f.WriteString(string(b)) + + case "toml": + + w := bufio.NewWriter(f) + if err := toml.NewEncoder(w).Encode(v.AllSettings()); err != nil { + jww.FATAL.Println("Panic while encoding into TOML format.") + } + w.Flush() } - w.Flush() return nil } @@ -930,6 +942,8 @@ func (v *Viper) ReadInConfig() error { return UnsupportedConfigError(v.getConfigType()) } + jww.DEBUG.Println("Reading file: ", v.getConfigFile()) + file, err := ioutil.ReadFile(v.getConfigFile()) if err != nil { return err From 11ae2b93920f8a790b92bc1fc39d64e827290f6a Mon Sep 17 00:00:00 2001 From: g3rk6 Date: Sun, 10 Jan 2016 21:59:10 -0500 Subject: [PATCH 3/8] Added method to write into YAML file. --- viper.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/viper.go b/viper.go index 7c542af..9a2ace3 100644 --- a/viper.go +++ b/viper.go @@ -40,6 +40,7 @@ import ( jww "github.com/spf13/jwalterweatherman" "github.com/spf13/pflag" "gopkg.in/fsnotify.v1" + "gopkg.in/yaml.v2" ) var v *Viper @@ -909,6 +910,14 @@ func (v *Viper) SaveConfig() error { jww.FATAL.Println("Panic while encoding into TOML format.") } w.Flush() + + case "yaml", "yml": + + b, err := yaml.Marshal(v.AllSettings()) + if err != nil { + jww.FATAL.Println("Panic while encoding into YAML format.") + } + f.WriteString(string(b)) } return nil From 2715727d1f8fa834985b76ec43e2262cf79978bb Mon Sep 17 00:00:00 2001 From: g3rk6 Date: Mon, 25 Jan 2016 22:56:20 -0500 Subject: [PATCH 4/8] Fixed the issue of incorrect defer and error checking order. The error checking must be first otherwise it will cause panic. --- viper.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/viper.go b/viper.go index 9a2ace3..4eefc53 100644 --- a/viper.go +++ b/viper.go @@ -888,11 +888,10 @@ func (v *Viper) SaveConfig() error { } f, err := os.Create(v.getConfigFile()) - defer f.Close() - if err != nil { return err } + defer f.Close() switch v.getConfigType() { case "json": From 4fae23fe25d66405f30bad05c82be46c7ad4fa7a Mon Sep 17 00:00:00 2001 From: Herkermer Sherwood Date: Thu, 8 Dec 2016 03:00:18 -0800 Subject: [PATCH 5/8] Add WriteConfig methods --- util.go | 47 +++++++++++++++++++++++++++++++- viper.go | 82 +++++++++++++++++++++++--------------------------------- 2 files changed, 80 insertions(+), 49 deletions(-) diff --git a/util.go b/util.go index 3ebada9..9d7d6d5 100644 --- a/util.go +++ b/util.go @@ -21,12 +21,14 @@ import ( "strings" "unicode" + yaml "gopkg.in/yaml.v2" + "github.com/hashicorp/hcl" "github.com/magiconair/properties" toml "github.com/pelletier/go-toml" + "github.com/spf13/afero" "github.com/spf13/cast" jww "github.com/spf13/jwalterweatherman" - "gopkg.in/yaml.v2" ) // ConfigParseError denotes failing to parse configuration file. @@ -39,6 +41,16 @@ func (pe ConfigParseError) Error() string { return fmt.Sprintf("While parsing config: %s", pe.err.Error()) } +// ConfigMarshalError happens when failing to marshal the configuration. +type ConfigMarshalError struct { + err error +} + +// Error returns the formatted configuration error. +func (e ConfigMarshalError) Error() string { + return fmt.Sprintf("While marshaling config: %s", e.err.Error()) +} + // toCaseInsensitiveValue checks if the value is a map; // if so, create a copy and lower-case the keys recursively. func toCaseInsensitiveValue(value interface{}) interface{} { @@ -152,6 +164,39 @@ func userHomeDir() string { return os.Getenv("HOME") } +func marshalConfigWriter(out afero.File, c map[string]interface{}, configType string) error { + switch configType { + case "json": + b, err := json.MarshalIndent(v.AllSettings(), "", " ") + if err != nil { + return ConfigMarshalError{err} + } + _, err = out.WriteString(string(b)) + if err != nil { + return ConfigMarshalError{err} + } + + // case "toml": + // w := bufio.NewWriter(out) + // if err := toml.NewEncoder(w).Encode(v.AllSettings()); err != nil { + // return ConfigMarshalError{err} + // } + // w.Flush() + + case "yaml", "yml": + b, err := yaml.Marshal(v.AllSettings()) + if err != nil { + return ConfigMarshalError{err} + } + _, err = out.WriteString(string(b)) + if err != nil { + return ConfigMarshalError{err} + } + } + + return nil +} + func unmarshallConfigReader(in io.Reader, c map[string]interface{}, configType string) error { buf := new(bytes.Buffer) buf.ReadFrom(in) diff --git a/viper.go b/viper.go index 04f55ea..a07d8f5 100644 --- a/viper.go +++ b/viper.go @@ -20,9 +20,7 @@ package viper import ( - "bufio" "bytes" - "encoding/json" "fmt" "io" "log" @@ -32,8 +30,6 @@ import ( "strings" "time" - "github.com/BurntSushi/toml" - "github.com/fsnotify/fsnotify" "github.com/mitchellh/mapstructure" "github.com/spf13/afero" "github.com/spf13/cast" @@ -1044,50 +1040,6 @@ func (v *Viper) InConfig(key string) bool { return exists } -// Save configuration to file -func SaveConfig() error { return v.SaveConfig() } -func (v *Viper) SaveConfig() error { - - jww.INFO.Println("Attempting to write config into the file.") - if !stringInSlice(v.getConfigType(), SupportedExts) { - return UnsupportedConfigError(v.getConfigType()) - } - - f, err := os.Create(v.getConfigFile()) - if err != nil { - return err - } - defer f.Close() - - switch v.getConfigType() { - case "json": - - b, err := json.MarshalIndent(v.AllSettings(), "", " ") - if err != nil { - jww.FATAL.Println("Panic while encoding into JSON format.") - } - f.WriteString(string(b)) - - case "toml": - - w := bufio.NewWriter(f) - if err := toml.NewEncoder(w).Encode(v.AllSettings()); err != nil { - jww.FATAL.Println("Panic while encoding into TOML format.") - } - w.Flush() - - case "yaml", "yml": - - b, err := yaml.Marshal(v.AllSettings()) - if err != nil { - jww.FATAL.Println("Panic while encoding into YAML format.") - } - f.WriteString(string(b)) - } - - return nil -} - // SetDefault sets the default value for this key. // SetDefault is case-insensitive for a key. // Default only used when no value is provided by the user via flag, config or ENV. @@ -1190,6 +1142,31 @@ func (v *Viper) MergeConfig(in io.Reader) error { return nil } +// WriteConfig writes the current configuration to a file. +func WriteConfig() error { return v.WriteConfig() } +func (v *Viper) WriteConfig() error { + + jww.INFO.Println("Attempting to write config into the file.") + // filename, err := v.getConfigFile() + filename := "out.yaml" + // if err != nil { + // return err + // } + if !stringInSlice(v.getConfigType(), SupportedExts) { + return UnsupportedConfigError(v.getConfigType()) + } + if v.config == nil { + v.config = make(map[string]interface{}) + } + + file, err := v.fs.OpenFile(filename, os.O_CREATE|os.O_WRONLY, os.FileMode(0644)) + if err != nil { + return err + } + + return v.marshalWriter(file, v.config) +} + func keyExists(k string, m map[string]interface{}) string { lk := strings.ToLower(k) for mk := range m { @@ -1308,6 +1285,15 @@ func (v *Viper) unmarshalReader(in io.Reader, c map[string]interface{}) error { return unmarshallConfigReader(in, c, v.getConfigType()) } +// Marshal a map into Writer. +func marshalWriter(out afero.File, c map[string]interface{}) error { + return v.marshalWriter(out, c) +} + +func (v *Viper) marshalWriter(out afero.File, c map[string]interface{}) error { + return marshalConfigWriter(out, c, v.getConfigType()) +} + func (v *Viper) insensitiviseMaps() { insensitiviseMap(v.config) insensitiviseMap(v.defaults) From c4b0efecc9f918454cb50781d1528cbf6aac6479 Mon Sep 17 00:00:00 2001 From: Herkermer Sherwood Date: Thu, 8 Dec 2016 17:07:08 -0800 Subject: [PATCH 6/8] Add support for toml --- util.go | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/util.go b/util.go index 9d7d6d5..4178ca4 100644 --- a/util.go +++ b/util.go @@ -164,31 +164,37 @@ func userHomeDir() string { return os.Getenv("HOME") } -func marshalConfigWriter(out afero.File, c map[string]interface{}, configType string) error { +func marshalConfigWriter(f afero.File, c map[string]interface{}, configType string) error { switch configType { case "json": b, err := json.MarshalIndent(v.AllSettings(), "", " ") if err != nil { return ConfigMarshalError{err} } - _, err = out.WriteString(string(b)) + _, err = f.WriteString(string(b)) if err != nil { return ConfigMarshalError{err} } - // case "toml": - // w := bufio.NewWriter(out) - // if err := toml.NewEncoder(w).Encode(v.AllSettings()); err != nil { - // return ConfigMarshalError{err} - // } - // w.Flush() + case "hcl": + // TODO: How to convert map to hcl??? + + case "prop", "props", "properties": + + case "toml": + t := toml.TreeFromMap(v.AllSettings()) + s := t.String() + _, err := f.WriteString(s) + if err != nil { + return ConfigMarshalError{err} + } case "yaml", "yml": b, err := yaml.Marshal(v.AllSettings()) if err != nil { return ConfigMarshalError{err} } - _, err = out.WriteString(string(b)) + _, err = f.WriteString(string(b)) if err != nil { return ConfigMarshalError{err} } From 6b4d3dc059dc2fd4ecca0b56ab05b908e5a651c7 Mon Sep 17 00:00:00 2001 From: Herkermer Sherwood Date: Thu, 8 Dec 2016 17:07:21 -0800 Subject: [PATCH 7/8] Remove unused import --- viper.go | 1 - 1 file changed, 1 deletion(-) diff --git a/viper.go b/viper.go index a07d8f5..9a1b7d8 100644 --- a/viper.go +++ b/viper.go @@ -36,7 +36,6 @@ import ( jww "github.com/spf13/jwalterweatherman" "github.com/spf13/pflag" "gopkg.in/fsnotify.v1" - "gopkg.in/yaml.v2" ) var v *Viper From aa295b144aedcbb0a6ee156239ac777ca09c7533 Mon Sep 17 00:00:00 2001 From: Herkermer Sherwood Date: Thu, 8 Dec 2016 17:07:43 -0800 Subject: [PATCH 8/8] Add shared write function and safe methods --- viper.go | 82 ++++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 62 insertions(+), 20 deletions(-) diff --git a/viper.go b/viper.go index 9a1b7d8..93d0a6f 100644 --- a/viper.go +++ b/viper.go @@ -1141,31 +1141,73 @@ func (v *Viper) MergeConfig(in io.Reader) error { return nil } -// WriteConfig writes the current configuration to a file. -func WriteConfig() error { return v.WriteConfig() } -func (v *Viper) WriteConfig() error { - - jww.INFO.Println("Attempting to write config into the file.") - // filename, err := v.getConfigFile() - filename := "out.yaml" - // if err != nil { - // return err - // } - if !stringInSlice(v.getConfigType(), SupportedExts) { - return UnsupportedConfigError(v.getConfigType()) +func writeConfig(filename string, force bool) error { return v.writeConfig(filename, force) } +func (v *Viper) writeConfig(filename string, force bool) error { + jww.INFO.Println("Attempting to write configuration to file.") + ext := filepath.Ext(filename) + if len(ext) <= 1 { + return fmt.Errorf("Filename: %s requires valid extension.", filename) + } + configType := ext[1:] + if !stringInSlice(configType, SupportedExts) { + return UnsupportedConfigError(configType) } if v.config == nil { v.config = make(map[string]interface{}) } - - file, err := v.fs.OpenFile(filename, os.O_CREATE|os.O_WRONLY, os.FileMode(0644)) + var flags int + if force == true { + flags = os.O_CREATE | os.O_WRONLY + } else { + if _, err := os.Stat(filename); os.IsNotExist(err) { + flags = os.O_WRONLY + } else { + return fmt.Errorf("File: %s exists. Use WriteConfig to overwrite.", filename) + } + } + f, err := v.fs.OpenFile(filename, flags, os.FileMode(0644)) if err != nil { return err } - - return v.marshalWriter(file, v.config) + return v.marshalWriter(f, v.config, configType) } +// WriteConfig writes the current configuration to a file. +func WriteConfig() error { return v.WriteConfig() } +func (v *Viper) WriteConfig() error { + filename, err := v.getConfigFile() + if err != nil { + return err + } + return v.writeConfig(filename, true) +} + +// SafeWriteConfig writes current configuration to file only if the file does not exist. +func SafeWriteConfig() error { return v.SafeWriteConfig() } +func (v *Viper) SafeWriteConfig() error { + filename, err := v.getConfigFile() + if err != nil { + return err + } + return v.writeConfig(filename, false) +} + +// WriteConfigAs writes current configuration to a given filename. +func WriteConfigAs(filename string) error { return v.WriteConfigAs(filename) } +func (v *Viper) WriteConfigAs(filename string) error { + return v.writeConfig(filename, true) +} + +// SafeWriteConfigAs writes current configuration to a given filename is it does not exist. +func SafeWriteConfigAs(filename string) error { return v.SafeWriteConfigAs(filename) } +func (v *Viper) SafeWriteConfigAs(filename string) error { + return v.writeConfig(filename, false) +} + +// WriteConfigKey writes given key and value to file. + +// WriteConfigKeyAs writes given key and value to a given filename. + func keyExists(k string, m map[string]interface{}) string { lk := strings.ToLower(k) for mk := range m { @@ -1285,12 +1327,12 @@ func (v *Viper) unmarshalReader(in io.Reader, c map[string]interface{}) error { } // Marshal a map into Writer. -func marshalWriter(out afero.File, c map[string]interface{}) error { - return v.marshalWriter(out, c) +func marshalWriter(out afero.File, c map[string]interface{}, configType string) error { + return v.marshalWriter(out, c, configType) } -func (v *Viper) marshalWriter(out afero.File, c map[string]interface{}) error { - return marshalConfigWriter(out, c, v.getConfigType()) +func (v *Viper) marshalWriter(out afero.File, c map[string]interface{}, configType string) error { + return marshalConfigWriter(out, c, configType) } func (v *Viper) insensitiviseMaps() {