funciton.go


// Doc2Map
// @path 1111111
// @path 2222222
// --> {"path":[1111111,2222222]}
func Doc2Map(doc string) map[string][]string {
	m := make(map[string][]string)
	for _, line := range strings.Split(doc, "\n") {
		if i := strings.Index(line, "@"); i >= 0 {
			line = line[i+1:]
			i = strings.IndexAny(line, " \t")
			var key, value string
			if i >= 0 {
				key = line[:i]
				value = line[i+1:]
			} else {
				key = line
			}
			if sa, ok := m[key]; ok {
				m[key] = append(sa, value)
			} else {
				m[key] = []string{value}
			}
		}
	}
	return m
}

// Text get func retirement text
func Text(funcDecl *ast.FuncDecl) string {
	if funcDecl == nil {
		return ""
	}
	return funcDecl.Doc.Text()
}

// Params get func param list
func Params(funcDecl *ast.FuncDecl) []string {
	if funcDecl == nil {
		return make([]string, 0)
	}
	params := make([]string, 0, len(funcDecl.Type.Params.List))
	for _, param := range funcDecl.Type.Params.List {
		for _, item := range param.Names {
			params = append(params, item.Name)
		}
	}
	return params
}

// Outs get func return list
func Outs(funcDecl *ast.FuncDecl) []string {
	if funcDecl.Type.Results == nil {
		return make([]string, 0)
	}
	params := make([]string, 0, len(funcDecl.Type.Results.List))
	for _, param := range funcDecl.Type.Results.List {
		for _, item := range param.Names {
			params = append(params, item.Name)
		}
	}
	return params
}

// GetFunc get func or struct method
func GetFunc(f interface{}) (funcDecl *ast.FuncDecl) {
	switch v := f.(type) {
	case reflect.Method:
		return getFuncByMethod(v)
	default:
		return FindFunc(GetFuncInfo(f))
	}
}

func GetFuncInfo(f interface{}) (string, string, string) {
	switch v := f.(type) {
	case reflect.Method:
		typ := isStruct(v.Type.In(0))
		methodName, structPkg, structName := v.Name, typ.PkgPath(), typ.Name()
		return methodName, structPkg, structName
	default:
		pc := reflect.ValueOf(f).Pointer()
		fn := runtime.FuncForPC(pc)
		// github.com/inu1255/annotation.Abc
		// github.com/inu1255/annotation.(*A).Show-fm
		methodName := fn.Name()
		dir, file := path.Split(methodName)
		ss := strings.Split(file, ".")
		// github.com/inu1255/annotation
		structPkg := path.Join(dir, ss[0])
		n := len(ss) - 1
		methodName = ss[n]
		structName := ""
		if n > 1 {
			structName = strings.Trim(ss[1], "(*)")
			methodName = ss[n][:strings.LastIndex(ss[n], "-")]
		}
		return methodName, structPkg, structName
	}
}

func getFuncByMethod(m reflect.Method) (funcDecl *ast.FuncDecl) {
	typ := isStruct(m.Type.In(0))
	methodName := m.Name
	return getFunc(methodName, "", typ)
}

// find func or struct method
// find func by methodName,structPkg
// find struct method by methodName,typ
func getFunc(methodName, structPkg string, typ reflect.Type) (funcDecl *ast.FuncDecl) {
	structName := ""
	typ = isStruct(typ)
	if typ != nil {
		structName = typ.Name()
		structPkg = typ.PkgPath()
	}
	if fn := FindFunc(methodName, structPkg, structName); fn != nil {
		return fn
	}
	if structName != "" {
		// find parent's func
		walkFields(typ, func(field reflect.StructField) bool {
			if field.Anonymous {
				walkMethods(field.Type, func(method reflect.Method) bool {
					if method.Name == methodName {
						funcDecl = getFuncByMethod(method)
						return true
					}
					return false
				})
			}
			return false
		})
	}
	return
}

func FindFunc(methodName, structPkg, structName string) (funcDecl *ast.FuncDecl) {
	var mpkg map[string]*ast.Package
	var err error
	fset := token.NewFileSet()
	if structPkg == "" || structPkg == "main" {
		mpkg, err = parser.ParseDir(fset, ".", nil, parser.ParseComments)
	} else {
		gopath, goroot := getGopathGoroot()
		dir := path.Join("vendor", structPkg)
		if _, err := os.Stat(dir); err != nil {
			dir = path.Join(gopath, "src", structPkg)
			if _, err := os.Stat(dir); err != nil {
				dir = path.Join(goroot, "src", structPkg)
			}
		}
		mpkg, err = parser.ParseDir(fset, dir, nil, parser.ParseComments)
	}
	if err != nil {
		return nil
	}
	for _, pkg := range mpkg {
		for _, f := range pkg.Files {
			for _, decl := range f.Decls {
				if fun, ok := decl.(*ast.FuncDecl); ok {
					if fun.Name.Name == methodName {
						if structName == "" { // func Foo()
							return fun
						} else if fun.Recv != nil && len(fun.Recv.List) > 0 { // func (*Bar)Foo()
							if starExpr, ok := fun.Recv.List[0].Type.(*ast.StarExpr); ok {
								if ident, ok := starExpr.X.(*ast.Ident); ok {
									if ident.Name == structName {
										return fun
									}
								}
							} else if ident, ok := fun.Recv.List[0].Type.(*ast.Ident); ok {
								if ident.Name == structName {
									return fun
								}
							}
						}
					}
				}
			}
		}
	}
	return
}

func getGopathGoroot() (gopath, goroot string) {
	output, _ := exec.Command("go", "env").Output()
	s := string(output)
	lines := strings.Split(s, "\n")
	for _, s := range lines {
		ss := strings.Split(s, "=")
		if len(ss) > 1 {
			if ss[0] == "GOPATH" {
				gopath = strings.Trim(ss[1], "\"")
			} else if ss[0] == "GOROOT" {
				goroot = strings.Trim(ss[1], "\"")
			}
		}
	}
	return
}

func isStruct(typ reflect.Type) reflect.Type {
	if typ == nil {
		return nil
	}
	switch typ.Kind() {
	case reflect.Interface, reflect.Ptr:
		return isStruct(typ.Elem())
	case reflect.Struct:
		return typ
	}
	return nil
}

func walkFields(typ reflect.Type, call func(reflect.StructField) bool) {
	switch typ.Kind() {
	case reflect.Interface, reflect.Ptr:
		walkFields(typ.Elem(), call)
	case reflect.Struct:
		numField := typ.NumField()
		for i := 0; i < numField; i++ {
			field := typ.Field(i)
			// Log.Println("field", field.Name, field.PkgPath, field.Anonymous, field.Type)
			if call(field) {
				break
			}
		}
	}
}

func walkMethods(typ reflect.Type, call func(reflect.Method) bool) {
	switch typ.Kind() {
	case reflect.Interface, reflect.Ptr:
		numMethod := typ.NumMethod()
		for i := 0; i < numMethod; i++ {
			method := typ.Method(i)
			// typ := method.Type.In(0).Elem()
			// Log.Println("method", typ.PkgPath(), typ.Name(), method.Name)
			if call(method) {
				break
			}
		}
	case reflect.Struct:
		walkMethods(reflect.PtrTo(typ), call)
	}
}

demo.go


// show abc
func Abc() {

}

type A struct {
	Name string
}

// show A
func (this *A) Show() {
	fmt.Println(this.Name)
}

type B struct {
	A
}

// show B
func (this *B) Show() {
}

func ExampleParse() {
	a := &A{}
	b := &B{}

	funcDecl := GetFunc(Abc)
	if funcDecl != nil {
		fmt.Println(funcDecl.Doc.Text())
	}

	funcDecl = GetFunc(a.Show)
	if funcDecl != nil {
		fmt.Println(funcDecl.Doc.Text())
	}

	funcDecl = GetFunc(b.Show)
	if funcDecl != nil {
		fmt.Println(funcDecl.Doc.Text())
	}

	m := reflect.TypeOf(b).Method(0)
	funcDecl = GetFunc(m)
	if funcDecl != nil {
		fmt.Println(funcDecl.Doc.Text())
	}

	// output:
	// show abc
	//
	// show A
	//
	// show B
	//
	// show B
}

other https://github.com/MarcGrol/golangAnnotations.git