116 lines
2.8 KiB
Go
116 lines
2.8 KiB
Go
|
package main
|
||
|
|
||
|
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
|
||
|
}
|
||
|
}
|