From 8a70c5b521c04ab540a899f5de7e4bb656474963 Mon Sep 17 00:00:00 2001 From: zoncoen Date: Thu, 25 Jan 2024 17:26:58 +0900 Subject: [PATCH] feat: add IsCaseInsensitive --- context.go | 17 +++++++++++++++++ key.go | 14 ++++++++++++++ key_test.go | 41 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 72 insertions(+) create mode 100644 context.go diff --git a/context.go b/context.go new file mode 100644 index 0000000..9f6f48c --- /dev/null +++ b/context.go @@ -0,0 +1,17 @@ +package query + +import "context" + +var caseInsensitiveKey = struct{}{} + +func withCaseInsensitive(ctx context.Context, b bool) context.Context { + return context.WithValue(ctx, caseInsensitiveKey, b) +} + +// IsCaseInsensitive reports whether case-insensitive querying is enabled or not. +func IsCaseInsensitive(ctx context.Context) bool { + if b, ok := ctx.Value(caseInsensitiveKey).(bool); ok { + return b + } + return false +} diff --git a/key.go b/key.go index 6b9456d..d4922ea 100644 --- a/key.go +++ b/key.go @@ -1,6 +1,7 @@ package query import ( + "context" "reflect" "strings" ) @@ -13,6 +14,14 @@ type KeyExtractor interface { ExtractByKey(key string) (interface{}, bool) } +// KeyExtractorContext is the interface that wraps the ExtractByKey method. +// +// ExtractByKey extracts the value by key. +// It reports whether the key is found and returns the found value. +type KeyExtractorContext interface { + ExtractByKey(ctx context.Context, key string) (any, bool) +} + // Key represents an extractor to access the value by key. type Key struct { key string @@ -28,6 +37,11 @@ type Key struct { // If v implements the KeyExtractor interface, this method extracts by calling v.ExtractByKey. func (e *Key) Extract(v reflect.Value) (reflect.Value, bool) { if v.IsValid() { + if i, ok := v.Interface().(KeyExtractorContext); ok { + ctx := withCaseInsensitive(context.Background(), e.caseInsensitive) + x, ok := i.ExtractByKey(ctx, e.key) + return reflect.ValueOf(x), ok + } if i, ok := v.Interface().(KeyExtractor); ok { x, ok := i.ExtractByKey(e.key) return reflect.ValueOf(x), ok diff --git a/key_test.go b/key_test.go index 978241c..1071e1d 100644 --- a/key_test.go +++ b/key_test.go @@ -1,8 +1,10 @@ package query import ( + "context" "net/http" "reflect" + "strings" "testing" "github.com/google/go-cmp/cmp" @@ -19,6 +21,27 @@ func (f *keyExtractor) ExtractByKey(_ string) (interface{}, bool) { return nil, false } +type keyExtractorContext struct { + v map[string]any +} + +func (f *keyExtractorContext) ExtractByKey(ctx context.Context, name string) (interface{}, bool) { + if f.v != nil { + if v, ok := f.v[name]; ok { + return v, true + } + if IsCaseInsensitive(ctx) { + name = strings.ToLower(name) + for k, v := range f.v { + if strings.ToLower(k) == name { + return v, true + } + } + } + } + return nil, false +} + type testTags struct { FooBar string `json:"foo_bar" yaml:"fooBar,omitempty"` AnonymousField @@ -160,6 +183,16 @@ func TestKey_Extract(t *testing.T) { v: &keyExtractor{v: "value"}, expect: "value", }, + "key extractor context": { + key: "key", + caseInsensitive: true, + v: &keyExtractorContext{ + v: map[string]any{ + "KEY": "value", + }, + }, + expect: "value", + }, } for name, test := range tests { test := test @@ -249,6 +282,14 @@ func TestKey_Extract(t *testing.T) { }, }, }, + "key extractor context (case sensitive)": { + key: "key", + v: &keyExtractorContext{ + v: map[string]any{ + "KEY": "value", + }, + }, + }, } for name, test := range tests { test := test