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 }