imports.go

  1package code
  2
  3import (
  4	"errors"
  5	"go/build"
  6	"go/parser"
  7	"go/token"
  8	"io/ioutil"
  9	"path/filepath"
 10	"regexp"
 11	"strings"
 12	"sync"
 13
 14	"golang.org/x/tools/go/packages"
 15)
 16
 17var nameForPackageCache = sync.Map{}
 18
 19var gopaths []string
 20
 21func init() {
 22	gopaths = filepath.SplitList(build.Default.GOPATH)
 23	for i, p := range gopaths {
 24		gopaths[i] = filepath.ToSlash(filepath.Join(p, "src"))
 25	}
 26}
 27
 28// NameForDir manually looks for package stanzas in files located in the given directory. This can be
 29// much faster than having to consult go list, because we already know exactly where to look.
 30func NameForDir(dir string) string {
 31	dir, err := filepath.Abs(dir)
 32	if err != nil {
 33		return SanitizePackageName(filepath.Base(dir))
 34	}
 35	files, err := ioutil.ReadDir(dir)
 36	if err != nil {
 37		return SanitizePackageName(filepath.Base(dir))
 38	}
 39	fset := token.NewFileSet()
 40	for _, file := range files {
 41		if !strings.HasSuffix(strings.ToLower(file.Name()), ".go") {
 42			continue
 43		}
 44
 45		filename := filepath.Join(dir, file.Name())
 46		if src, err := parser.ParseFile(fset, filename, nil, parser.PackageClauseOnly); err == nil {
 47			return src.Name.Name
 48		}
 49	}
 50
 51	return SanitizePackageName(filepath.Base(dir))
 52}
 53
 54// ImportPathForDir takes a path and returns a golang import path for the package
 55func ImportPathForDir(dir string) (res string) {
 56	dir, err := filepath.Abs(dir)
 57	if err != nil {
 58		panic(err)
 59	}
 60	dir = filepath.ToSlash(dir)
 61
 62	modDir := dir
 63	assumedPart := ""
 64	for {
 65		f, err := ioutil.ReadFile(filepath.Join(modDir, "go.mod"))
 66		if err == nil {
 67			// found it, stop searching
 68			return string(modregex.FindSubmatch(f)[1]) + assumedPart
 69		}
 70
 71		assumedPart = "/" + filepath.Base(modDir) + assumedPart
 72		parentDir, err := filepath.Abs(filepath.Join(modDir, ".."))
 73		if err != nil {
 74			panic(err)
 75		}
 76
 77		if parentDir == modDir {
 78			// Walked all the way to the root and didnt find anything :'(
 79			break
 80		}
 81		modDir = parentDir
 82	}
 83
 84	for _, gopath := range gopaths {
 85		if len(gopath) < len(dir) && strings.EqualFold(gopath, dir[0:len(gopath)]) {
 86			return dir[len(gopath)+1:]
 87		}
 88	}
 89
 90	return ""
 91}
 92
 93var modregex = regexp.MustCompile("module (.*)\n")
 94
 95// NameForPackage returns the package name for a given import path. This can be really slow.
 96func NameForPackage(importPath string) string {
 97	if importPath == "" {
 98		panic(errors.New("import path can not be empty"))
 99	}
100	if v, ok := nameForPackageCache.Load(importPath); ok {
101		return v.(string)
102	}
103	importPath = QualifyPackagePath(importPath)
104	p, _ := packages.Load(&packages.Config{
105		Mode: packages.NeedName,
106	}, importPath)
107
108	if len(p) != 1 || p[0].Name == "" {
109		return SanitizePackageName(filepath.Base(importPath))
110	}
111
112	nameForPackageCache.Store(importPath, p[0].Name)
113
114	return p[0].Name
115}