|
|
|
package project
|
|
|
|
|
|
|
|
import (
|
|
|
|
"os"
|
|
|
|
"path/filepath"
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
"github.com/pkg/errors"
|
|
|
|
"github.com/siddontang/go/ioutil2"
|
|
|
|
|
|
|
|
"github.com/go-kratos/kratos/tool/protobuf/pkg/utils"
|
|
|
|
)
|
|
|
|
|
|
|
|
// if proto file is inside a project (that has a /api directory)
|
|
|
|
// this present a project info
|
|
|
|
// 必须假设proto文件的路径就是相对work-dir的路径,否则无法找到proto文件以及对应的project
|
|
|
|
type ProjectInfo struct {
|
|
|
|
// AbsolutePath of the project
|
|
|
|
AbsolutePath string
|
|
|
|
// ImportPath of project
|
|
|
|
ImportPath string
|
|
|
|
// dir name of project
|
|
|
|
Name string
|
|
|
|
// parent dir of project, maybe empty
|
|
|
|
Department string
|
|
|
|
// grandma dir of project , maybe empty
|
|
|
|
Typ string
|
|
|
|
HasInternalPkg bool
|
|
|
|
// 从工作目录(working directory)到project目录的相对路径 比如a/b .. ../a
|
|
|
|
// 作用是什么?
|
|
|
|
// 假设目录结构是
|
|
|
|
// -project
|
|
|
|
// - api/api.proto
|
|
|
|
// - internal/service
|
|
|
|
// 我想在 internal/service下生成一个文件 service.go
|
|
|
|
// work-dir 为 project/api
|
|
|
|
// proto 生成命令为 protoc --xx_out=. api.proto
|
|
|
|
// 那么在 protoc plugin 中的文件输出路径就得是 ../internal/service/ => {pathRefToProj}internal/service
|
|
|
|
//
|
|
|
|
PathRefToProj string
|
|
|
|
}
|
|
|
|
|
|
|
|
func NewProjInfo(file string, modDirName string, modImportPath string) (projInfo *ProjectInfo, err error) {
|
|
|
|
projInfo = &ProjectInfo{}
|
|
|
|
wd, err := os.Getwd()
|
|
|
|
if err != nil {
|
|
|
|
panic("cannot get working directory")
|
|
|
|
}
|
|
|
|
protoAbs := wd + "/" + file
|
|
|
|
protoAbs, _ = filepath.Abs(protoAbs)
|
|
|
|
|
|
|
|
if !ioutil2.FileExists(protoAbs) {
|
|
|
|
return nil, errors.Errorf("Cannot find proto file in current dir %s, file: %s ", wd, file)
|
|
|
|
}
|
|
|
|
//appIndex := strings.Index(wd, modDirName)
|
|
|
|
//if appIndex == -1 {
|
|
|
|
// err = errors.New("not in " + modDirName)
|
|
|
|
// return nil, err
|
|
|
|
//}
|
|
|
|
|
|
|
|
projPath := LookupProjPath(protoAbs)
|
|
|
|
|
|
|
|
if projPath == "" {
|
|
|
|
err = errors.New("not in project")
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
rel, _ := filepath.Rel(wd, projPath)
|
|
|
|
projInfo.PathRefToProj = rel
|
|
|
|
projInfo.AbsolutePath = projPath
|
|
|
|
if ioutil2.FileExists(projPath + "/internal") {
|
|
|
|
projInfo.HasInternalPkg = true
|
|
|
|
}
|
|
|
|
|
|
|
|
i := strings.Index(projInfo.AbsolutePath, modDirName)
|
|
|
|
if i == -1 {
|
|
|
|
err = errors.Errorf("project is not inside module, project=%s, module=%s", projPath, modDirName)
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
relativePath := projInfo.AbsolutePath[i+len(modDirName):]
|
|
|
|
projInfo.ImportPath = modImportPath + relativePath
|
|
|
|
projInfo.Name = filepath.Base(projPath)
|
|
|
|
if p := filepath.Dir(projPath); p != "/" {
|
|
|
|
projInfo.Department = filepath.Base(p)
|
|
|
|
if p = filepath.Dir(p); p != "/" {
|
|
|
|
projInfo.Typ = filepath.Base(p)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return projInfo, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// LookupProjPath get project path by proto absolute path
|
|
|
|
// assume that proto is in the project's api directory
|
|
|
|
func LookupProjPath(protoAbs string) (result string) {
|
|
|
|
f := func(protoAbs string, dirs []string) string {
|
|
|
|
lastIndex := len(protoAbs)
|
|
|
|
curPath := protoAbs
|
|
|
|
|
|
|
|
for lastIndex > 0 {
|
|
|
|
found := true
|
|
|
|
for _, d := range dirs {
|
|
|
|
if !utils.IsDir(curPath + "/" + d) {
|
|
|
|
found = false
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if found {
|
|
|
|
return curPath
|
|
|
|
}
|
|
|
|
lastIndex = strings.LastIndex(curPath, string(os.PathSeparator))
|
|
|
|
curPath = protoAbs[:lastIndex]
|
|
|
|
}
|
|
|
|
result = ""
|
|
|
|
return result
|
|
|
|
}
|
|
|
|
|
|
|
|
firstStep := f(protoAbs, []string{"cmd", "api"})
|
|
|
|
if firstStep != "" {
|
|
|
|
return firstStep
|
|
|
|
}
|
|
|
|
return f(protoAbs, []string{"api"})
|
|
|
|
}
|