bou.ke/statictemplate@v0.0.0-20180821122055-510411a5e7dd/internal/internal.go (about)

     1  package internal // import "bou.ke/statictemplate/internal"
     2  
     3  import (
     4  	"fmt"
     5  	"go/ast"
     6  	"go/token"
     7  	"go/types"
     8  	"regexp"
     9  	"strconv"
    10  
    11  	"golang.org/x/tools/go/loader"
    12  )
    13  
    14  var valueReferenceRe = regexp.MustCompile(`^(?:(.+)\.)?([A-Za-z][A-Za-z0-9]*)$`)
    15  
    16  func ImportFuncMap(funcMap string) (string, string, map[string]*types.Func, error) {
    17  	if funcMap == "" {
    18  		return "", "", nil, nil
    19  	}
    20  	var funcMapImport, funcMapName string
    21  	values := valueReferenceRe.FindStringSubmatch(funcMap)
    22  	if values == nil || values[1] == "" {
    23  		return "", "", nil, fmt.Errorf("invalid funcs value %q, expected <import>.<name>", funcMap)
    24  	}
    25  	funcMapImport = values[1]
    26  	funcMapName = values[2]
    27  
    28  	var conf loader.Config
    29  	conf.Import(funcMapImport)
    30  	prog, err := conf.Load()
    31  	if err != nil {
    32  		return "", "", nil, err
    33  	}
    34  	pack := prog.Package(funcMapImport)
    35  	var obj *ast.Object
    36  	for _, f := range pack.Files {
    37  		if obj = f.Scope.Lookup(funcMapName); obj != nil {
    38  			break
    39  		}
    40  	}
    41  	if obj == nil {
    42  		return "", "", nil, fmt.Errorf("Can't find function map %q", funcMap)
    43  	}
    44  
    45  	funcs := make(map[string]*types.Func)
    46  	for _, el := range obj.Decl.(*ast.ValueSpec).Values[0].(*ast.CompositeLit).Elts {
    47  		ex, ok := el.(*ast.KeyValueExpr)
    48  		if !ok {
    49  			return "", "", nil, fmt.Errorf("invalid function map format")
    50  		}
    51  		lit, ok := ex.Key.(*ast.BasicLit)
    52  		if !ok || lit.Kind != token.STRING {
    53  			return "", "", nil, fmt.Errorf("invalid function map format")
    54  		}
    55  		name, err := strconv.Unquote(lit.Value)
    56  		if err != nil {
    57  			return "", "", nil, fmt.Errorf("invalid function map format: %v", err)
    58  		}
    59  		ident, ok := ex.Value.(*ast.Ident)
    60  		if !ok {
    61  			return "", "", nil, fmt.Errorf("invalid function map format")
    62  		}
    63  		if f, ok := pack.Pkg.Scope().Lookup(ident.Name).(*types.Func); ok {
    64  			funcs[name] = f
    65  		} else {
    66  			return "", "", nil, fmt.Errorf("invalid function map format")
    67  		}
    68  	}
    69  	return funcMapImport, funcMapName, funcs, nil
    70  }