tailscale/drive/driveimpl/shared/pathutil.go
Percy Wegmann 7209c4f91e drive: parse depth 1 PROPFIND results to include children in cache
Clients often perform a PROPFIND for the parent directory before
performing PROPFIND for specific children within that directory.
The PROPFIND for the parent directory is usually done at depth 1,
meaning that we already have information for all of the children.
By immediately adding that to the cache, we save a roundtrip to
the remote peer on the PROPFIND for the specific child.

Updates tailscale/corp#19779

Signed-off-by: Percy Wegmann <percy@tailscale.com>
2024-05-10 15:19:44 -05:00

73 lines
1.9 KiB
Go

// Copyright (c) Tailscale Inc & AUTHORS
// SPDX-License-Identifier: BSD-3-Clause
package shared
import (
"net/url"
"path"
"strings"
)
// This file provides utility functions for working with URL paths. These are
// similar to functions in package path in the standard library, but differ in
// ways that are documented on the relevant functions.
const (
sepString = "/"
sepStringAndDot = "/."
sep = '/'
)
// CleanAndSplit cleans the provided path p and splits it into its constituent
// parts. This is different from path.Split which just splits a path into prefix
// and suffix.
func CleanAndSplit(p string) []string {
return strings.Split(strings.Trim(path.Clean(p), sepStringAndDot), sepString)
}
// Normalize normalizes the given path (e.g. dropping trailing slashes).
func Normalize(p string) string {
return Join(CleanAndSplit(p)...)
}
// Parent extracts the parent of the given path.
func Parent(p string) string {
parts := CleanAndSplit(p)
return Join(parts[:len(parts)-1]...)
}
// Join behaves like path.Join() but also includes a leading slash.
func Join(parts ...string) string {
fullParts := make([]string, 0, len(parts))
fullParts = append(fullParts, sepString)
for _, part := range parts {
fullParts = append(fullParts, part)
}
return path.Join(fullParts...)
}
// JoinEscaped is like Join but path escapes each part.
func JoinEscaped(parts ...string) string {
fullParts := make([]string, 0, len(parts))
fullParts = append(fullParts, sepString)
for _, part := range parts {
fullParts = append(fullParts, url.PathEscape(part))
}
return path.Join(fullParts...)
}
// IsRoot determines whether a given path p is the root path, defined as either
// empty or "/".
func IsRoot(p string) bool {
return p == "" || p == sepString
}
// Base is like path.Base except that it returns "" for the root folder
func Base(p string) string {
if IsRoot(p) {
return ""
}
return path.Base(p)
}