Crosspath: add IsRoot(path) bool function

Add a function that can determine whether the given path is considered
a "root path". Considers `/`, `X:`, `X:\` and `X:/` root paths, where `X`
is any upper or lower case Latin letters.
This commit is contained in:
Sybren A. Stüvel 2022-06-23 16:40:46 +02:00
parent b441f3f3de
commit fd730351b5
2 changed files with 60 additions and 0 deletions

View File

@ -75,3 +75,32 @@ func ToNative(path string) string {
panic(fmt.Sprintf("this platform has an unknown path separator: %q", filepath.Separator))
}
}
func validDriveLetter(r rune) bool {
return ('A' <= r && r <= 'Z') || ('a' <= r && r <= 'z')
}
// IsRoot returns whether the given path is a root path or not.
// Paths "/", "C:", "C:\", and "C:/" are considered root, for all latin drive
// letters A-Z.
//
// NOTE: this does NOT resolve symlinks or `..` entries.
func IsRoot(path string) bool {
switch {
case path == "":
return false
case path == "/":
return true
// From here on, it can only be a DOS root, so must have a drive letter and a colon.
case len(path) < 2, len(path) > 3:
return false
case path[1] != ':':
return false
// C:\ and C:/ are both considered roots.
case len(path) == 3 && path[2] != '/' && path[2] != '\\':
return false
}
runes := []rune(path)
return validDriveLetter(runes[0])
}

View File

@ -169,3 +169,34 @@ func TestToNative_unsupported(t *testing.T) {
t.Fatalf("ToNative not supported on this platform %q with path separator %q",
runtime.GOOS, filepath.Separator)
}
func TestIsRoot(t *testing.T) {
tests := []struct {
name string
path string
want bool
}{
{"empty", "", false},
{"UNIX", "/", true},
{"Drive only", "C:", true},
{"Drive and slash", "C:/", true},
{"Drive and backslash", `C:\`, true},
{"backslash", `\`, false},
{"just letters", "just letters", false},
{"subdir of root", "/subdir", false},
{"subdir of drive", "C:\\subdir", false},
{"indirectly root", "/subdir/..", false},
{"UNC notation", `\\NAS\Share\`, false},
{"Slashed UNC notation", `//NAS/Share/`, false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := IsRoot(tt.path); got != tt.want {
t.Errorf("IsRoot() = %v, want %v", got, tt.want)
}
})
}
}