Repository navigation
Expand file tree
/
Copy pathmod.go
More file actions
144 lines (131 loc) · 3.47 KB
/
Copy pathmod.go
File metadata and controls
144 lines (131 loc) · 3.47 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
package mod
import (
"errors"
"fmt"
"go/build"
"io/fs"
"os"
"path/filepath"
"strings"
"golang.org/x/mod/modfile"
)
// ErrFileNotFound occurs when no go.mod can be found
var ErrFileNotFound = fmt.Errorf(`mod: unable to find "go.mod": %w`, fs.ErrNotExist)
// New module
func New(dir string) *Module {
modulePath := Infer(dir)
if modulePath == "" {
modulePath = "change.me"
}
module, err := Parse(filepath.Join(dir, "go.mod"), []byte(`module `+modulePath))
if err != nil {
panic("mod: invalid module data: " + err.Error())
}
return module
}
// Find the first go.mod file in one of the directories below or return an
// error. Find will also search parent directories for a go.mod file.
func Find(dirs ...string) (*Module, error) {
if len(dirs) == 0 {
return find(".")
}
for _, dir := range dirs {
module, err := find(dir)
if err != nil {
if !errors.Is(err, ErrFileNotFound) {
return nil, err
}
continue
}
return module, nil
}
return nil, ErrFileNotFound
}
func find(dir string) (*Module, error) {
absDir, err := filepath.Abs(dir)
if err != nil {
return nil, err
}
modPath, err := lookup(absDir)
if err != nil {
return nil, err
}
data, err := os.ReadFile(modPath)
if err != nil {
return nil, fmt.Errorf(`mod: unable to read "go.mod": %w`, err)
}
return Parse(modPath, data)
}
// MustFind a go.mod file in this directory or any parent directory. If no
// go.mod file is found, this will panic.
func MustFind(dirs ...string) *Module {
module, err := Find(dirs...)
if err != nil {
panic(err)
}
return module
}
// Parse a go.mod file
func Parse(path string, data []byte) (*Module, error) {
modfile, err := modfile.Parse(path, data, nil)
if err != nil {
return nil, err
}
if modfile.Module == nil {
modFile, err := modfile.Format()
if err != nil {
return nil, fmt.Errorf("mod: missing module statement in %q and got an error while formatting %s", path, err)
}
return nil, fmt.Errorf("mod: missing module statement in %q, received %q", path, string(modFile))
}
dir := filepath.Dir(path)
return &Module{dir, modfile, os.DirFS(dir)}, nil
}
// Lookup finds the absolute path of the go.mod file in the given directory
func Lookup(dir string) (path string, err error) {
path, err = lookup(dir)
if err != nil {
return "", err
}
return filepath.Abs(path)
}
func lookup(dir string) (path string, err error) {
path = filepath.Join(dir, "go.mod")
// Check if this path exists, otherwise recursively traverse towards root
if _, err = os.Stat(path); err != nil {
if !errors.Is(err, fs.ErrNotExist) {
return "", err
}
nextDir := filepath.Dir(dir)
if nextDir == dir {
return "", ErrFileNotFound
}
return lookup(nextDir)
}
return filepath.EvalSymlinks(path)
}
// Abs finds the absolute directory that contains the go.mod file
func Abs(dir string) (absDir string, err error) {
path, err := lookup(dir)
if err != nil {
return "", err
}
absPath, err := filepath.Abs(path)
if err != nil {
return "", err
}
return filepath.Dir(absPath), nil
}
// GOPATH is the default GOPATH for the current build environment. This is
// exposed for testing purposes, but should not be changed in production code.
var GOPATH = build.Default.GOPATH
// Infer the module path from the $GOPATH. This only works if you work inside
// $GOPATH otherwise returns an empty string.
func Infer(dir string) string {
src := filepath.Join(GOPATH, "src") + "/"
if !strings.HasPrefix(dir, src) {
return ""
}
modulePath := strings.TrimPrefix(dir, src)
return modulePath
}