diff --git a/register.go b/register.go new file mode 100644 index 0000000..2e8a158 --- /dev/null +++ b/register.go @@ -0,0 +1,19 @@ +package viper + +type RegisteredConfig struct { + Key string + CanBeNil bool + OnUpdate func(e *Event) + OnUpdateFailed func(e *Event) + Schema *interface{} + Validator func(interface{}) bool +} + +func (v *Viper) Register(r []RegisteredConfig) { + if v.registered == nil { + v.registered = make(map[string]RegisteredConfig) + } + for _, config := range r { + v.registered[config.Key] = config + } +} diff --git a/viper.go b/viper.go index fa6f3e3..2b86afb 100644 --- a/viper.go +++ b/viper.go @@ -22,6 +22,7 @@ package viper import ( "bytes" "encoding/csv" + js "encoding/json" "errors" "fmt" "io" @@ -223,6 +224,8 @@ type Viper struct { // TODO: should probably be protected with a mutex encoderRegistry *encoding.EncoderRegistry decoderRegistry *encoding.DecoderRegistry + + registered map[string]RegisteredConfig } // New returns an initialized Viper instance. @@ -474,10 +477,57 @@ func (v *Viper) WatchConfig() { (event.Has(fsnotify.Write) || event.Has(fsnotify.Create))) || (currentConfigFile != "" && currentConfigFile != realConfigFile) { realConfigFile = currentConfigFile - err := v.ReadInConfig() + tempViper := New() + tempViper.AddConfigPath(realConfigFile) + err := tempViper.ReadInConfig() if err != nil { log.Printf("error reading config file: %v\n", err) } + + for key, config := range v.registered { + oldValue := v.Get(key) + newValue := tempViper.Get(key) + // Check exist + if newValue == nil && !config.CanBeNil { + if config.OnUpdateFailed != nil { + config.OnUpdateFailed(&Event{ + old: oldValue, + new: nil, + }) + } + continue + } + + // Type check & convert + newValueJson, _ := js.Marshal(newValue) + err = js.Unmarshal(newValueJson, config.Schema) + if err != nil { + config.OnUpdateFailed(&Event{ + old: oldValue, + new: nil, + }) + continue + } + + // Validation + if !config.Validator(config.Schema) { + config.OnUpdateFailed(&Event{ + old: oldValue, + new: nil, + }) + continue + } + + // Success + v.Set(key, config.Schema) + if config.OnUpdate != nil { + config.OnUpdate(&Event{ + new: config.Schema, + old: oldValue, + }) + } + } + if v.onConfigChange != nil { v.onConfigChange(event) } diff --git a/watch.go b/watch.go index 1ce84ea..5e04479 100644 --- a/watch.go +++ b/watch.go @@ -10,3 +10,16 @@ type watcher = fsnotify.Watcher func newWatcher() (*watcher, error) { return fsnotify.NewWatcher() } + +type Event struct { + new interface{} + old interface{} +} + +func (s *Event) New() interface{} { + return s.new +} + +func (s *Event) Old() interface{} { + return s.old +}