package config import ( "fmt" "github.com/urfave/cli/v2" "gopkg.in/yaml.v3" "os" "reflect" ) const cliMetaKey = "cli.config" type IConfig interface { Invalidate() error } func Invalidate(config IConfig) error { if config == nil { return fmt.Errorf("config must not be a nil") } val := reflect.ValueOf(config) if val.Kind() != reflect.Struct { if val.IsZero() { return fmt.Errorf("config is empty") } val = reflect.ValueOf(config).Elem() } for i := 0; i < val.NumField(); i++ { if !val.Field(i).CanInterface() || val.Field(i).IsZero() { continue } if elm, ok := val.Field(i).Interface().(IConfig); ok { if er := elm.Invalidate(); er != nil { return er } } } return nil } func Read[T IConfig](path string) (*T, error) { var ( cfg = new(T) fd []byte err error ) if fd, err = os.ReadFile(path); err != nil { return nil, err } if err = yaml.Unmarshal(fd, cfg); err != nil { return nil, err } if err = (*cfg).Invalidate(); err != nil { return nil, err } if err = Invalidate(*cfg); err != nil { return nil, err } return cfg, nil } func Load[T IConfig](ctx *cli.Context, path cli.Path) error { cfg, err := Read[T](path) if err != nil { return fmt.Errorf("could not load config from: %s; %v", path, err) } if cfg == nil { return fmt.Errorf("could not load config from: %s", path) } ctx.App.Metadata[cliMetaKey] = cfg return nil } func Get[T IConfig](ctx *cli.Context) *T { cfg, ok := ctx.App.Metadata["cli.config"].(*T) if !ok { return nil } return cfg }