diff options
author | Runxi Yu <me@runxiyu.org> | 2024-12-29 18:40:22 +0000 |
---|---|---|
committer | Runxi Yu <me@runxiyu.org> | 2024-12-29 18:40:22 +0000 |
commit | b6c8304364d1457fa7454da257f7ad3d71131b91 (patch) | |
tree | de85e19971aa6ac127abf35b80421ff8f6907727 /scfg/struct.go | |
parent | Initial commit (diff) | |
download | go-lindenii-common-b6c8304364d1457fa7454da257f7ad3d71131b91.tar.gz go-lindenii-common-b6c8304364d1457fa7454da257f7ad3d71131b91.tar.zst go-lindenii-common-b6c8304364d1457fa7454da257f7ad3d71131b91.zip |
Add ~emersion/go-scfg as a subdirectory
Diffstat (limited to 'scfg/struct.go')
-rw-r--r-- | scfg/struct.go | 79 |
1 files changed, 79 insertions, 0 deletions
diff --git a/scfg/struct.go b/scfg/struct.go new file mode 100644 index 0000000..8b39e34 --- /dev/null +++ b/scfg/struct.go @@ -0,0 +1,79 @@ +package scfg + +import ( + "fmt" + "reflect" + "strings" + "sync" +) + +// structInfo contains scfg metadata for structs. +type structInfo struct { + param int // index of field storing parameters + children map[string]int // indices of fields storing child directives +} + +var ( + structCacheMutex sync.Mutex + structCache = make(map[reflect.Type]*structInfo) +) + +func getStructInfo(t reflect.Type) (*structInfo, error) { + structCacheMutex.Lock() + defer structCacheMutex.Unlock() + + if info := structCache[t]; info != nil { + return info, nil + } + + info := &structInfo{ + param: -1, + children: make(map[string]int), + } + + for i := 0; i < t.NumField(); i++ { + f := t.Field(i) + if f.Anonymous { + return nil, fmt.Errorf("scfg: anonymous struct fields are not supported") + } else if !f.IsExported() { + continue + } + + tag := f.Tag.Get("scfg") + parts := strings.Split(tag, ",") + k, options := parts[0], parts[1:] + if k == "-" { + continue + } else if k == "" { + k = f.Name + } + + isParam := false + for _, opt := range options { + switch opt { + case "param": + isParam = true + default: + return nil, fmt.Errorf("scfg: invalid option %q in struct tag", opt) + } + } + + if isParam { + if info.param >= 0 { + return nil, fmt.Errorf("scfg: param option specified multiple times in struct tag in %v", t) + } + if parts[0] != "" { + return nil, fmt.Errorf("scfg: name must be empty when param option is specified in struct tag in %v", t) + } + info.param = i + } else { + if _, ok := info.children[k]; ok { + return nil, fmt.Errorf("scfg: key %q specified multiple times in struct tag in %v", k, t) + } + info.children[k] = i + } + } + + structCache[t] = info + return info, nil +} |