Skip to content

Commit

Permalink
Merge pull request #53 from zoncoen/inline
Browse files Browse the repository at this point in the history
feat: add CustomIsInlineStructFieldFunc
  • Loading branch information
zoncoen authored Jan 25, 2024
2 parents 10914ca + 18f9bfb commit f19d6a5
Show file tree
Hide file tree
Showing 4 changed files with 61 additions and 8 deletions.
18 changes: 15 additions & 3 deletions key.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ type Key struct {
caseInsensitive bool
structTags []string
fieldNameGetter func(f reflect.StructField) string
isInlineFuncs []func(reflect.StructField) bool
}

// Extract extracts the value from v by key.
Expand Down Expand Up @@ -57,6 +58,7 @@ func (e *Key) extract(v reflect.Value) (reflect.Value, bool) {
for i := 0; i < v.Type().NumField(); i++ {
field := v.Type().FieldByIndex([]int{i})
fieldNames := []string{}
var inline bool
for _, t := range e.structTags {
if s := field.Tag.Get(t); s != "" {
name, opts, _ := strings.Cut(s, ",")
Expand All @@ -65,7 +67,8 @@ func (e *Key) extract(v reflect.Value) (reflect.Value, bool) {
}
for _, o := range strings.Split(opts, ",") {
if o == "inline" {
inlines = append(inlines, i)
inline = true
break
}
}
}
Expand All @@ -86,6 +89,16 @@ func (e *Key) extract(v reflect.Value) (reflect.Value, bool) {
}
}
if field.Anonymous {
inline = true
}
for _, f := range e.isInlineFuncs {
f := f
if f(field) {
inlines = append(inlines, i)
break
}
}
if inline {
inlines = append(inlines, i)
}
}
Expand Down Expand Up @@ -124,8 +137,7 @@ func isUnexportedField(v reflect.Value) bool {
func (e *Key) String() string {
for _, ch := range e.key {
switch ch {
case '[', '.',
'\\', '\'':
case '[', '.', '\\', '\'':
return quote(e.key)
}
}
Expand Down
40 changes: 36 additions & 4 deletions key_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ func (f *keyExtractor) ExtractByKey(_ string) (interface{}, bool) {
type testTags struct {
FooBar string `json:"foo_bar" yaml:"fooBar,omitempty"`
AnonymousField
M map[string]string `json:",inline"`
M map[string]string `json:",inline"`
Inline map[string]string

state struct{}
State string `json:"state"`
Expand All @@ -38,6 +39,7 @@ func TestKey_Extract(t *testing.T) {
key string
caseInsensitive bool
structTags []string
isInlineFuncs []func(reflect.StructField) bool
v interface{}
expect interface{}
}{
Expand Down Expand Up @@ -123,6 +125,23 @@ func TestKey_Extract(t *testing.T) {
},
expect: "xxx",
},
"struct (custom inline func)": {
key: "aaa",
isInlineFuncs: []func(reflect.StructField) bool{
func(f reflect.StructField) bool {
return f.Name == "Inline"
},
},
v: testTags{
M: map[string]string{
"aaa": "xxx",
},
Inline: map[string]string{
"aaa": "yyy",
},
},
expect: "yyy",
},
"struct (fallthrough unexported field)": {
key: "state",
structTags: []string{"json"},
Expand All @@ -149,6 +168,7 @@ func TestKey_Extract(t *testing.T) {
key: test.key,
caseInsensitive: test.caseInsensitive,
structTags: test.structTags,
isInlineFuncs: test.isInlineFuncs,
}
v, ok := e.Extract(reflect.ValueOf(test.v))
if !ok {
Expand All @@ -162,9 +182,10 @@ func TestKey_Extract(t *testing.T) {
})
t.Run("not found", func(t *testing.T) {
tests := map[string]struct {
key string
structTags []string
v interface{}
key string
structTags []string
isInlineFuncs []func(reflect.StructField) bool
v interface{}
}{
"target is nil": {
key: "key",
Expand Down Expand Up @@ -217,6 +238,17 @@ func TestKey_Extract(t *testing.T) {
},
},
},
"inline (no custom inline func)": {
key: "aaa",
v: testTags{
M: map[string]string{
"aaa": "xxx",
},
Inline: map[string]string{
"aaa": "yyy",
},
},
},
}
for name, test := range tests {
test := test
Expand Down
9 changes: 8 additions & 1 deletion option.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ func CaseInsensitive() Option {
// ExtractByStructTag returns the Option to allow extracting by struct tag.
func ExtractByStructTag(tagNames ...string) Option {
return func(q *Query) {
q.structTags = tagNames
q.structTags = append(q.structTags, tagNames...)
}
}

Expand All @@ -35,3 +35,10 @@ func CustomStructFieldNameGetter(f func(f reflect.StructField) string) Option {
q.customStructFieldNameGetter = f
}
}

// CustomIsInlineStructFieldFunc returns the Option to customize the behavior of extractors.
func CustomIsInlineStructFieldFunc(f func(reflect.StructField) bool) Option {
return func(q *Query) {
q.customIsInlineFuncs = append(q.customIsInlineFuncs, f)
}
}
2 changes: 2 additions & 0 deletions query.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ type Query struct {
structTags []string
customExtractFuncs []func(ExtractFunc) ExtractFunc
customStructFieldNameGetter func(f reflect.StructField) string
customIsInlineFuncs []func(reflect.StructField) bool
hasExplicitRoot bool
}

Expand Down Expand Up @@ -48,6 +49,7 @@ func (q Query) Key(k string) *Query {
caseInsensitive: q.caseInsensitive,
structTags: q.structTags,
fieldNameGetter: q.customStructFieldNameGetter,
isInlineFuncs: q.customIsInlineFuncs,
})
}

Expand Down

0 comments on commit f19d6a5

Please sign in to comment.