aboutsummaryrefslogtreecommitdiff
path: root/scfg/struct.go
diff options
context:
space:
mode:
authorRunxi Yu <me@runxiyu.org>2024-12-29 18:40:22 +0000
committerRunxi Yu <me@runxiyu.org>2024-12-29 18:40:22 +0000
commitb6c8304364d1457fa7454da257f7ad3d71131b91 (patch)
treede85e19971aa6ac127abf35b80421ff8f6907727 /scfg/struct.go
parentInitial commit (diff)
downloadgo-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.go79
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
+}