pathutil.go 2.1 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677
  1. // Copyright (c) Tailscale Inc & AUTHORS
  2. // SPDX-License-Identifier: BSD-3-Clause
  3. package shared
  4. import (
  5. "net/url"
  6. "path"
  7. "strings"
  8. )
  9. // This file provides utility functions for working with URL paths. These are
  10. // similar to functions in package path in the standard library, but differ in
  11. // ways that are documented on the relevant functions.
  12. const (
  13. sepString = "/"
  14. sepStringAndDot = "/."
  15. sep = '/'
  16. )
  17. // CleanAndSplit cleans the provided path p and splits it into its constituent
  18. // parts. This is different from path.Split which just splits a path into prefix
  19. // and suffix.
  20. //
  21. // If p is empty or contains only path separators, CleanAndSplit returns a slice
  22. // of length 1 whose only element is "".
  23. func CleanAndSplit(p string) []string {
  24. return strings.Split(strings.Trim(path.Clean(p), sepStringAndDot), sepString)
  25. }
  26. // Normalize normalizes the given path (e.g. dropping trailing slashes).
  27. func Normalize(p string) string {
  28. return Join(CleanAndSplit(p)...)
  29. }
  30. // Parent extracts the parent of the given path.
  31. func Parent(p string) string {
  32. parts := CleanAndSplit(p)
  33. return Join(parts[:len(parts)-1]...)
  34. }
  35. // Join behaves like path.Join() but also includes a leading slash.
  36. //
  37. // When parts are missing, the result is "/".
  38. func Join(parts ...string) string {
  39. fullParts := make([]string, 0, len(parts))
  40. fullParts = append(fullParts, sepString)
  41. for _, part := range parts {
  42. fullParts = append(fullParts, part)
  43. }
  44. return path.Join(fullParts...)
  45. }
  46. // JoinEscaped is like Join but path escapes each part.
  47. func JoinEscaped(parts ...string) string {
  48. fullParts := make([]string, 0, len(parts))
  49. fullParts = append(fullParts, sepString)
  50. for _, part := range parts {
  51. fullParts = append(fullParts, url.PathEscape(part))
  52. }
  53. return path.Join(fullParts...)
  54. }
  55. // IsRoot determines whether a given path p is the root path, defined as either
  56. // empty or "/".
  57. func IsRoot(p string) bool {
  58. return p == "" || p == sepString
  59. }
  60. // Base is like path.Base except that it returns "" for the root folder
  61. func Base(p string) string {
  62. if IsRoot(p) {
  63. return ""
  64. }
  65. return path.Base(p)
  66. }