pkgdash/internal/modules/packages.go

116 lines
2.8 KiB
Go

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
}
}