diff --git a/README.md b/README.md index 520a43f..7f77884 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,27 @@ # yaconf Golang yaml config reader + +```golang +package main + +import ( + "log" + + "github.com/onrik/yaconf" +) + +type Config struct { + LogFile string `yaml:"log_file" yaconf:"required"` +} + +func main() { + config := Config{} + err := yaconf.Read("config.yml", &config) + if err != nil { + log.Println(err) + return + } + + log.Printf("%+v\n", config) +} +``` \ No newline at end of file diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..68121ad --- /dev/null +++ b/go.mod @@ -0,0 +1,5 @@ +module github.com/onrik/yaconf + +go 1.19 + +require gopkg.in/yaml.v3 v3.0.1 diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..a62c313 --- /dev/null +++ b/go.sum @@ -0,0 +1,4 @@ +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/yaconf.go b/yaconf.go new file mode 100644 index 0000000..f1a16d4 --- /dev/null +++ b/yaconf.go @@ -0,0 +1,85 @@ +package yaconf + +import ( + "fmt" + "os" + "reflect" + "strings" + + "gopkg.in/yaml.v3" +) + +type validator interface { + Validate() error +} + +func addPrefix(prefix, name string) string { + if prefix == "" { + return name + } + + return fmt.Sprintf("%s.%s", prefix, name) +} + +func validate(config interface{}, prefix string) []string { + t := reflect.TypeOf(config) + v := reflect.ValueOf(config) + if t.Kind() == reflect.Ptr { + t = t.Elem() + v = v.Elem() + } + + errors := []string{} + for i := 0; i < t.NumField(); i++ { + f := t.Field(i) + name := f.Tag.Get("yaml") + if name == "" { + name = f.Name + } + + if f.Tag.Get("yaconf") == "required" && v.Field(i).IsZero() { + errors = append(errors, fmt.Sprintf("%s is required", addPrefix(prefix, name))) + continue + } + + if f.Type.Kind() == reflect.Struct { + errors = append(errors, validate(v.Field(i).Interface(), addPrefix(prefix, name))...) + continue + } + + if f.Type.Kind() == reflect.Ptr { + if f.Type.Elem().Kind() == reflect.Struct { + if v.Field(i).IsNil() { + v.Field(i).Set(reflect.New(f.Type.Elem())) + } + errors = append(errors, validate(v.Field(i).Elem().Interface(), addPrefix(prefix, name))...) + } + } + + } + return errors +} + +// Read config from file +func Read(filename string, config interface{}) error { + data, err := os.ReadFile(filename) + if err != nil { + return err + } + + err = yaml.Unmarshal(data, config) + if err != nil { + return err + } + + errors := validate(config, "") + if len(errors) > 0 { + return fmt.Errorf(strings.Join(errors, ", ")) + } + + if v, ok := config.(validator); ok { + return v.Validate() + } + + return nil +}