29
definition/generate/pkg/alias.go
Normal file
29
definition/generate/pkg/alias.go
Normal file
@@ -0,0 +1,29 @@
|
||||
package pkg
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
func AliasName(name string) string {
|
||||
data := strings.Builder{}
|
||||
toUp := false
|
||||
|
||||
for _, char := range name {
|
||||
isLeter := unicode.IsLetter(char)
|
||||
isAllowed := isLeter || unicode.IsDigit(char)
|
||||
|
||||
switch {
|
||||
case isAllowed && !toUp:
|
||||
data.WriteRune(char)
|
||||
case !isAllowed && data.Len() > 0:
|
||||
toUp = true
|
||||
case toUp:
|
||||
data.WriteString(strings.ToUpper(string(char)))
|
||||
|
||||
toUp = false
|
||||
}
|
||||
}
|
||||
|
||||
return data.String()
|
||||
}
|
||||
8
definition/generate/pkg/errors.go
Normal file
8
definition/generate/pkg/errors.go
Normal file
@@ -0,0 +1,8 @@
|
||||
package pkg
|
||||
|
||||
import "errors"
|
||||
|
||||
var (
|
||||
ErrWrongFormat = errors.New("wrong format")
|
||||
ErrNotFound = errors.New("not found")
|
||||
)
|
||||
106
definition/generate/pkg/imports.go
Normal file
106
definition/generate/pkg/imports.go
Normal file
@@ -0,0 +1,106 @@
|
||||
package pkg
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func NewImports(pkg string) *Imports {
|
||||
imp := Imports{
|
||||
data: make(map[string]string),
|
||||
pkg: pkg,
|
||||
}
|
||||
|
||||
return &imp
|
||||
}
|
||||
|
||||
type Imports struct {
|
||||
data map[string]string
|
||||
pkg string
|
||||
}
|
||||
|
||||
func (i *Imports) Imports() []Import {
|
||||
imports := make([]Import, 0, len(i.data))
|
||||
for name, alias := range i.data {
|
||||
imports = append(imports, Import{
|
||||
Package: name,
|
||||
Alias: alias,
|
||||
})
|
||||
}
|
||||
|
||||
return imports
|
||||
}
|
||||
|
||||
func (i *Imports) Short(fullType string) (string, error) {
|
||||
idx := strings.LastIndexByte(fullType, '.')
|
||||
if idx == -1 {
|
||||
return "", fmt.Errorf("%w: expect package.Type", ErrWrongFormat)
|
||||
}
|
||||
|
||||
if alias, ok := i.data[fullType[:idx]]; ok {
|
||||
return alias + fullType[idx:], nil
|
||||
}
|
||||
|
||||
return "", fmt.Errorf("%w alias for pkg %v", ErrNotFound, fullType[:idx])
|
||||
}
|
||||
|
||||
func (i *Imports) AddType(fullType string) (string, error) {
|
||||
idx := strings.LastIndexByte(fullType, '.')
|
||||
if idx == -1 {
|
||||
return "", fmt.Errorf("%w: expect pckage.Type got %v", ErrWrongFormat, fullType)
|
||||
}
|
||||
|
||||
imp := i.Add(fullType[:idx])
|
||||
|
||||
if imp.Alias == "" {
|
||||
return fullType[idx+1:], nil
|
||||
}
|
||||
|
||||
return imp.Alias + fullType[idx:], nil
|
||||
}
|
||||
|
||||
func (i *Imports) Adds(pkgs ...string) *Imports {
|
||||
for _, pkg := range pkgs {
|
||||
i.Add(pkg)
|
||||
}
|
||||
|
||||
return i
|
||||
}
|
||||
|
||||
func (i *Imports) Add(pkg string) Import {
|
||||
if pkg == i.pkg {
|
||||
return Import{
|
||||
Alias: "",
|
||||
Package: pkg,
|
||||
}
|
||||
}
|
||||
|
||||
alias := pkg
|
||||
|
||||
if idx := strings.LastIndexByte(pkg, '/'); idx != -1 {
|
||||
alias = AliasName(pkg[idx+1:])
|
||||
}
|
||||
|
||||
if al, ok := i.data[pkg]; ok {
|
||||
return Import{Package: pkg, Alias: al}
|
||||
}
|
||||
|
||||
for _, al := range i.data {
|
||||
if al == alias {
|
||||
alias += strconv.Itoa(len(i.data))
|
||||
}
|
||||
}
|
||||
|
||||
i.data[pkg] = alias
|
||||
|
||||
return Import{
|
||||
Alias: alias,
|
||||
Package: pkg,
|
||||
}
|
||||
}
|
||||
|
||||
type Import struct {
|
||||
Alias string
|
||||
Package string
|
||||
}
|
||||
187
definition/generate/pkg/pkg.go
Normal file
187
definition/generate/pkg/pkg.go
Normal file
@@ -0,0 +1,187 @@
|
||||
package pkg
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"go/build"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var cache = sync.Map{}
|
||||
|
||||
func ByPath(ctx context.Context, fname string, isDir bool) (string, error) {
|
||||
if !filepath.IsAbs(fname) {
|
||||
pwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("%w", err)
|
||||
}
|
||||
|
||||
fname = filepath.Join(pwd, fname)
|
||||
}
|
||||
|
||||
goModPath, _ := goModPath(ctx, fname, isDir)
|
||||
if strings.Contains(goModPath, "go.mod") {
|
||||
pkgPath, err := getPkgPathFromGoMod(fname, isDir, goModPath)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return pkgPath, nil
|
||||
}
|
||||
|
||||
return getPkgPathFromGOPATH(fname, isDir)
|
||||
}
|
||||
|
||||
// empty if no go.mod, GO111MODULE=off or go without go modules support.
|
||||
func goModPath(ctx context.Context, fname string, isDir bool) (string, error) {
|
||||
root := fname
|
||||
if !isDir {
|
||||
root = filepath.Dir(fname)
|
||||
}
|
||||
|
||||
var modPath string
|
||||
|
||||
loadModPath, ok := cache.Load(root)
|
||||
if ok {
|
||||
modPath, _ = loadModPath.(string)
|
||||
|
||||
return modPath, nil
|
||||
}
|
||||
|
||||
defer func() {
|
||||
cache.Store(root, modPath)
|
||||
}()
|
||||
|
||||
cmd := exec.CommandContext(ctx, "go", "env", "GOMOD")
|
||||
cmd.Dir = root
|
||||
|
||||
stdout, err := cmd.Output()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("%w", err)
|
||||
}
|
||||
|
||||
modPath = string(bytes.TrimSpace(stdout))
|
||||
|
||||
return modPath, nil
|
||||
}
|
||||
|
||||
func getPkgPathFromGoMod(fname string, isDir bool, goModPath string) (string, error) {
|
||||
modulePath := getModulePath(goModPath)
|
||||
if modulePath == "" {
|
||||
return "", fmt.Errorf("c%w module path from %s", ErrNotFound, goModPath)
|
||||
}
|
||||
|
||||
rel := path.Join(modulePath, filePathToPackagePath(strings.TrimPrefix(fname, filepath.Dir(goModPath))))
|
||||
|
||||
if !isDir {
|
||||
return path.Dir(rel), nil
|
||||
}
|
||||
|
||||
return path.Clean(rel), nil
|
||||
}
|
||||
|
||||
func getModulePath(goModPath string) string {
|
||||
var pkgPath string
|
||||
|
||||
cacheOkgPath, ok := cache.Load(goModPath)
|
||||
if ok {
|
||||
pkgPath, _ = cacheOkgPath.(string)
|
||||
|
||||
return pkgPath
|
||||
}
|
||||
|
||||
defer func() {
|
||||
cache.Store(goModPath, pkgPath)
|
||||
}()
|
||||
|
||||
data, err := os.ReadFile(goModPath)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
pkgPath = modulePath(data)
|
||||
|
||||
return pkgPath
|
||||
}
|
||||
|
||||
func getPkgPathFromGOPATH(fname string, isDir bool) (string, error) {
|
||||
gopath := os.Getenv("GOPATH")
|
||||
if gopath == "" {
|
||||
gopath = build.Default.GOPATH
|
||||
}
|
||||
|
||||
for _, p := range strings.Split(gopath, string(filepath.ListSeparator)) {
|
||||
prefix := filepath.Join(p, "src") + string(filepath.Separator)
|
||||
|
||||
rel, err := filepath.Rel(prefix, fname)
|
||||
if err == nil && !strings.HasPrefix(rel, ".."+string(filepath.Separator)) {
|
||||
if !isDir {
|
||||
return path.Dir(filePathToPackagePath(rel)), nil
|
||||
}
|
||||
|
||||
return path.Clean(filePathToPackagePath(rel)), nil
|
||||
}
|
||||
}
|
||||
|
||||
return "", fmt.Errorf("%w: file '%v' is not in GOPATH '%v'", ErrNotFound, fname, gopath)
|
||||
}
|
||||
|
||||
func filePathToPackagePath(path string) string {
|
||||
return filepath.ToSlash(path)
|
||||
}
|
||||
|
||||
var (
|
||||
slashSlash = []byte("//")
|
||||
moduleStr = []byte("module")
|
||||
)
|
||||
|
||||
// modulePath returns the module path from the gomod file text.
|
||||
// If it cannot find a module path, it returns an empty string.
|
||||
// It is tolerant of unrelated problems in the go.mod file.
|
||||
func modulePath(mod []byte) string {
|
||||
for len(mod) > 0 {
|
||||
line := mod
|
||||
|
||||
mod = nil
|
||||
if i := bytes.IndexByte(line, '\n'); i >= 0 {
|
||||
line, mod = line[:i], line[i+1:]
|
||||
}
|
||||
|
||||
if i := bytes.Index(line, slashSlash); i >= 0 {
|
||||
line = line[:i]
|
||||
}
|
||||
|
||||
line = bytes.TrimSpace(line)
|
||||
if !bytes.HasPrefix(line, moduleStr) {
|
||||
continue
|
||||
}
|
||||
|
||||
line = line[len(moduleStr):]
|
||||
n := len(line)
|
||||
|
||||
line = bytes.TrimSpace(line)
|
||||
if len(line) == n || len(line) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
if line[0] == '"' || line[0] == '`' {
|
||||
p, err := strconv.Unquote(string(line))
|
||||
if err != nil {
|
||||
return "" // malformed quoted string or multiline module path
|
||||
}
|
||||
|
||||
return p
|
||||
}
|
||||
|
||||
return string(line)
|
||||
}
|
||||
|
||||
return "" // missing module path
|
||||
}
|
||||
Reference in New Issue
Block a user