Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,8 @@ type Field interface {
// - tag: string, representing the tag name.
// - ignoreParentTagMissing: bool, representing whether to ignore the missing parent tags or not.
// It returns the tag value path as a string.
// Embedded structs without the name in the tag are transparent: their fields are on the level of the embedding struct,
// like in encoding/json, so they are not missing parents.
GetTagPath(tag string, ignoreParentTagMissing bool) string

// GetParent returns the parent field of the current field, if not exist return nil.
Expand Down
23 changes: 14 additions & 9 deletions field.go
Original file line number Diff line number Diff line change
Expand Up @@ -175,21 +175,26 @@ func (f *field) Get(obj interface{}) interface{} {
}
}

// tagName returns the name from the tag value, the part before the comma: json:"name,omitempty" -> name.
func (f *field) tagName(tag string) string {
return strings.Split(f.Tag.Get(tag), ",")[0]
}

func (f *field) GetTagPath(tag string, ignoreParentTagMissing bool) string {
tagPath := ""
if val, ok := f.Tag.Lookup(tag); ok {
vals := strings.Split(val, ",")
if len(vals) > 0 && len(vals[0]) > 0 {
tagPath = vals[0]
}
}
tagPath := f.tagName(tag)
if tagPath == "" {
return tagPath
}
if f.parent == nil {
// embedded structs without the name in the tag are transparent, their fields are on the level of the embedding struct,
// like in encoding/json and the most of the encoding libraries; yaml:",inline" has no name too
parent := f.parent
for parent != nil && parent.Anonymous && parent.tagName(tag) == "" {
parent = parent.parent
}
if parent == nil {
return tagPath
}
parentTag := f.parent.GetTagPath(tag, ignoreParentTagMissing)
parentTag := parent.GetTagPath(tag, ignoreParentTagMissing)
if parentTag == "" && !ignoreParentTagMissing {
return ""
}
Expand Down
57 changes: 57 additions & 0 deletions field_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -539,3 +539,60 @@ func TestField_SetNamedTypes(t *testing.T) {
}, *conf)
assert.Same(t, &duration, conf.DurationPtr)
}

type tagPathCommon struct {
Port int `json:"port" yaml:"port"`
}

type tagPathDeep struct {
tagPathCommon
Level string `json:"level" yaml:"level"`
}

type tagPathConfig struct {
tagPathCommon
HTTP struct {
*tagPathDeep
Host string `json:"host" yaml:"host"`
} `json:"http" yaml:"http"`
Named struct {
tagPathCommon `json:"common" yaml:",inline"`
} `json:"named" yaml:"named"`
NoTag struct {
tagPathCommon
}
}

func TestField_GetTagPath_Embedded(t *testing.T) {
storage, err := Get[tagPathConfig]()
if err != nil {
t.Fatal(err)
}
tests := []struct {
path string
tag string
ignoreParentTag bool
want string
}{
// fields of the embedded structs are on the level of the embedding struct
{"tagPathCommon.Port", "json", false, "port"},
{"HTTP.tagPathDeep.Level", "json", false, "http.level"},
// embedded into embedded, stored by pointer
{"HTTP.tagPathDeep.tagPathCommon.Port", "json", false, "http.port"},
{"HTTP.tagPathDeep.tagPathCommon.Port", "json", true, "http.port"},
// the name in the tag of the embedded struct is used
{"Named.tagPathCommon.Port", "json", false, "named.common.port"},
// the tag without the name, inline for example, is transparent too
{"Named.tagPathCommon.Port", "yaml", false, "named.port"},
// the named parent without the tag is missing
{"NoTag.tagPathCommon.Port", "json", false, ""},
{"NoTag.tagPathCommon.Port", "json", true, "port"},
}
for _, tt := range tests {
t.Run(tt.path+" "+tt.tag, func(t *testing.T) {
if got := storage.MustFind(tt.path).GetTagPath(tt.tag, tt.ignoreParentTag); got != tt.want {
t.Errorf("field.GetTagPath() = %v, want %v", got, tt.want)
}
})
}
}
2 changes: 2 additions & 0 deletions types.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@ type Field interface {
// - tag: string, representing the tag name.
// - ignoreParentTagMissing: bool, representing whether to ignore the missing parent tags or not.
// It returns the tag value path as a string.
// Embedded structs without the name in the tag are transparent: their fields are on the level of the embedding struct,
// like in encoding/json, so they are not missing parents.
GetTagPath(tag string, ignoreParentTagMissing bool) string

// GetParent returns the parent field of the current field, if not exist return nil.
Expand Down
Loading