All checks were successful
		
		
	
	test / test (push) Successful in 42s
				
			## Pull Request template Please, go through these steps before clicking submit on this PR. 1. Give a descriptive title to your PR. 2. Provide a description of your changes. 3. Make sure you have some relevant tests. 4. Put `closes #XXXX` in your comment to auto-close the issue that your PR fixes (if applicable). **PLEASE REMOVE THIS TEMPLATE BEFORE SUBMITTING** Reviewed-on: #369 Co-authored-by: Evstigneev Denis <danteevstigneev@yandex.ru> Co-committed-by: Evstigneev Denis <danteevstigneev@yandex.ru>
		
			
				
	
	
		
			225 lines
		
	
	
		
			5.4 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			225 lines
		
	
	
		
			5.4 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // Package config is an interface for dynamic configuration.
 | |
| package config
 | |
| 
 | |
| import (
 | |
| 	"context"
 | |
| 	"errors"
 | |
| 	"reflect"
 | |
| 	"time"
 | |
| )
 | |
| 
 | |
| type Validator interface {
 | |
| 	Validate() error
 | |
| }
 | |
| 
 | |
| // DefaultConfig default config
 | |
| var DefaultConfig = NewConfig()
 | |
| 
 | |
| // DefaultWatcherMinInterval default min interval for poll changes
 | |
| var DefaultWatcherMinInterval = 5 * time.Second
 | |
| 
 | |
| // DefaultWatcherMaxInterval default max interval for poll changes
 | |
| var DefaultWatcherMaxInterval = 9 * time.Second
 | |
| 
 | |
| var (
 | |
| 	// ErrCodecMissing is returned when codec needed and not specified
 | |
| 	ErrCodecMissing = errors.New("codec missing")
 | |
| 	// ErrInvalidStruct is returned when the target struct is invalid
 | |
| 	ErrInvalidStruct = errors.New("invalid struct specified")
 | |
| 	// ErrWatcherStopped is returned when source watcher has been stopped
 | |
| 	ErrWatcherStopped = errors.New("watcher stopped")
 | |
| 	// ErrWatcherNotImplemented returned when config does not implement watch
 | |
| 	ErrWatcherNotImplemented = errors.New("watcher not implemented")
 | |
| )
 | |
| 
 | |
| // Config is an interface abstraction for dynamic configuration
 | |
| type Config interface {
 | |
| 	// Name returns name of config
 | |
| 	Name() string
 | |
| 	// Init the config
 | |
| 	Init(opts ...Option) error
 | |
| 	// Options in the config
 | |
| 	Options() Options
 | |
| 	// Load config from sources
 | |
| 	Load(context.Context, ...LoadOption) error
 | |
| 	// Save config to sources
 | |
| 	Save(context.Context, ...SaveOption) error
 | |
| 	// Watch a config for changes
 | |
| 	Watch(context.Context, ...WatchOption) (Watcher, error)
 | |
| 	// String returns config type name
 | |
| 	String() string
 | |
| }
 | |
| 
 | |
| type (
 | |
| 	FuncLoad func(ctx context.Context, opts ...LoadOption) error
 | |
| 	HookLoad func(next FuncLoad) FuncLoad
 | |
| 	FuncSave func(ctx context.Context, opts ...SaveOption) error
 | |
| 	HookSave func(next FuncSave) FuncSave
 | |
| )
 | |
| 
 | |
| // Watcher is the config watcher
 | |
| type Watcher interface {
 | |
| 	// Next blocks until update happens or error returned
 | |
| 	Next() (map[string]interface{}, error)
 | |
| 	// Stop stops watcher
 | |
| 	Stop() error
 | |
| }
 | |
| 
 | |
| // Load loads config from config sources
 | |
| func Load(ctx context.Context, cs []Config, opts ...LoadOption) error {
 | |
| 	var err error
 | |
| 	for _, c := range cs {
 | |
| 		if err = c.Init(); err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 		if err = c.Load(ctx, opts...); err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 	}
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| // Validate runs Validate() error func for each struct field
 | |
| func Validate(ctx context.Context, cfg interface{}) error {
 | |
| 	if cfg == nil {
 | |
| 		return nil
 | |
| 	}
 | |
| 
 | |
| 	if v, ok := cfg.(Validator); ok {
 | |
| 		if err := v.Validate(); err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	sv := reflect.ValueOf(cfg)
 | |
| 	if sv.Kind() == reflect.Ptr {
 | |
| 		sv = sv.Elem()
 | |
| 	}
 | |
| 	if sv.Kind() != reflect.Struct {
 | |
| 		return nil
 | |
| 	}
 | |
| 
 | |
| 	typ := sv.Type()
 | |
| 	for idx := 0; idx < typ.NumField(); idx++ {
 | |
| 		fld := typ.Field(idx)
 | |
| 		val := sv.Field(idx)
 | |
| 		if !val.IsValid() || len(fld.PkgPath) != 0 {
 | |
| 			continue
 | |
| 		}
 | |
| 
 | |
| 		if v, ok := val.Interface().(Validator); ok {
 | |
| 			if err := v.Validate(); err != nil {
 | |
| 				return err
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		switch val.Kind() {
 | |
| 		case reflect.Ptr:
 | |
| 			if reflect.Indirect(val).Kind() == reflect.Struct {
 | |
| 				if err := Validate(ctx, val.Interface()); err != nil {
 | |
| 					return err
 | |
| 				}
 | |
| 			}
 | |
| 		case reflect.Struct:
 | |
| 			if err := Validate(ctx, val.Interface()); err != nil {
 | |
| 				return err
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| var (
 | |
| 	// DefaultBeforeLoad default func that runs before config Load
 | |
| 	DefaultBeforeLoad = func(ctx context.Context, c Config) error {
 | |
| 		for _, fn := range c.Options().BeforeLoad {
 | |
| 			if fn == nil {
 | |
| 				return nil
 | |
| 			}
 | |
| 			if err := fn(ctx, c); err != nil {
 | |
| 				c.Options().Logger.Error(ctx, c.String()+" BeforeLoad error", err)
 | |
| 				if !c.Options().AllowFail {
 | |
| 					return err
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 		return nil
 | |
| 	}
 | |
| 	// DefaultAfterLoad default func that runs after config Load
 | |
| 	DefaultAfterLoad = func(ctx context.Context, c Config) error {
 | |
| 		for _, fn := range c.Options().AfterLoad {
 | |
| 			if fn == nil {
 | |
| 				return nil
 | |
| 			}
 | |
| 			if err := fn(ctx, c); err != nil {
 | |
| 				c.Options().Logger.Error(ctx, c.String()+" AfterLoad error", err)
 | |
| 				if !c.Options().AllowFail {
 | |
| 					return err
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 		return nil
 | |
| 	}
 | |
| 	// DefaultBeforeSave default func that runs befora config Save
 | |
| 	DefaultBeforeSave = func(ctx context.Context, c Config) error {
 | |
| 		for _, fn := range c.Options().BeforeSave {
 | |
| 			if fn == nil {
 | |
| 				return nil
 | |
| 			}
 | |
| 			if err := fn(ctx, c); err != nil {
 | |
| 				c.Options().Logger.Error(ctx, c.String()+" BeforeSave error", err)
 | |
| 				if !c.Options().AllowFail {
 | |
| 					return err
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 		return nil
 | |
| 	}
 | |
| 	// DefaultAfterSave default func that runs after config Save
 | |
| 	DefaultAfterSave = func(ctx context.Context, c Config) error {
 | |
| 		for _, fn := range c.Options().AfterSave {
 | |
| 			if fn == nil {
 | |
| 				return nil
 | |
| 			}
 | |
| 			if err := fn(ctx, c); err != nil {
 | |
| 				c.Options().Logger.Error(ctx, c.String()+" AfterSave error", err)
 | |
| 				if !c.Options().AllowFail {
 | |
| 					return err
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 		return nil
 | |
| 	}
 | |
| 	// DefaultBeforeInit default func that runs befora config Init
 | |
| 	DefaultBeforeInit = func(ctx context.Context, c Config) error {
 | |
| 		for _, fn := range c.Options().BeforeInit {
 | |
| 			if fn == nil {
 | |
| 				return nil
 | |
| 			}
 | |
| 			if err := fn(ctx, c); err != nil {
 | |
| 				c.Options().Logger.Error(ctx, c.String()+" BeforeInit error", err)
 | |
| 				if !c.Options().AllowFail {
 | |
| 					return err
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 		return nil
 | |
| 	}
 | |
| 	// DefaultAfterInit default func that runs after config Init
 | |
| 	DefaultAfterInit = func(ctx context.Context, c Config) error {
 | |
| 		for _, fn := range c.Options().AfterSave {
 | |
| 			if fn == nil {
 | |
| 				return nil
 | |
| 			}
 | |
| 			if err := fn(ctx, c); err != nil {
 | |
| 				c.Options().Logger.Error(ctx, c.String()+" AfterInit error", err)
 | |
| 				if !c.Options().AllowFail {
 | |
| 					return err
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 		return nil
 | |
| 	}
 | |
| )
 |