Go文件解析

实际上,ent的generate指的是entc,即ent codegen.

解析Go包,获取实体(即schema)

这里使用了 golang.org/x/tools/go/packages用来校验和分析Go包。

// 从包路径中解析出schema的实体名称
func Generate(cmd *cobra.Command, args []string)  {
	cfg := &packages.Config{Mode: packages.NeedName | packages.NeedTypes | packages.NeedTypesInfo | packages.NeedModule,}
	// 加载要解析的Go包
    pkgs, err := packages.Load(cfg, args[0], entInterface.PkgPath())
	if err != nil {
		fmt.Fprintf(os.Stderr, "load: %v\n", err)
		os.Exit(1)
	}
	entPkg, pkg := pkgs[0], pkgs[1]
	if len(pkg.Errors) != 0 {
		log.Println(pkg.Errors[0])
	}
	if pkgs[0].PkgPath != entInterface.PkgPath() {
		entPkg, pkg = pkgs[1], pkgs[0]
	}
	var names []string
	//entInterface.Name() 反射类型名称
	//entPkg.Types.Scope().Lookup(entInterface.Name()) 从包的类型中找到给定名称的类型,返回其对象
	//.Type().Underlying() 对象类型的基础类型
	//(*types.Interface) 转为接口类型
	iface := entPkg.Types.Scope().Lookup(entInterface.Name()).Type().Underlying().(*types.Interface)
	//pkg.TypesInfo提供有关包的语法树的类型信息
    //pkg.TypesInfo.Defs 对象类型的定义信息
	//k ast.ident 
    //type Ident struct {
    //    NamePos token.Pos // 标识符的位置
    //    Name    string    // 标识符的名称
    //    Obj     *Object   // 所表示的对象; or nil
    //}
    //v obj
	for k, v := range pkg.TypesInfo.Defs {
        //做一些必要的校验
		typ, ok := v.(*types.TypeName)
		//IsExported id是否是大写开头
		//types.Implements 第一参数是否实现了第二参数
		if !ok || !k.IsExported() || !types.Implements(typ.Type(), iface) {
			continue
		}
		//一个类型的声明
		spec, ok := k.Obj.Decl.(*ast.TypeSpec)
		if !ok {
			fmt.Errorf("invalid declaration %T for %s", k.Obj.Decl, k.Name)
		}
		// ast.StructType 结构体类型声明
		if _, ok := spec.Type.(*ast.StructType); !ok {
			fmt.Errorf("invalid spec type %T for %s", spec.Type, k.Name)
		}
        //获取到schema的名称
		names = append(names, k.Name)
	}

	fmt.Println(names)
	fmt.Println(pkg.PkgPath)
}

//输出:
//[Link User]
//gitee.com/wennmu/gint.git/gint/schema

参考资料

  • go/packages - 用来校验和分析Go包。
  • go/ast - 用来表示Go包的语法树的类型。