// Во имя Бога Милостивого, Милосердного!!! 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 }