goconf/goconf.go
2021-05-27 18:34:02 +03:00

156 строки
3,3 КиБ
Go

// Во имя Бога Милостивого, Милосердного!!!
package goconf
import (
"errors"
"go/ast"
"go/parser"
"go/token"
"reflect"
"strconv"
)
type ConfigParser struct {
config interface{}
filename string
}
func LoadConfig(config interface{}, config_go string) error {
p := &ConfigParser{config: config, filename: config_go}
return p.parse()
}
func (c *ConfigParser) parse() error {
fset := token.NewFileSet()
af, err := parser.ParseFile(fset, c.filename, nil, parser.ParseComments)
if err != nil {
return err
}
return c.parseAST(af)
}
func (c *ConfigParser) parseAST(af *ast.File) error {
ast.Inspect(af, c.searchForConfigAndShouldContinue)
return nil
}
func (c *ConfigParser) searchForConfigAndShouldContinue(n ast.Node) bool {
vs, ok := n.(*ast.ValueSpec)
if ok && vs.Names[0].Name == "config" {
c.digConfig(vs)
return false
}
return true
}
func (c *ConfigParser) digConfig(config ast.Node) error {
ast.Inspect(config, c.searchForConfigElementsAndShouldContinue)
return nil
}
func (c *ConfigParser) searchForConfigElementsAndShouldContinue(n ast.Node) bool {
definition, ok := n.(*ast.CompositeLit)
if ok {
t, ok := definition.Type.(*ast.Ident)
if ok && t.Name == "Config" {
c.digConfigElements(definition.Elts)
return false
}
}
return true
}
func (c *ConfigParser) digConfigElements(elements []ast.Expr) error {
for _, e := range elements {
c.digConfigElement(e)
}
return nil
}
func (c *ConfigParser) digConfigElement(e ast.Expr) error {
keyvalue, ok := e.(*ast.KeyValueExpr)
if !ok {
return errors.New("Not ast.KeyValueExpr")
}
key, ok := keyvalue.Key.(*ast.Ident)
if !ok {
return errors.New("Key not found")
}
value, ok := keyvalue.Value.(*ast.BasicLit)
if !ok {
return errors.New("Value not found")
}
return c.setConfigElement(key.Name, value)
}
func (c *ConfigParser) setConfigElement(key string, value *ast.BasicLit) error {
config := reflect.ValueOf(c.config)
kind := config.Kind()
if config.IsNil() || kind != reflect.Ptr {
return errors.New("Bad config pointer")
}
config = config.Elem()
kind = config.Kind()
if kind != reflect.Struct {
return errors.New("Config is not a struct")
}
vt := config.Type()
numFields := config.NumField()
for i := 0; i < numFields; i++ {
c.setConfigElementField(key, value, config, i, vt)
}
return nil
}
func (c *ConfigParser) setConfigElementField(key string, value *ast.BasicLit, config reflect.Value, i int, vt reflect.Type) error {
if vt.Field(i).Name != key {
return nil
}
vf := config.Field(i)
uv := unpackValue(vf)
kind := uv.Kind()
switch kind {
case reflect.String:
c.setConfigFieldToString(uv, value)
case reflect.Int:
c.setConfigFieldToInt(uv, value)
}
return nil
}
func unpackValue(v reflect.Value) reflect.Value {
if v.Kind() == reflect.Interface && !v.IsNil() {
v = v.Elem()
}
return v
}
func (c *ConfigParser) setConfigFieldToString(uv reflect.Value, value *ast.BasicLit) error {
if value.Kind != token.STRING {
return errors.New("Not string")
}
s, err := strconv.Unquote(value.Value)
if err != nil {
return err
}
uv.SetString(s)
return nil
}
func (c *ConfigParser) setConfigFieldToInt(uv reflect.Value, value *ast.BasicLit) error {
if value.Kind != token.INT {
return errors.New("Not int")
}
num, err := strconv.Atoi(value.Value)
if err != nil {
return err
}
uv.SetInt(int64(num))
return nil
}