197 строки
4 КиБ
Go
197 строки
4 КиБ
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")
|
|
}
|
|
|
|
switch keyvalue.Value.(type) {
|
|
case *ast.BasicLit, *ast.Ident:
|
|
return c.setConfigElement(key.Name, keyvalue.Value)
|
|
default:
|
|
return errors.New("Value not found")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (c *ConfigParser) setConfigElement(key string, value ast.Expr) 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.Expr, 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)
|
|
case reflect.Bool:
|
|
c.setConfigFieldToBool(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, v ast.Expr) error {
|
|
switch v.(type) {
|
|
case *ast.BasicLit:
|
|
default:
|
|
return errors.New("Not string")
|
|
}
|
|
|
|
value := v.(*ast.BasicLit)
|
|
|
|
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, v ast.Expr) error {
|
|
switch v.(type) {
|
|
case *ast.BasicLit:
|
|
default:
|
|
return errors.New("Not int")
|
|
}
|
|
|
|
value := v.(*ast.BasicLit)
|
|
|
|
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
|
|
}
|
|
|
|
func (c *ConfigParser) setConfigFieldToBool(uv reflect.Value, v ast.Expr) error {
|
|
switch v.(type) {
|
|
case *ast.Ident:
|
|
default:
|
|
return errors.New("Not bool")
|
|
}
|
|
|
|
value := v.(*ast.Ident)
|
|
|
|
switch value.Name {
|
|
case "true":
|
|
uv.SetBool(true)
|
|
case "false":
|
|
uv.SetBool(false)
|
|
default:
|
|
return errors.New("Not bool")
|
|
}
|
|
|
|
return nil
|
|
}
|