-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathresolve.go
More file actions
109 lines (103 loc) · 2.77 KB
/
Copy pathresolve.go
File metadata and controls
109 lines (103 loc) · 2.77 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
package module
import (
"os"
"path/filepath"
"strings"
)
// Per Project Structure Spec: No more than 4 parts of a module
const MaxModuleDepth = 4
func splitPath(p string) (string, string) {
parent, base := filepath.Split(p)
return strings.TrimSuffix(parent, sep), base
}
// PackageRoot returns the package root and project root
// for a given path, following the Klar Project Structure Spec. For accurate
// results, p should be an absolute path.
func PackageRoot(p string) (pkg, project string) {
// Check if a manifest is located in dir
if info, err := os.Stat(p); err == nil && !info.IsDir() {
p = filepath.Dir(p)
if info.Name() == ManifestFile {
proj := p
if maybePkg := DirFast(p); filepath.Base(maybePkg) == PkgDir {
proj = DirFast(maybePkg)
}
return p, proj
}
}
if _, err := os.Stat(p + sep + ManifestFile); err == nil {
proj := p
if maybePkg := filepath.Dir(p); filepath.Base(maybePkg) == PkgDir {
proj = DirFast(maybePkg)
}
return p, proj
}
// Walk up the directory tree
curr, prev := filepath.Clean(p), ""
for {
parent, name := splitPath(curr)
// Stop if we've reached the root
if curr == parent {
break
}
if _, ok := KlarPackageDirs[name]; ok {
// Parent of 'pkg' guaranteed to be project root
if name == PkgDir {
return prev, parent // x/pkg/y -> (x/pkg/y, x, nil)
}
// Found the project root
if _, ok := ProjectOnlyDirs[name]; ok {
return parent, parent
}
// Check if parent is 'pkg' (e.g: x/pkg/y/src)
if pkgPar, pkg := splitPath(DirFast(parent)); pkg == PkgDir {
return parent, pkgPar
}
return parent, parent
}
// Track the last directory we saw (potential package inside pkg)
prev = curr // Child
curr = parent
}
// Not found
return p, p
}
// IsPackage reports whether p is a path to a package, as defined by the Klar
// Project Structure Spec. IsPackage assumes that p is a directory path.
func IsPackage(p string) bool {
if _, err := os.Stat(filepath.Join(p, ManifestFile)); err == nil {
return true
}
var depth int
var parent, name string
for {
// p is a package if a package directory is found
parent, name = filepath.Split(p)
switch {
case name == PkgDir:
// We're one level inside pkg folder - this is a package
return depth == 1
case IsPackageDir(name):
// Found a Klar project directory - not a package (parent is)
return false
case p == parent:
return true
}
p = strings.TrimSuffix(parent, sep)
depth++
}
}
// DirFast is [filepath.Dir] without running [filepath.Clean] on the result.
func DirFast(path string) string {
vol := filepath.VolumeName(path)
i := len(path) - 1
for i >= len(vol) && !os.IsPathSeparator(path[i]) {
i--
}
dir := path[len(vol) : i+1]
if dir == "." && len(vol) > 2 {
// must be UNC
return vol
}
return vol + dir
}