aboutsummaryrefslogtreecommitdiffhomepage
path: root/modules/caddyhttp/templates
diff options
context:
space:
mode:
authorAleks <[email protected]>2022-05-25 01:47:08 +0200
committerGitHub <[email protected]>2022-05-24 19:47:08 -0400
commit6891f7f421eac71dac8f8687255ede5189e7eb3a (patch)
tree7974c4b7577eb9982956befff02d190c47dd03eb /modules/caddyhttp/templates
parent499ad6d182d6993b1d149a5ee96dbd37d14cacd7 (diff)
downloadcaddy-6891f7f421eac71dac8f8687255ede5189e7eb3a.tar.gz
caddy-6891f7f421eac71dac8f8687255ede5189e7eb3a.zip
templates: Add `humanize` function (#4767)
Co-authored-by: Francis Lavoie <[email protected]>
Diffstat (limited to 'modules/caddyhttp/templates')
-rw-r--r--modules/caddyhttp/templates/templates.go23
-rw-r--r--modules/caddyhttp/templates/tplcontext.go40
-rw-r--r--modules/caddyhttp/templates/tplcontext_test.go49
3 files changed, 112 insertions, 0 deletions
diff --git a/modules/caddyhttp/templates/templates.go b/modules/caddyhttp/templates/templates.go
index b2fe184d7..85612ba07 100644
--- a/modules/caddyhttp/templates/templates.go
+++ b/modules/caddyhttp/templates/templates.go
@@ -238,6 +238,29 @@ func init() {
// {{stripHTML "Shows <b>only</b> text content"}}
// ```
//
+// ##### `humanize`
+//
+// Transforms size and time inputs to a human readable format.
+// This uses the [go-humanize](https://github.com/dustin/go-humanize) library.
+//
+// The first argument must be a format type, and the last argument
+// is the input, or the input can be piped in. The supported format
+// types are:
+// - **size** which turns an integer amount of bytes into a string like `2.3 MB`
+// - **time** which turns a time string into a relative time string like `2 weeks ago`
+//
+// For the `time` format, the layout for parsing the input can be configured
+// by appending a colon `:` followed by the desired time layout. You can
+// find the documentation on time layouts [in Go's docs](https://pkg.go.dev/time#pkg-constants).
+// The default time layout is `RFC1123Z`, i.e. `Mon, 02 Jan 2006 15:04:05 -0700`.
+//
+// ```
+// {{humanize "size" "2048000"}}
+// {{placeholder "http.response.header.Content-Length" | humanize "size"}}
+// {{humanize "time" "Fri, 05 May 2022 15:04:05 +0200"}}
+// {{humanize "time:2006-Jan-02" "2022-May-05"}}
+// ```
+
type Templates struct {
// The root path from which to load files. Required if template functions
// accessing the file system are used (such as include). Default is
diff --git a/modules/caddyhttp/templates/tplcontext.go b/modules/caddyhttp/templates/tplcontext.go
index 7843455a5..bae24ba8b 100644
--- a/modules/caddyhttp/templates/tplcontext.go
+++ b/modules/caddyhttp/templates/tplcontext.go
@@ -26,11 +26,13 @@ import (
"strings"
"sync"
"text/template"
+ "time"
"github.com/Masterminds/sprig/v3"
"github.com/alecthomas/chroma/formatters/html"
"github.com/caddyserver/caddy/v2"
"github.com/caddyserver/caddy/v2/modules/caddyhttp"
+ "github.com/dustin/go-humanize"
"github.com/yuin/goldmark"
highlighting "github.com/yuin/goldmark-highlighting"
"github.com/yuin/goldmark/extension"
@@ -81,6 +83,7 @@ func (c *TemplateContext) NewTemplate(tplName string) *template.Template {
"placeholder": c.funcPlaceholder,
"fileExists": c.funcFileExists,
"httpError": c.funcHTTPError,
+ "humanize": c.funcHumanize,
})
return c.tpl
}
@@ -398,6 +401,43 @@ func (c TemplateContext) funcHTTPError(statusCode int) (bool, error) {
return false, caddyhttp.Error(statusCode, nil)
}
+// funcHumanize transforms size and time inputs to a human readable format.
+//
+// Size inputs are expected to be integers, and are formatted as a
+// byte size, such as "83 MB".
+//
+// Time inputs are parsed using the given layout (default layout is RFC1123Z)
+// and are formatted as a relative time, such as "2 weeks ago".
+// See https://pkg.go.dev/time#pkg-constants for time layout docs.
+func (c TemplateContext) funcHumanize(formatType, data string) (string, error) {
+ // The format type can optionally be followed
+ // by a colon to provide arguments for the format
+ parts := strings.Split(formatType, ":")
+
+ switch parts[0] {
+ case "size":
+ dataint, dataerr := strconv.ParseUint(data, 10, 64)
+ if dataerr != nil {
+ return "", fmt.Errorf("humanize: size cannot be parsed: %s", dataerr.Error())
+ }
+ return humanize.Bytes(dataint), nil
+
+ case "time":
+ timelayout := time.RFC1123Z
+ if len(parts) > 1 {
+ timelayout = parts[1]
+ }
+
+ dataint, dataerr := time.Parse(timelayout, data)
+ if dataerr != nil {
+ return "", fmt.Errorf("humanize: time cannot be parsed: %s", dataerr.Error())
+ }
+ return humanize.Time(dataint), nil
+ }
+
+ return "", fmt.Errorf("no know function was given")
+}
+
// WrappedHeader wraps niladic functions so that they
// can be used in templates. (Template functions must
// return a value.)
diff --git a/modules/caddyhttp/templates/tplcontext_test.go b/modules/caddyhttp/templates/tplcontext_test.go
index 61fc80c65..15a369ee8 100644
--- a/modules/caddyhttp/templates/tplcontext_test.go
+++ b/modules/caddyhttp/templates/tplcontext_test.go
@@ -606,6 +606,55 @@ title = "Welcome"
}
+func TestHumanize(t *testing.T) {
+ tplContext := getContextOrFail(t)
+ for i, test := range []struct {
+ format string
+ inputData string
+ expect string
+ errorCase bool
+ verifyErr func(actual_string, substring string) bool
+ }{
+ {
+ format: "size",
+ inputData: "2048000",
+ expect: "2.0 MB",
+ errorCase: false,
+ verifyErr: strings.Contains,
+ },
+ {
+ format: "time",
+ inputData: "Fri, 05 May 2022 15:04:05 +0200",
+ expect: "ago",
+ errorCase: false,
+ verifyErr: strings.HasSuffix,
+ },
+ {
+ format: "time:2006-Jan-02",
+ inputData: "2022-May-05",
+ expect: "ago",
+ errorCase: false,
+ verifyErr: strings.HasSuffix,
+ },
+ {
+ format: "time",
+ inputData: "Fri, 05 May 2022 15:04:05 GMT+0200",
+ expect: "error:",
+ errorCase: true,
+ verifyErr: strings.HasPrefix,
+ },
+ } {
+ if actual, err := tplContext.funcHumanize(test.format, test.inputData); !test.verifyErr(actual, test.expect) {
+ if !test.errorCase {
+ t.Errorf("Test %d: Expected '%s' but got '%s'", i, test.expect, actual)
+ if err != nil {
+ t.Errorf("Test %d: error: %s", i, err.Error())
+ }
+ }
+ }
+ }
+}
+
func getContextOrFail(t *testing.T) TemplateContext {
tplContext, err := initTestContext()
t.Cleanup(func() {