// JoinPath returns a new [URL] with the provided path elements joined to
// any existing path and the resulting path cleaned of any ./ or ../ elements.
// Any sequences of multiple / characters will be reduced to a single /.
+// Path elements must already be in escaped form, as produced by [PathEscape].
func (u *URL) JoinPath(elem ...string) *URL {
+ url, _ := u.joinPath(elem...)
+ return url
+}
+
+func (u *URL) joinPath(elem ...string) (*URL, error) {
elem = append([]string{u.EscapedPath()}, elem...)
var p string
if !strings.HasPrefix(elem[0], "/") {
p += "/"
}
url := *u
- url.setPath(p)
- return &url
+ err := url.setPath(p)
+ return &url, err
}
// validUserinfo reports whether s is a valid userinfo string per RFC 3986
// JoinPath returns a [URL] string with the provided path elements joined to
// the existing path of base and the resulting path cleaned of any ./ or ../ elements.
+// Path elements must already be in escaped form, as produced by [PathEscape].
func JoinPath(base string, elem ...string) (result string, err error) {
url, err := Parse(base)
if err != nil {
return
}
- result = url.JoinPath(elem...).String()
- return
+ res, err := url.joinPath(elem...)
+ if err != nil {
+ return "", err
+ }
+ return res.String(), nil
}
elem: []string{"../go"},
out: "https://go.googlesource.com/go",
},
- {
- base: "https://go.googlesource.com",
- elem: []string{"../go"},
- out: "https://go.googlesource.com/go",
- },
{
base: "https://go.googlesource.com",
elem: []string{"../go", "../../go", "../../../go"},
elem: []string{"/go"},
out: "https://go.googlesource.com/a/b/go",
},
+ {
+ base: "https://go.googlesource.com/",
+ elem: []string{"100%"},
+ },
{
base: "/",
elem: nil,
if tt.out == "" {
wantErr = "non-nil error"
}
- if out, err := JoinPath(tt.base, tt.elem...); out != tt.out || (err == nil) != (tt.out != "") {
+ out, err := JoinPath(tt.base, tt.elem...)
+ if out != tt.out || (err == nil) != (tt.out != "") {
t.Errorf("JoinPath(%q, %q) = %q, %v, want %q, %v", tt.base, tt.elem, out, err, tt.out, wantErr)
}
- var out string
+
u, err := Parse(tt.base)
- if err == nil {
- u = u.JoinPath(tt.elem...)
- out = u.String()
+ if err != nil {
+ if tt.out != "" {
+ t.Errorf("Parse(%q) = %v", tt.base, err)
+ }
+ continue
}
- if out != tt.out || (err == nil) != (tt.out != "") {
- t.Errorf("Parse(%q).JoinPath(%q) = %q, %v, want %q, %v", tt.base, tt.elem, out, err, tt.out, wantErr)
+ if tt.out == "" {
+ // URL.JoinPath doesn't return an error, so leave it unchanged
+ tt.out = tt.base
+ }
+ out = u.JoinPath(tt.elem...).String()
+ if out != tt.out {
+ t.Errorf("Parse(%q).JoinPath(%q) = %q, want %q", tt.base, tt.elem, out, tt.out)
}
}
}