mirror of
https://github.com/anotherhadi/iknowyou.git
synced 2026-04-12 00:47:26 +02:00
init
This commit is contained in:
155
back/internal/tools/config_reflect.go
Normal file
155
back/internal/tools/config_reflect.go
Normal file
@@ -0,0 +1,155 @@
|
||||
package tools
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// ReflectConfigFields builds []ConfigField from a struct using yaml/iky tags.
|
||||
// iky tag format: iky:"desc=...;default=...;required=true;options=a|b|c"
|
||||
func ReflectConfigFields(cfg any) []ConfigField {
|
||||
v := reflect.ValueOf(cfg)
|
||||
if v.Kind() == reflect.Ptr {
|
||||
v = v.Elem()
|
||||
}
|
||||
t := v.Type()
|
||||
|
||||
var fields []ConfigField
|
||||
for i := range t.NumField() {
|
||||
sf := t.Field(i)
|
||||
fv := v.Field(i)
|
||||
|
||||
yamlKey := sf.Tag.Get("yaml")
|
||||
if yamlKey == "" || yamlKey == "-" {
|
||||
continue
|
||||
}
|
||||
yamlKey = strings.SplitN(yamlKey, ",", 2)[0]
|
||||
|
||||
meta := parseIkyTag(sf.Tag.Get("iky"))
|
||||
|
||||
fieldType := goKindToString(sf.Type.Kind())
|
||||
if len(meta.options) > 0 {
|
||||
fieldType = "enum"
|
||||
}
|
||||
|
||||
fields = append(fields, ConfigField{
|
||||
Name: yamlKey,
|
||||
Type: fieldType,
|
||||
Required: meta.required,
|
||||
Description: meta.desc,
|
||||
Default: parseTypedDefault(meta.rawDefault, sf.Type.Kind()),
|
||||
Value: fv.Interface(),
|
||||
Options: meta.options,
|
||||
})
|
||||
}
|
||||
return fields
|
||||
}
|
||||
|
||||
// ApplyDefaults sets each field to its iky default if the field is zero.
|
||||
func ApplyDefaults(cfg any) {
|
||||
v := reflect.ValueOf(cfg)
|
||||
if v.Kind() == reflect.Ptr {
|
||||
v = v.Elem()
|
||||
}
|
||||
t := v.Type()
|
||||
|
||||
for i := range t.NumField() {
|
||||
sf := t.Field(i)
|
||||
fv := v.Field(i)
|
||||
|
||||
if !fv.CanSet() {
|
||||
continue
|
||||
}
|
||||
meta := parseIkyTag(sf.Tag.Get("iky"))
|
||||
if meta.rawDefault == "" || !fv.IsZero() {
|
||||
continue
|
||||
}
|
||||
applyDefault(fv, sf.Type.Kind(), meta.rawDefault)
|
||||
}
|
||||
}
|
||||
|
||||
type ikyMeta struct {
|
||||
desc string
|
||||
rawDefault string
|
||||
required bool
|
||||
options []string
|
||||
}
|
||||
|
||||
func parseIkyTag(tag string) ikyMeta {
|
||||
var m ikyMeta
|
||||
for _, part := range strings.Split(tag, ";") {
|
||||
k, v, ok := strings.Cut(strings.TrimSpace(part), "=")
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
switch strings.TrimSpace(k) {
|
||||
case "desc":
|
||||
m.desc = strings.TrimSpace(v)
|
||||
case "default":
|
||||
m.rawDefault = strings.TrimSpace(v)
|
||||
case "required":
|
||||
m.required = strings.TrimSpace(v) == "true"
|
||||
case "options":
|
||||
for _, opt := range strings.Split(v, "|") {
|
||||
if o := strings.TrimSpace(opt); o != "" {
|
||||
m.options = append(m.options, o)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
func goKindToString(k reflect.Kind) string {
|
||||
switch k {
|
||||
case reflect.String:
|
||||
return "string"
|
||||
case reflect.Bool:
|
||||
return "bool"
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
return "int"
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return "float"
|
||||
default:
|
||||
return k.String()
|
||||
}
|
||||
}
|
||||
|
||||
func parseTypedDefault(raw string, k reflect.Kind) any {
|
||||
if raw == "" {
|
||||
return nil
|
||||
}
|
||||
switch k {
|
||||
case reflect.Bool:
|
||||
b, _ := strconv.ParseBool(raw)
|
||||
return b
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
n, _ := strconv.ParseInt(raw, 10, 64)
|
||||
return int(n)
|
||||
case reflect.Float32, reflect.Float64:
|
||||
f, _ := strconv.ParseFloat(raw, 64)
|
||||
return f
|
||||
default:
|
||||
return raw
|
||||
}
|
||||
}
|
||||
|
||||
func applyDefault(fv reflect.Value, k reflect.Kind, raw string) {
|
||||
switch k {
|
||||
case reflect.String:
|
||||
fv.SetString(raw)
|
||||
case reflect.Bool:
|
||||
if b, err := strconv.ParseBool(raw); err == nil {
|
||||
fv.SetBool(b)
|
||||
}
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
if n, err := strconv.ParseInt(raw, 10, 64); err == nil {
|
||||
fv.SetInt(n)
|
||||
}
|
||||
case reflect.Float32, reflect.Float64:
|
||||
if f, err := strconv.ParseFloat(raw, 64); err == nil {
|
||||
fv.SetFloat(f)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user