In Go templates/Sprig, how to modify a captured group in a regular expression before replacing it?

1.5k views Asked by At

I'd like to write a Go template which takes in a URL with a query parameter earliest which represents a relative time, e.g. earliest=-15m, and converts it to an absolute time relative to now as a Unix timestamp. So for example, https://search.com?earliest=-15m would be converted to https://search.com?earliest=1646626616.

So far, I've written the following HTML template:

<html>
    <p><a href='{{ .URL }}'>Original link</a></p>
    <p><a href='{{ regexReplaceAll "earliest=(.+?)&" .URL (list "earliest=" ((now).UTC.Unix | toString) "&" | join "") }}'>Modified link</a></p>
</html>

which, when I render it with this main.go,

package main

import (
    "html/template"
    "log"
    "os"

    "github.com/Masterminds/sprig"
)

type Alert struct {
    URL string
}

func main() {
    tmpl := template.Must(template.New("base.html").Funcs(sprig.FuncMap()).ParseGlob("*.html"))

    if err := tmpl.Execute(os.Stdout, &Alert{
        URL: `https://search.com/search?earliest=-5m&latest=now`,
    }); err != nil {
        log.Fatal(err)
    }
}

Shows the following:

<html>
    <p><a href='https://search.com/search?earliest=-5m&amp;latest=now'>Original link</a></p>
    <p><a href='https://search.com/search?earliest=1646627228&amp;latest=now'>Modified link</a></p>
</html>

This is similar to what I want except that the Unix timestamp represents the current time and not the time 5 minutes ago as intended. I'd actually like to pass now into the date_modify function where the argument is the captured group, -15m. However, if I try to modify the template replacing (now) with (now | date_modify ${1}), I get a syntax error, i.e. this template,

<html>
    <p><a href='{{ .URL }}'>Original link</a></p>
    <p><a href='{{ regexReplaceAll "earliest=(.+?)&" .URL (list "earliest=" ((now | date_modify ${1}).UTC.Unix | toString) "&" | join "") }}'>Modified link</a></p>
</html>

leads to

panic: template: base.html:3: bad character U+007B '{'

How could a pass the captured group in the regular expression as an argument to date_modify?

1

There are 1 answers

0
Kurt Peek On

I managed to get this to work by defining a variable,

package main

import (
    "html/template"
    "os"

    "github.com/Masterminds/sprig"
)

type Alert struct {
    URL string
}

func main() {
    tmpl, err := template.New("test").Funcs(sprig.FuncMap()).Parse(`
{{- $x := regexFind "earliest=(.+?)&" .URL | replace "earliest=" "" | replace "&" "" -}}
{{ regexReplaceAll "earliest=(.+?)&" .URL (list "earliest=" ((now).UTC.Unix | toString) "&" | join "") }}
{{ .URL }}`)
    if err != nil {
        panic(err)
    }
    err = tmpl.Execute(os.Stdout, &Alert{URL: "https://search.com?earliest=-15m&latest=now"})
    if err != nil {
        panic(err)
    }
}

which prints

https://search.com?earliest=1646631378&amp;latest=now
https://search.com?earliest=-15m&amp;latest=now

The code is not DRY and is rather verbose, however, so any suggestions to improve it are welcome.