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包的语法树的类型。