diff options
Diffstat (limited to 'tpl/urls')
-rw-r--r-- | tpl/urls/init.go | 8 | ||||
-rw-r--r-- | tpl/urls/urls.go | 35 | ||||
-rw-r--r-- | tpl/urls/urls_test.go | 35 |
3 files changed, 78 insertions, 0 deletions
diff --git a/tpl/urls/init.go b/tpl/urls/init.go index ec954640d..cdc597f34 100644 --- a/tpl/urls/init.go +++ b/tpl/urls/init.go @@ -68,6 +68,14 @@ func init() { }, ) + ns.AddMethodMapping(ctx.JoinPath, + nil, + [][2]string{ + {`{{ urls.JoinPath "https://example.org" "foo" }}`, `https://example.org/foo`}, + {`{{ urls.JoinPath (slice "a" "b") }}`, `a/b`}, + }, + ) + return ns } diff --git a/tpl/urls/urls.go b/tpl/urls/urls.go index 551b53875..5de2342d4 100644 --- a/tpl/urls/urls.go +++ b/tpl/urls/urls.go @@ -185,3 +185,38 @@ func (ns *Namespace) AbsLangURL(s any) (template.HTML, error) { return template.HTML(ns.deps.PathSpec.AbsURL(ss, !ns.multihost)), nil } + +// JoinPath joins the provided elements into a URL string and cleans the result +// of any ./ or ../ elements. +func (ns *Namespace) JoinPath(elements ...any) (string, error) { + + var selements []string + for _, e := range elements { + switch v := e.(type) { + case []string: + for _, e := range v { + selements = append(selements, e) + } + case []any: + for _, e := range v { + se, err := cast.ToStringE(e) + if err != nil { + return "", err + } + selements = append(selements, se) + } + default: + se, err := cast.ToStringE(e) + if err != nil { + return "", err + } + selements = append(selements, se) + } + } + + result, err := url.JoinPath(selements[0], selements[1:]...) + if err != nil { + return "", err + } + return result, nil +} diff --git a/tpl/urls/urls_test.go b/tpl/urls/urls_test.go index f33e128be..1567875c0 100644 --- a/tpl/urls/urls_test.go +++ b/tpl/urls/urls_test.go @@ -69,3 +69,38 @@ func TestParse(t *testing.T) { qt.CmpEquals(hqt.DeepAllowUnexported(&url.URL{}, url.Userinfo{})), test.expect) } } + +func TestJoinPath(t *testing.T) { + t.Parallel() + c := qt.New(t) + + for _, test := range []struct { + elements any + expect any + }{ + {"", `/`}, + {"a", `a`}, + {"/a/b", `/a/b`}, + {"./../a/b", `a/b`}, + {[]any{""}, `/`}, + {[]any{"a"}, `a`}, + {[]any{"/a", "b"}, `/a/b`}, + {[]any{".", "..", "/a", "b"}, `a/b`}, + {[]any{"https://example.org", "a"}, `https://example.org/a`}, + {[]any{nil}, `/`}, + // errors + {tstNoStringer{}, false}, + {[]any{tstNoStringer{}}, false}, + } { + + result, err := ns.JoinPath(test.elements) + + if b, ok := test.expect.(bool); ok && !b { + c.Assert(err, qt.Not(qt.IsNil)) + continue + } + + c.Assert(err, qt.IsNil) + c.Assert(result, qt.Equals, test.expect) + } +} |