I 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}}
Full code example
```go
package main
import (
"html/template"
"log"
"os"
"reflect"
)
type A[T any] struct {
Metadata T
}
type B struct {
Foo string
}
type C struct {
Bar string
}
const tmpl = `
{{if hasField $.Metadata "Foo"}}
We have Foo.
{{else}}
We have no Foo.
{{end}}`
func main() {
f := template.FuncMap{"hasField": hasField}
t := template.Must(
template.New("base").Funcs(f).Parse(tmpl),
)
ab := &A[B]{Metadata: B{Foo: "Lorem"}}
ac := &A[C]{Metadata: C{Bar: "Ipsum"}}
if err := t.Execute(os.Stdout, ab); err != nil {
log.Fatal(err)
}
if err := t.Execute(os.Stdout, ac); err != nil {
log.Fatal(err)
}
}
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()
}
```
You have a proposal, explain it!
Originally proposed here: https://github.com/Masterminds/sprig/issues/401
I recently worked on a project that used generics in a similar fashion:
Then, similar objects were being passed to a template for rendering:
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:Full code example
```go package main import ( "html/template" "log" "os" "reflect" ) type A[T any] struct { Metadata T } type B struct { Foo string } type C struct { Bar string } const tmpl = ` {{if hasField $.Metadata "Foo"}} We have Foo. {{else}} We have no Foo. {{end}}` func main() { f := template.FuncMap{"hasField": hasField} t := template.Must( template.New("base").Funcs(f).Parse(tmpl), ) ab := &A[B]{Metadata: B{Foo: "Lorem"}} ac := &A[C]{Metadata: C{Bar: "Ipsum"}} if err := t.Execute(os.Stdout, ab); err != nil { log.Fatal(err) } if err := t.Execute(os.Stdout, ac); err != nil { log.Fatal(err) } } 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() } ```Describe the solution you'd like
I took the
hasField
method implementation from the following StackOverflow thread: Field detection in Go HTML template.It looks like this:
I feel something similar can be added to the
reflect
registry, so it can be used with Sprout out of the box.I'd be happy to open a PR!
Additional context
No response
Code of Conduct