github.com/fortexxx/gqlgen@v0.10.3-0.20191216030626-ca5ea8b21ead/internal/code/imports.go (about)

     1  package code
     2  
     3  import (
     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  
    17  var nameForPackageCache = sync.Map{}
    18  
    19  var gopaths []string
    20  
    21  func 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.
    30  func 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  // goModuleRoot returns the root of the current go module if there is a go.mod file in the directory tree
    55  // If not, it returns false
    56  func goModuleRoot(dir string) (string, bool) {
    57  	dir, err := filepath.Abs(dir)
    58  	if err != nil {
    59  		panic(err)
    60  	}
    61  	dir = filepath.ToSlash(dir)
    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, true
    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  	return "", false
    84  }
    85  
    86  // ImportPathForDir takes a path and returns a golang import path for the package
    87  func ImportPathForDir(dir string) (res string) {
    88  	dir, err := filepath.Abs(dir)
    89  	if err != nil {
    90  		panic(err)
    91  	}
    92  	dir = filepath.ToSlash(dir)
    93  
    94  	modDir, ok := goModuleRoot(dir)
    95  	if ok {
    96  		return modDir
    97  	}
    98  
    99  	for _, gopath := range gopaths {
   100  		if len(gopath) < len(dir) && strings.EqualFold(gopath, dir[0:len(gopath)]) {
   101  			return dir[len(gopath)+1:]
   102  		}
   103  	}
   104  
   105  	return ""
   106  }
   107  
   108  var modregex = regexp.MustCompile("module (.*)\n")
   109  
   110  // NameForPackage returns the package name for a given import path. This can be really slow.
   111  func NameForPackage(importPath string) string {
   112  	if importPath == "" {
   113  		panic(errors.New("import path can not be empty"))
   114  	}
   115  	if v, ok := nameForPackageCache.Load(importPath); ok {
   116  		return v.(string)
   117  	}
   118  	importPath = QualifyPackagePath(importPath)
   119  	p, _ := packages.Load(&packages.Config{
   120  		Mode: packages.NeedName,
   121  	}, importPath)
   122  
   123  	if len(p) != 1 || p[0].Name == "" {
   124  		return SanitizePackageName(filepath.Base(importPath))
   125  	}
   126  
   127  	nameForPackageCache.Store(importPath, p[0].Name)
   128  
   129  	return p[0].Name
   130  }