Search code examples
stringgogo-templates

golang text/template startswith function


I don't see any type of startswith function in the Go text/template package. Is this the best implementation?

{{if eq (slice $c 0 5) "begin"}}

Solution

  • There is no builtin startswith template function.

    The cleanest is if you register a custom function with that functionality:

    func main() {
        t := template.Must(template.New("").Funcs(template.FuncMap{
            "hasPrefix": strings.HasPrefix,
        }).Parse(src))
    
        for _, s := range []string{"foo", "begining"} {
            if err := t.Execute(os.Stdout, s); err != nil {
                panic(err)
            }
        }
    }
    
    const src = `{{.}}: {{if hasPrefix . "begin"}}yes{{else}}no{{end}}
    `
    

    This will output (try it on the Go Playground):

    foo: no
    begining: yes
    

    If you can't or don't want to register a custom function, slice works for strings, but you must use it with care: if the input string is shorter than 5 bytes, you'll get a template execution error!

    Instead (if you don't want to register a custom function), I suggest to use the builtin printf function with precision being the length of the string to compare to. printf will not panic if the input string is shorter:

    {{if eq (printf "%.5s" .) "begin"}}yes{{else}}no{{end}}
    

    This outputs the same. Try this one on the Go Playground.

    Note that using hasPrefix is safer, cleaner and simpler as we don't have to hard-code the length of the prefix (5).

    Note that using explicit argument indices we can also make this part dynamic:

    {{$prefix := "begin"}}{{if eq (printf "%.[1]*s" (len $prefix) .) $prefix}}yes{{else}}no{{end}}
    

    As you can see, we could get rid of the hard-coded length of 5 of the prefix. This again outputs the same, try it on the Go Playground.

    One last thing to note: slicing a string interprets indices as byte-indices, while the precision used in a format string is interpreted as rune count!