feat: `hasField` function for use with structs
Recently worked on a project that used generics in a similar fashion:
type A[T any] struct {
Metadata T
}
type B struct {
Foo string
}
type C struct {
Bar string
}
Then, similar objects were being passed to a template for rendering:
ab := &A[B]{Metadata: B{Foo: "Lorem"}}
ac := &A[C]{Metadata: C{Bar: "Ipsum"}}
A section in the template had to look slightly different depending on the type of metadata being passed in, which I solved by using a custom hasField method:
{{if hasField $.Metadata "Foo"}}
We have Foo.
{{else}}
We have no Foo.
{{end}}
The hasField method comes from this StackOverflow thread: Field detection in Go HTML template and looks like this:
func hasField(v interface{}, name string) bool {
rv := reflect.ValueOf(v)
if rv.Kind() == reflect.Ptr {
rv = rv.Elem()
}
if rv.Kind() != reflect.Struct {
return false
}
return rv.FieldByName(name).IsValid()
}
Full code example
package main
import (
"log"
"os"
"reflect"
"text/template"
)
type A[T any] struct {
Metadata T
}
type B struct {
Foo string
}
type C struct {
Bar string
}
func main() {
ab := &A[B]{Metadata: B{Foo: "Lorem"}}
ac := &A[C]{Metadata: C{Bar: "Ipsum"}}
s := `
{{if hasField $.Metadata "Foo"}}
We have Foo.
{{else}}
We have no Foo.
{{end}}
`
f := template.FuncMap{"hasField": hasField}
t, err := template.
New("test").
Funcs(f).
Parse(s)
if err != nil {
log.Fatal(err)
}
_ = t.Execute(os.Stdout, ab)
_ = t.Execute(os.Stdout, ac)
}
func hasField(v interface{}, name string) bool {
rv := reflect.ValueOf(v)
if rv.Kind() == reflect.Ptr {
rv = rv.Elem()
}
if rv.Kind() != reflect.Struct {
return false
}
return rv.FieldByName(name).IsValid()
}
Would you approve a PR that adds a hasField method to Sprig, or is that something you prefer to keep separate from the main library?
I'd be happy to open a PR!
Hi @mbezhanov, as sprig seams to be not maintained actually, we decide to create a fork to go forward.
You can go forward by creating and reference this initial issue on https://github.com/go-sprout/sprout and you can create a PR for sure in the registry reflect.
Thanks for your understand and your time ! 🌱
Sugar code snippets:
rv := reflect.ValueOf(v)
if rv.Kind() == reflect.Ptr {
rv = rv.Elem()
}
As the same feature of reflect.Indirect, I lets you check that :)