Skip to content

Commit 8d329ed

Browse files
committed
Allow arbitrary nesting of struct tag values.
This uses a pattern that leverages the idea that all of these values are just maps, and an optional path can be provided to access a value at an arbitrary nested level in the struct tags. Why would you want to do this? I don't know, it's probably not a good idea to go any further than one or two nestings. However, it does make the signature a little nicer to work with as this makes the second param optional.
1 parent 09380c9 commit 8d329ed

File tree

3 files changed

+35
-31
lines changed

3 files changed

+35
-31
lines changed

stronf/field.go

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -68,24 +68,6 @@ func (f Field) set(val any) error {
6868
return nil
6969
}
7070

71-
// LookupTag will return the value associated with the key and optional tag. See
72-
// examples for supported formats. The bool reports if the key or tag was
73-
// explicitly found in the struct tag.
74-
func (f Field) LookupTag(key, tag string) (string, bool) {
75-
value, ok := f.rStructField.Tag.Lookup(key)
76-
if !ok {
77-
return "", false
78-
}
79-
80-
if tag == "" {
81-
return value, true
82-
}
83-
84-
tags := parseStructTag(value)
85-
val, ok := tags[tag]
86-
return val, ok
87-
}
88-
8971
// Parse will call the handler against the field and set the field to the
9072
// returned handler value. If the handler returns nil, no change is made to the
9173
// field.

stronf/field_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ func TestField_LookupTag(t *testing.T) {
5050
}
5151

5252
field := fields[0]
53-
v, ok := field.LookupTag("key", "")
53+
v, ok := field.LookupTag("key")
5454
if !ok {
5555
t.Error("expected key to exist")
5656
}
@@ -74,7 +74,7 @@ func TestField_LookupTag(t *testing.T) {
7474
}
7575

7676
field := fields[0]
77-
v, ok := field.LookupTag("key", "")
77+
v, ok := field.LookupTag("key")
7878
if !ok {
7979
t.Error("expected key to exist")
8080
}
@@ -140,7 +140,7 @@ func ExampleField_LookupTag() {
140140
log.Println("should not recieve a value for a key that doesn't exist")
141141
}
142142

143-
if val, ok := field.LookupTag("emptyKey", ""); ok {
143+
if val, ok := field.LookupTag("emptyKey"); ok {
144144
fmt.Printf("val: %q\n", val)
145145
}
146146

stronf/structtag.go

Lines changed: 32 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,40 @@ package stronf
22

33
import "strings"
44

5-
func parseStructTag(tag string) map[string]string {
6-
options := make(map[string]string)
7-
for _, option := range strings.Split(tag, ",") {
8-
optionParts := strings.SplitN(option, ":", 2)
9-
key := optionParts[0]
10-
var value string
11-
if len(optionParts) > 1 {
12-
value = optionParts[1]
5+
// LookupTag will return the value associated with the tag and optional path.
6+
// The tag arg is the only required argument and uses the reflect package's
7+
// Lookup semantics. An optional path can be provided to lookup nested values.
8+
// Nested values can themselves be maps, each key/val pair separated by a comma.
9+
// See examples for supported formats. The bool reports if the value was
10+
// explicitly found at the struct tag path.
11+
func (f Field) LookupTag(tag string, path ...string) (string, bool) {
12+
value, ok := f.rStructField.Tag.Lookup(tag)
13+
if !ok {
14+
return "", false
15+
}
16+
17+
if len(path) == 0 {
18+
return value, true
19+
}
20+
21+
parseStructTag := func(tag string) map[string]string {
22+
subTags := make(map[string]string)
23+
for _, subTag := range strings.Split(tag, ",") {
24+
key, val, _ := strings.Cut(subTag, ":")
25+
subTags[key] = val
1326
}
1427

15-
options[key] = value
28+
return subTags
29+
}
30+
31+
val, ok := "", false
32+
for _, p := range path {
33+
tags := parseStructTag(value)
34+
val, ok = tags[p]
35+
if !ok {
36+
return "", false
37+
}
1638
}
1739

18-
return options
40+
return val, ok
1941
}

0 commit comments

Comments
 (0)