Skip to content

Commit

Permalink
Merge pull request #68 from ggicci/feat/adjust-api
Browse files Browse the repository at this point in the history
feat: adjust API
  • Loading branch information
ggicci authored Sep 8, 2023
2 parents e75f7bc + 609b7ce commit 9be6ca3
Show file tree
Hide file tree
Showing 6 changed files with 63 additions and 44 deletions.
10 changes: 5 additions & 5 deletions core_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ func decodeMyDate(value string) (interface{}, error) {
return t, nil
}

var dateAdaptor = AdaptDecoderFunc[time.Time, string](decodeMyDate)
var myDateDecoder = DecoderFunc[string](decodeMyDate)

func (e *InvalidDate) Error() string {
return fmt.Sprintf("invalid date: %q (date must conform to format \"2006-01-02\"), %s", e.Value, e.Err)
Expand Down Expand Up @@ -227,7 +227,7 @@ func TestCore_Decode_UnexportedFields(t *testing.T) {
}

func TestCore_Decode_CustomDecoder_TypeDecoder(t *testing.T) {
RegisterValueTypeDecoder[bool](boolAdaptor) // usually done in init()
RegisterValueTypeDecoder[bool](myBoolDecoder) // usually done in init()

type BoolInput struct {
IsMember bool `in:"form=is_member"`
Expand All @@ -253,7 +253,7 @@ type CustomNamedDecoderInput struct {
}

func TestCore_Decode_CustomDecoder_NamedDecoder(t *testing.T) {
ReplaceNamedDecoder[time.Time]("decodeMyDate", dateAdaptor) // usually done in init()
ReplaceNamedDecoder[time.Time]("decodeMyDate", myDateDecoder) // usually done in init()

r, _ := http.NewRequest("GET", "/", nil)
r.Form = url.Values{
Expand Down Expand Up @@ -304,7 +304,7 @@ func TestCore_Decode_CustomDecoder_NamedDecoder_ErrDecoderNotFound(t *testing.T)
}

func TestCore_Decode_CustomDecoder_NamedDecoder_ErrValueTypeMismatch(t *testing.T) {
ReplaceNamedDecoder[time.Time]("decodeMyDate", dateAdaptor) // usually done in init()
ReplaceNamedDecoder[time.Time]("decodeMyDate", myDateDecoder) // usually done in init()

type Input struct {
Birthday string `in:"form=birthday;decoder=decodeMyDate"` // cause ErrValueTypeMismatch
Expand All @@ -322,7 +322,7 @@ func TestCore_Decode_CustomDecoder_NamedDecoder_ErrValueTypeMismatch(t *testing.
}

func TestCore_Decode_CustomDecoder_NamedDecoder_DecodeError(t *testing.T) {
ReplaceNamedDecoder[time.Time]("decodeMyDate", dateAdaptor) // usually done in init()
ReplaceNamedDecoder[time.Time]("decodeMyDate", myDateDecoder) // usually done in init()

r, _ := http.NewRequest("GET", "/", nil)
r.Form = url.Values{
Expand Down
38 changes: 19 additions & 19 deletions decoder.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,23 +14,23 @@ var (
)

func init() {
registerTypeDecoderTo[bool](builtinDecoders, AdaptDecoderFunc[bool, string](decodeBool), false)
registerTypeDecoderTo[int](builtinDecoders, AdaptDecoderFunc[int, string](decodeInt), false)
registerTypeDecoderTo[int8](builtinDecoders, AdaptDecoderFunc[int8, string](decodeInt8), false)
registerTypeDecoderTo[int16](builtinDecoders, AdaptDecoderFunc[int16, string](decodeInt16), false)
registerTypeDecoderTo[int32](builtinDecoders, AdaptDecoderFunc[int32, string](decodeInt32), false)
registerTypeDecoderTo[int64](builtinDecoders, AdaptDecoderFunc[int64, string](decodeInt64), false)
registerTypeDecoderTo[uint](builtinDecoders, AdaptDecoderFunc[uint, string](decodeUint), false)
registerTypeDecoderTo[uint8](builtinDecoders, AdaptDecoderFunc[uint8, string](decodeUint8), false)
registerTypeDecoderTo[uint16](builtinDecoders, AdaptDecoderFunc[uint16, string](decodeUint16), false)
registerTypeDecoderTo[uint32](builtinDecoders, AdaptDecoderFunc[uint32, string](decodeUint32), false)
registerTypeDecoderTo[uint64](builtinDecoders, AdaptDecoderFunc[uint64, string](decodeUint64), false)
registerTypeDecoderTo[float32](builtinDecoders, AdaptDecoderFunc[float32, string](decodeFloat32), false)
registerTypeDecoderTo[float64](builtinDecoders, AdaptDecoderFunc[float64, string](decodeFloat64), false)
registerTypeDecoderTo[complex64](builtinDecoders, AdaptDecoderFunc[complex64, string](decodeComplex64), false)
registerTypeDecoderTo[complex128](builtinDecoders, AdaptDecoderFunc[complex128, string](decodeComplex128), false)
registerTypeDecoderTo[string](builtinDecoders, AdaptDecoderFunc[string, string](decodeString), false)
registerTypeDecoderTo[time.Time](builtinDecoders, AdaptDecoderFunc[time.Time, string](decodeTime), false)
registerTypeDecoderTo[bool](builtinDecoders, DecoderFunc[string](decodeBool), false)
registerTypeDecoderTo[int](builtinDecoders, DecoderFunc[string](decodeInt), false)
registerTypeDecoderTo[int8](builtinDecoders, DecoderFunc[string](decodeInt8), false)
registerTypeDecoderTo[int16](builtinDecoders, DecoderFunc[string](decodeInt16), false)
registerTypeDecoderTo[int32](builtinDecoders, DecoderFunc[string](decodeInt32), false)
registerTypeDecoderTo[int64](builtinDecoders, DecoderFunc[string](decodeInt64), false)
registerTypeDecoderTo[uint](builtinDecoders, DecoderFunc[string](decodeUint), false)
registerTypeDecoderTo[uint8](builtinDecoders, DecoderFunc[string](decodeUint8), false)
registerTypeDecoderTo[uint16](builtinDecoders, DecoderFunc[string](decodeUint16), false)
registerTypeDecoderTo[uint32](builtinDecoders, DecoderFunc[string](decodeUint32), false)
registerTypeDecoderTo[uint64](builtinDecoders, DecoderFunc[string](decodeUint64), false)
registerTypeDecoderTo[float32](builtinDecoders, DecoderFunc[string](decodeFloat32), false)
registerTypeDecoderTo[float64](builtinDecoders, DecoderFunc[string](decodeFloat64), false)
registerTypeDecoderTo[complex64](builtinDecoders, DecoderFunc[string](decodeComplex64), false)
registerTypeDecoderTo[complex128](builtinDecoders, DecoderFunc[string](decodeComplex128), false)
registerTypeDecoderTo[string](builtinDecoders, DecoderFunc[string](decodeString), false)
registerTypeDecoderTo[time.Time](builtinDecoders, DecoderFunc[string](decodeTime), false)
}

type DataSource interface{ string | *multipart.FileHeader }
Expand Down Expand Up @@ -90,7 +90,7 @@ func registerTypeDecoderTo[T any](m map[reflect.Type]interface{}, decoder interf
panic(fmt.Errorf("httpin: %w: %q", ErrDuplicateTypeDecoder, typ))
}

m[typ] = adaptDecoder[T](decoder)
m[typ] = adaptDecoderX[T](decoder)
}

// RegisterNamedDecoder registers a decoder by name. Panics on conflicts.
Expand All @@ -105,7 +105,7 @@ func RegisterNamedDecoder[T any](name string, decoder interface{}) {
// ReplaceNamedDecoder replaces a decoder by name.
func ReplaceNamedDecoder[T any](name string, decoder interface{}) {
panicOnInvalidDecoder(decoder)
namedDecoders[name] = adaptDecoder[T](decoder)
namedDecoders[name] = adaptDecoderX[T](decoder)
}

func panicOnInvalidDecoder(decoder interface{}) {
Expand Down
18 changes: 9 additions & 9 deletions decoder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ func decodeCustomBool(value string) (interface{}, error) {
return strconv.ParseBool(sdata)
}

var boolAdaptor = AdaptDecoderFunc[bool, string](decodeCustomBool)
var myBoolDecoder = DecoderFunc[string](decodeCustomBool)

func invalidDecoder(string) error {
return nil
Expand Down Expand Up @@ -63,10 +63,10 @@ func TestRegisterTypeDecoder(t *testing.T) {

// Register duplicate decoder should fail.
assert.NotPanics(t, func() {
RegisterValueTypeDecoder[bool](boolAdaptor)
RegisterValueTypeDecoder[bool](myBoolDecoder)
})
assert.Panics(t, func() {
RegisterValueTypeDecoder[bool](boolAdaptor)
RegisterValueTypeDecoder[bool](myBoolDecoder)
})
removeTypeDecoder[bool]() // remove the custom decoder
}
Expand All @@ -78,31 +78,31 @@ func TestRegisterNamedDecoder(t *testing.T) {

// Register duplicate decoder should fail.
assert.NotPanics(t, func() {
RegisterNamedDecoder[bool]("mybool", boolAdaptor)
RegisterNamedDecoder[bool]("mybool", myBoolDecoder)
})
assert.Panics(t, func() {
RegisterNamedDecoder[bool]("mybool", boolAdaptor)
RegisterNamedDecoder[bool]("mybool", myBoolDecoder)
})
removeTypeDecoder[bool]() // remove the custom decoder
}

func TestReplaceTypeDecoder(t *testing.T) {
assert.NotPanics(t, func() {
ReplaceValueTypeDecoder[bool](boolAdaptor)
ReplaceValueTypeDecoder[bool](myBoolDecoder)
})

assert.NotPanics(t, func() {
ReplaceValueTypeDecoder[bool](boolAdaptor)
ReplaceValueTypeDecoder[bool](myBoolDecoder)
})
}

func TestReplaceNamedDecoder(t *testing.T) {
assert.NotPanics(t, func() {
ReplaceNamedDecoder[bool]("mybool", boolAdaptor)
ReplaceNamedDecoder[bool]("mybool", myBoolDecoder)
})

assert.NotPanics(t, func() {
ReplaceNamedDecoder[bool]("mybool", boolAdaptor)
ReplaceNamedDecoder[bool]("mybool", myBoolDecoder)
})
}

Expand Down
16 changes: 6 additions & 10 deletions decoderadaptor.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,28 +67,24 @@ func (sva *decoderAdaptorImpl[T, DT]) DecoderByKind(kind decoderKindType) decode
return nil
}

func AdaptDecoder[T any, DT DataSource](decoder Decoder[DT]) *decoderAdaptorImpl[T, DT] {
func adaptDecoder[T any, DT DataSource](decoder Decoder[DT]) *decoderAdaptorImpl[T, DT] {
return &decoderAdaptorImpl[T, DT]{decoder}
}

func AdaptDecoderFunc[T any, DT DataSource](fn func(value DT) (interface{}, error)) *decoderAdaptorImpl[T, DT] {
return &decoderAdaptorImpl[T, DT]{decoderFunc[DT](fn)}
}

func adaptDecoder[T any](decoder interface{}) interface{} {
func adaptDecoderX[T any](decoder interface{}) interface{} {
switch decoder := decoder.(type) {
case ValueTypeDecoder:
return AdaptDecoder[T, string](decoder)
return adaptDecoder[T, string](decoder)
case FileTypeDecoder:
return AdaptDecoder[T, *multipart.FileHeader](decoder)
return adaptDecoder[T, *multipart.FileHeader](decoder)
default:
return decoder // noop
}
}

type decoderFunc[DT DataSource] func(value DT) (interface{}, error)
type DecoderFunc[DT DataSource] func(value DT) (interface{}, error)

func (fn decoderFunc[DT]) Decode(value DT) (interface{}, error) {
func (fn DecoderFunc[DT]) Decode(value DT) (interface{}, error) {
return fn(value)
}

Expand Down
23 changes: 23 additions & 0 deletions default_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"net/url"
"testing"

"github.com/ggicci/httpin/patch"
"github.com/stretchr/testify/assert"
)

Expand All @@ -31,3 +32,25 @@ func TestDirectiveDefault(t *testing.T) {
assert.NoError(t, err)
assert.Equal(t, expected, got)
}

func TestDirectiveDefault_PatchField(t *testing.T) {
type ThingWithDefaultValues struct {
Page patch.Field[int] `in:"form=page;default=1"`
PerPage patch.Field[int] `in:"form=per_page;default=20"`
StateList patch.Field[[]string] `in:"form=state;default=pending,in_progress,failed"`
}

r := newMultipartFormRequestFromMap(map[string]interface{}{
"page": "7",
})
expected := &ThingWithDefaultValues{
Page: patch.Field[int]{Value: 7, Valid: true},
PerPage: patch.Field[int]{Value: 20, Valid: true},
StateList: patch.Field[[]string]{Value: []string{"pending", "in_progress", "failed"}, Valid: true},
}
core, err := New(ThingWithDefaultValues{})
assert.NoError(t, err)
got, err := core.Decode(r)
assert.NoError(t, err)
assert.Equal(t, expected, got)
}
2 changes: 1 addition & 1 deletion file.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import (
)

func init() {
registerTypeDecoderTo[File](builtinDecoders, AdaptDecoderFunc[File, *multipart.FileHeader](decodeFile), false)
registerTypeDecoderTo[File](builtinDecoders, DecoderFunc[*multipart.FileHeader](decodeFile), false)
}

type File struct {
Expand Down

0 comments on commit 9be6ca3

Please sign in to comment.