dae/config/parser.go
2023-02-01 12:30:26 +08:00

227 lines
7.0 KiB
Go

/*
* SPDX-License-Identifier: AGPL-3.0-only
* Copyright (c) since 2023, v2rayA Organization <team@v2raya.org>
*/
package config
import (
"fmt"
"github.com/v2rayA/dae/common"
"github.com/v2rayA/dae/pkg/config_parser"
"reflect"
)
// Parser is section items parser
type Parser func(to reflect.Value, section *config_parser.Section) error
var ParserMap = map[string]Parser{
"StringListParser": StringListParser,
"ParamParser": ParamParser,
"GroupListParser": GroupListParser,
"RoutingRuleAndParamParser": RoutingRuleAndParamParser,
}
func StringListParser(to reflect.Value, section *config_parser.Section) error {
if to.Kind() != reflect.Pointer {
return fmt.Errorf("StringListParser can only unmarshal section to *[]string")
}
to = to.Elem()
if to.Type() != reflect.TypeOf([]string{}) {
return fmt.Errorf("StringListParser can only unmarshal section to *[]string")
}
var list []string
for _, item := range section.Items {
switch itemVal := item.Value.(type) {
case *config_parser.Param:
list = append(list, itemVal.String(true))
default:
return fmt.Errorf("section %v does not support type %v: %v", section.Name, item.Type.String(), item.String())
}
}
to.Set(reflect.ValueOf(list))
return nil
}
func paramParser(to reflect.Value, section *config_parser.Section, ignoreType []reflect.Type) error {
if to.Kind() != reflect.Pointer {
return fmt.Errorf("ParamParser can only unmarshal section to *struct")
}
to = to.Elem()
if to.Kind() != reflect.Struct {
return fmt.Errorf("ParamParser can only unmarshal section to struct")
}
// keyToField is for further parsing use.
type Field struct {
Val reflect.Value
Index int
Set bool
}
var keyToField = make(map[string]*Field)
tot := to.Type()
for i := 0; i < to.NumField(); i++ {
field := to.Field(i)
structField := tot.Field(i)
// Set up key to field mapping.
key, ok := structField.Tag.Lookup("mapstructure")
if !ok {
return fmt.Errorf("field %v has no mapstructure tag", structField.Name)
}
if key == "_" {
// omit
continue
}
keyToField[key] = &Field{Val: field, Index: i}
// Fill in default value before parsing section.
defaultValue, ok := structField.Tag.Lookup("default")
if ok {
if !common.FuzzyDecode(field.Addr().Interface(), defaultValue) {
return fmt.Errorf(`failed to decode default value of "%v"`, structField.Name)
}
}
}
// Convert ignoreType from list to set.
ignoreTypeSet := make(map[reflect.Type]struct{})
for _, typ := range ignoreType {
ignoreTypeSet[typ] = struct{}{}
}
// Parse section.
for _, item := range section.Items {
switch itemVal := item.Value.(type) {
case *config_parser.Param:
if itemVal.Key == "" {
return fmt.Errorf("section %v does not support text without a key: %v", section.Name, itemVal.String(true))
}
field, ok := keyToField[itemVal.Key]
if !ok {
return fmt.Errorf("section %v does not support key: %v", section.Name, itemVal.Key)
}
if itemVal.AndFunctions != nil {
// AndFunctions.
// If field is interface{} or types equal, we can assign.
if field.Val.Kind() == reflect.Interface ||
field.Val.Type() == reflect.TypeOf(itemVal.AndFunctions) {
field.Val.Set(reflect.ValueOf(itemVal.AndFunctions))
} else {
return fmt.Errorf("failed to parse \"%v.%v\": value \"%v\" cannot be convert to %v", section.Name, itemVal.Key, itemVal.Val, field.Val.Type().String())
}
} else {
// String value.
switch field.Val.Kind() {
case reflect.Interface:
// Field is interface{}, we can assign.
field.Val.Set(reflect.ValueOf(itemVal.Val))
case reflect.Slice:
// Field is not interface{}, we can decode.
vPointerNew := reflect.New(field.Val.Type().Elem())
if !common.FuzzyDecode(vPointerNew.Interface(), itemVal.Val) {
return fmt.Errorf("failed to parse \"%v.%v\": value \"%v\" cannot be convert to %v", section.Name, itemVal.Key, itemVal.Val, field.Val.Type().Elem().String())
}
field.Val.Set(reflect.Append(field.Val, vPointerNew.Elem()))
default:
// Field is not interface{}, we can decode.
if !common.FuzzyDecode(field.Val.Addr().Interface(), itemVal.Val) {
return fmt.Errorf("failed to parse \"%v.%v\": value \"%v\" cannot be convert to %v", section.Name, itemVal.Key, itemVal.Val, field.Val.Type().String())
}
}
}
field.Set = true
default:
if _, ignore := ignoreTypeSet[reflect.TypeOf(itemVal)]; !ignore {
return fmt.Errorf("section %v does not support type %v: %v", section.Name, item.Type.String(), item.String())
}
}
}
// Check required.
for key, field := range keyToField {
if field.Set {
continue
}
t := to.Type().Field(field.Index)
_, required := t.Tag.Lookup("required")
if required {
return fmt.Errorf(`section "%v" requires param "%v" but not found`, section.Name, key)
}
}
return nil
}
func ParamParser(to reflect.Value, section *config_parser.Section) error {
return paramParser(to, section, nil)
}
func GroupListParser(to reflect.Value, section *config_parser.Section) error {
if to.Kind() != reflect.Pointer {
return fmt.Errorf("GroupListParser can only unmarshal section to *[]Group")
}
to = to.Elem()
if to.Type() != reflect.TypeOf([]Group{}) {
return fmt.Errorf("GroupListParser can only unmarshal section to *[]Group")
}
for _, item := range section.Items {
switch itemVal := item.Value.(type) {
case *config_parser.Section:
group := Group{
Name: itemVal.Name,
Param: GroupParam{},
}
paramVal := reflect.ValueOf(&group.Param)
if err := paramParser(paramVal, itemVal, nil); err != nil {
return fmt.Errorf("failed to parse \"%v\": %w", itemVal.Name, err)
}
to.Set(reflect.Append(to, reflect.ValueOf(group)))
default:
return fmt.Errorf("section %v does not support type %v: %v", section.Name, item.Type.String(), item.String())
}
}
return nil
}
func RoutingRuleAndParamParser(to reflect.Value, section *config_parser.Section) error {
if to.Kind() != reflect.Pointer {
return fmt.Errorf("RoutingRuleAndParamParser can only unmarshal section to *struct")
}
to = to.Elem()
if to.Kind() != reflect.Struct {
return fmt.Errorf("RoutingRuleAndParamParser can only unmarshal section to *struct")
}
// Find the first []*RoutingRule field to unmarshal.
targetType := reflect.TypeOf([]*config_parser.RoutingRule{})
var ruleTo *reflect.Value
for i := 0; i < to.NumField(); i++ {
field := to.Field(i)
if field.Type() == targetType {
ruleTo = &field
break
}
}
if ruleTo == nil {
return fmt.Errorf(`no %v field found`, targetType.String())
}
// Parse and unmarshal list of RoutingRule to ruleTo.
for _, item := range section.Items {
switch itemVal := item.Value.(type) {
case *config_parser.RoutingRule:
ruleTo.Set(reflect.Append(*ruleTo, reflect.ValueOf(itemVal)))
case *config_parser.Param:
// pass
default:
return fmt.Errorf("section %v does not support type %v: %v", section.Name, item.Type.String(), item.String())
}
}
// Parse Param.
return paramParser(to.Addr(), section,
[]reflect.Type{reflect.TypeOf(&config_parser.RoutingRule{})},
)
}