diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..c7b6e32 --- /dev/null +++ b/go.mod @@ -0,0 +1,5 @@ +module gitea.meta-tech.academy/go/config-loader + +go 1.20 + +require gopkg.in/yaml.v3 v3.0.1 diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..4bc0337 --- /dev/null +++ b/go.sum @@ -0,0 +1,3 @@ +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/loader.go b/loader.go new file mode 100644 index 0000000..d8af616 --- /dev/null +++ b/loader.go @@ -0,0 +1,110 @@ +package config + +import ( + "os" + "reflect" + "regexp" + "strings" + + "gopkg.in/yaml.v3" +) + +type Cfg interface { + GetVar() []VarConfig +} + +type VarConfig struct { + Name string `yaml:"name"` + Value string `yaml:"value"` +} + +type ConfigLoader struct { + Var []VarConfig + Config interface{} + varDef *VarPattern + refstr []*reflect.Value +} + +func LoadConfig(path string, out Cfg, varPattern *VarPattern) { + cl := &ConfigLoader{varDef: varPattern, refstr: []*reflect.Value{}} + data, err := os.ReadFile(path) + if err != nil { + panic(err) + } + if err = yaml.Unmarshal(data, out); err != nil { + panic(err) + } + cl.Var = out.GetVar() + cl.Config = out + cl.parse() + cl.refstr = []*reflect.Value{} + // cl.setConfigPtr(outi) +} + +func (cl *ConfigLoader) findVarName(match string) string { + re := regexp.MustCompile(cl.varDef.getPatternCaptureName()) + parts := re.FindStringSubmatch(match) + if len(parts) > 0 { + match = parts[1] + } + return match +} + +func (cl *ConfigLoader) findVarValue(v string) string { + names := strings.SplitN(v, ".", 2) + if names[0] == "var" { + for _, varConfig := range cl.Var { + if varConfig.Name == names[1] { + v = varConfig.Value + } + } + } + return v +} + +func (cl *ConfigLoader) findStringRef(ref reflect.Value) { + switch ref.Kind() { + case reflect.Ptr, reflect.Interface: + cl.findStringRef(ref.Elem()) + case reflect.Struct: + for i := 0; i < ref.NumField(); i++ { + refc := ref.Field(i) + cl.findStringRef(refc) + } + case reflect.Slice: + if !ref.IsNil() { + for i := 0; i < ref.Len(); i++ { + refc := ref.Index(i) + cl.findStringRef(refc) + } + } + case reflect.String: + if ref.Len() > 0 { + cl.refstr = append(cl.refstr, &ref) + } + } +} + +func (cl *ConfigLoader) parse() { + var name string + var tmp string + var parts []string + var re2 *regexp.Regexp + + cl.findStringRef(reflect.ValueOf(&cl)) + + re := regexp.MustCompile(cl.varDef.getPatternVar()) + for _, ref := range cl.refstr { + tmp = ref.String() + parts = re.FindStringSubmatch(tmp) + if len(parts) > 0 { + s := strings.SplitAfter(parts[1], cl.varDef.Close) + for _, part := range s { + name = cl.findVarName(part) + re2 = regexp.MustCompile(cl.varDef.getPatternVarName(name)) + tmp = re2.ReplaceAllString(tmp, cl.findVarValue(name)) + } + ref.SetString(tmp) + } + } +} diff --git a/varpattern.go b/varpattern.go new file mode 100644 index 0000000..85e26d7 --- /dev/null +++ b/varpattern.go @@ -0,0 +1,31 @@ +package config + +import ( + "fmt" + "regexp" +) + +type VarPattern struct { + Open string + Close string +} + +func (vp *VarPattern) getPatternCaptureName() string { + return fmt.Sprintf("%s([^%s]+)%s", regexp.QuoteMeta(vp.Open), regexp.QuoteMeta(string(vp.Close[:1])), regexp.QuoteMeta((vp.Close))) +} + +func (vp *VarPattern) getPatternVar() string { + return fmt.Sprintf("(%s.+%s)", regexp.QuoteMeta(vp.Open), regexp.QuoteMeta(vp.Close)) +} + +func (vp *VarPattern) getPatternVarName(name string) string { + return fmt.Sprintf("%s%s%s", regexp.QuoteMeta(vp.Open), name, regexp.QuoteMeta(vp.Close)) +} + +func NewVarPattern(open string, close string) *VarPattern { + return &VarPattern{open, close} +} + +func DefaultVarPattern() *VarPattern { + return NewVarPattern("{%", "%}") +}