]> git.maquefel.me Git - brevno-suite/hugo/commitdiff
tpl/urls: Add JoinPath template function
authorJoe Mooring <joe.mooring@veriphor.com>
Fri, 14 Apr 2023 20:27:16 +0000 (13:27 -0700)
committerBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
Fri, 19 May 2023 08:31:17 +0000 (10:31 +0200)
See https://pkg.go.dev/net/url#JoinPath

Closes #9694

docs/content/en/functions/urls.JoinPath.md [new file with mode: 0644]
tpl/urls/init.go
tpl/urls/urls.go
tpl/urls/urls_test.go

diff --git a/docs/content/en/functions/urls.JoinPath.md b/docs/content/en/functions/urls.JoinPath.md
new file mode 100644 (file)
index 0000000..c0e0464
--- /dev/null
@@ -0,0 +1,24 @@
+---
+title: urls.JoinPath
+description: Joins the provided elements into a URL string and cleans the result of any ./ or ../ elements.
+categories: [functions]
+menu:
+  docs:
+    parent: functions
+keywords: [urls,path,join]
+signature: ["urls.JoinPath ELEMENT..."]
+---
+
+```go-html-template
+{{ urls.JoinPath "" }} → "/"
+{{ urls.JoinPath "a" }} → "a"
+{{ urls.JoinPath "a" "b" }} → "a/b"
+{{ urls.JoinPath "/a" "b" }} → "/a/b"
+{{ urls.JoinPath "https://example.org" "b" }} → "https://example.org/b"
+
+{{ urls.JoinPath (slice "a" "b") }} → "a/b"
+```
+
+Unlike the [`path.Join`] function, `urls.JoinPath` retains consecutive leading slashes.
+
+[`path.Join`]: /functions/path.join/
index ec954640d2915535b6937c9a018f3629cbd9bf84..cdc597f3444b5718269f5626d9d039c69212543e 100644 (file)
@@ -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
        }
 
index 551b5387504e808d3705f5487796fb67262fc13c..5de2342d47b2689505eb84a62be13fe6e480c676 100644 (file)
@@ -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
+}
index f33e128be5bf62c2d1cda0fa9b8e3e14ed94c9dc..1567875c070905d26b5620e5ebd5df4bc5d3f57c 100644 (file)
@@ -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)
+       }
+}