115
internal/modules/packages.go
Normal file
115
internal/modules/packages.go
Normal file
@@ -0,0 +1,115 @@
|
||||
package modules
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/mod/module"
|
||||
"golang.org/x/mod/semver"
|
||||
)
|
||||
|
||||
// ModPrefix returns the module path with no SIV
|
||||
func ModPrefix(modpath string) string {
|
||||
prefix, _, ok := module.SplitPathVersion(modpath)
|
||||
if !ok {
|
||||
prefix = modpath
|
||||
}
|
||||
return prefix
|
||||
}
|
||||
|
||||
// ModMajor returns the major version in vN format
|
||||
func ModMajor(modpath string) (string, bool) {
|
||||
_, major, ok := module.SplitPathVersion(modpath)
|
||||
if ok {
|
||||
major = strings.TrimPrefix(major, "/")
|
||||
major = strings.TrimPrefix(major, ".")
|
||||
}
|
||||
return major, ok
|
||||
}
|
||||
|
||||
// SplitPath splits the package path into the module path and the package subdirectory.
|
||||
// It requires the a module path prefix to figure this out.
|
||||
func SplitPath(modprefix, pkgpath string) (modpath, pkgdir string, ok bool) {
|
||||
if !strings.HasPrefix(pkgpath, modprefix) {
|
||||
return "", "", false
|
||||
}
|
||||
modpathlen := len(modprefix)
|
||||
if rest := pkgpath[modpathlen:]; len(rest) > 0 && rest[0] != '/' && rest[0] != '.' {
|
||||
return "", "", false
|
||||
}
|
||||
if strings.HasPrefix(pkgpath[modpathlen:], "/") {
|
||||
modpathlen++
|
||||
}
|
||||
if idx := strings.Index(pkgpath[modpathlen:], "/"); idx >= 0 {
|
||||
modpathlen += idx
|
||||
} else {
|
||||
modpathlen = len(pkgpath)
|
||||
}
|
||||
modpath = modprefix
|
||||
if major, ok := ModMajor(pkgpath[:modpathlen]); ok {
|
||||
modpath = JoinPath(modprefix, major, "")
|
||||
}
|
||||
pkgdir = strings.TrimPrefix(pkgpath[len(modpath):], "/")
|
||||
return modpath, pkgdir, true
|
||||
}
|
||||
|
||||
// SplitSpec splits the path/to/package@query format strings
|
||||
func SplitSpec(spec string) (path, query string) {
|
||||
parts := strings.SplitN(spec, "@", 2)
|
||||
if len(parts) == 2 {
|
||||
path = parts[0]
|
||||
query = parts[1]
|
||||
} else {
|
||||
path = spec
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// JoinPath creates a full package path given a module prefix, version, and package directory.
|
||||
func JoinPath(modprefix, version, pkgdir string) string {
|
||||
version = strings.TrimPrefix(version, ".")
|
||||
version = strings.TrimPrefix(version, "/")
|
||||
major := semver.Major(version)
|
||||
pkgpath := modprefix
|
||||
switch {
|
||||
case strings.HasPrefix(pkgpath, "gopkg.in"):
|
||||
pkgpath += "." + major
|
||||
case major != "" && major != "v0" && major != "v1" && !strings.Contains(version, "+incompatible"):
|
||||
if !strings.HasSuffix(pkgpath, "/") {
|
||||
pkgpath += "/"
|
||||
}
|
||||
pkgpath += major
|
||||
}
|
||||
if pkgdir != "" {
|
||||
pkgpath += "/" + pkgdir
|
||||
}
|
||||
return pkgpath
|
||||
}
|
||||
|
||||
// FindModFile recursively searches up the directory structure until it
|
||||
// finds the go.mod, reaches the root of the directory tree, or encounters
|
||||
// an error.
|
||||
func FindModFile(dir string) (string, error) {
|
||||
var err error
|
||||
dir, err = filepath.Abs(dir)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
for {
|
||||
name := filepath.Join(dir, "go.mod")
|
||||
_, err := os.Stat(name)
|
||||
if err == nil {
|
||||
return name, nil
|
||||
}
|
||||
if !os.IsNotExist(err) {
|
||||
return "", err
|
||||
}
|
||||
parent := filepath.Dir(dir)
|
||||
if parent == dir {
|
||||
return "", fmt.Errorf("cannot find go.mod")
|
||||
}
|
||||
dir = parent
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user