From e2565063ed35c6723f0ce524561abb0444ba5ac7 Mon Sep 17 00:00:00 2001 From: josephyu Date: Sun, 16 Aug 2020 15:26:57 +0800 Subject: [PATCH 1/2] Make lookup support Ptr type --- lookup.go | 30 +++++++++++++++++++++--------- lookup_test.go | 8 ++++++++ 2 files changed, 29 insertions(+), 9 deletions(-) diff --git a/lookup.go b/lookup.go index 763e8e8..860eab8 100644 --- a/lookup.go +++ b/lookup.go @@ -43,17 +43,14 @@ func lookup(name string, context ...interface{}) (interface{}, bool) { // struct that matches the name. In the near future I'd like to add // support for matching struct names to tags so we can use lower_case // names in our templates which makes it more mustache like. + case reflect.Ptr: + reflectValue = reflectValue.Elem() + fallthrough case reflect.Struct: - field := reflectValue.FieldByName(name) - if field.IsValid() { - return field.Interface(), truth(field) + result, isTrue := lookupStructByName(reflectValue, name) + if result != nil { + return result, isTrue } - method := reflectValue.MethodByName(name) - if method.IsValid() && method.Type().NumIn() == 1 { - out := method.Call(nil)[0] - return out.Interface(), truth(out) - } - } // If by this point no value was matched, we'll move up a step in the // chain and try to match a value there. @@ -63,6 +60,21 @@ func lookup(name string, context ...interface{}) (interface{}, bool) { return nil, false } +// lookupStructByName will return the field or the method result with the given name. +// Returns nil, false if no such field or method. +func lookupStructByName(reflectValue reflect.Value, name string) (interface{}, bool) { + field := reflectValue.FieldByName(name) + if field.IsValid() { + return field.Interface(), truth(field) + } + method := reflectValue.MethodByName(name) + if method.IsValid() && method.Type().NumIn() == 1 { + out := method.Call(nil)[0] + return out.Interface(), truth(out) + } + return nil, false +} + // The truth function will tell us if r is a truthy value or not. This is // important for sections as they will render their content based on the output // of this function. diff --git a/lookup_test.go b/lookup_test.go index eb58628..d5f5906 100644 --- a/lookup_test.go +++ b/lookup_test.go @@ -24,6 +24,13 @@ func TestSimpleLookup(t *testing.T) { "map": map[string]interface{}{ "in": "I'm nested!", }, + "ptr": &struct { + Foo *struct{ Bar string } + }{ + Foo: &struct{ Bar string }{ + Bar: "bar", + }, + }, }, assertions: []struct { name string @@ -34,6 +41,7 @@ func TestSimpleLookup(t *testing.T) { {"string", "abc", true}, {"boolean", true, true}, {"map.in", "I'm nested!", true}, + {"ptr.Foo.Bar", "bar", true}, }, }, { From bd215fbb20486bb0c5b7854ae108c632567bc444 Mon Sep 17 00:00:00 2001 From: josephyu Date: Sun, 16 Aug 2020 15:41:42 +0800 Subject: [PATCH 2/2] Support struct field tag --- lookup.go | 26 +++++++++++++++++++++++--- lookup_test.go | 6 ++++++ 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/lookup.go b/lookup.go index 860eab8..c3a5da6 100644 --- a/lookup.go +++ b/lookup.go @@ -40,9 +40,8 @@ func lookup(name string, context ...interface{}) (interface{}, bool) { return item.Interface(), truth(item) } // If the current context is a struct, we'll look for a property in that - // struct that matches the name. In the near future I'd like to add - // support for matching struct names to tags so we can use lower_case - // names in our templates which makes it more mustache like. + // struct that matches the name. + // Support struct field tags, like `mustache:"foo_bar"` case reflect.Ptr: reflectValue = reflectValue.Elem() fallthrough @@ -60,6 +59,8 @@ func lookup(name string, context ...interface{}) (interface{}, bool) { return nil, false } +const kLookupTagKey = "mustache" + // lookupStructByName will return the field or the method result with the given name. // Returns nil, false if no such field or method. func lookupStructByName(reflectValue reflect.Value, name string) (interface{}, bool) { @@ -72,6 +73,25 @@ func lookupStructByName(reflectValue reflect.Value, name string) (interface{}, b out := method.Call(nil)[0] return out.Interface(), truth(out) } + + for i := 0; i < reflectValue.NumField(); i++ { + tag := reflectValue.Type().Field(i).Tag + val, ok := tag.Lookup(kLookupTagKey) + if !ok { + continue + } + // NOTE: Is there any option we need to add? + // Just treat all tag value as the symbol name for now. + if val != name { + continue + } + field := reflectValue.Field(i) + if !field.IsValid() { + continue + } + return field.Interface(), truth(field) + } + return nil, false } diff --git a/lookup_test.go b/lookup_test.go index d5f5906..3241d84 100644 --- a/lookup_test.go +++ b/lookup_test.go @@ -31,6 +31,11 @@ func TestSimpleLookup(t *testing.T) { Bar: "bar", }, }, + "tag": &struct { + Foo string `mustache:"foo"` + }{ + Foo: "foo", + }, }, assertions: []struct { name string @@ -42,6 +47,7 @@ func TestSimpleLookup(t *testing.T) { {"boolean", true, true}, {"map.in", "I'm nested!", true}, {"ptr.Foo.Bar", "bar", true}, + {"tag.foo", "foo", true}, }, }, {