Skip to content

Commit

Permalink
feat: add variadic function support
Browse files Browse the repository at this point in the history
Signed-off-by: ghosind <[email protected]>
  • Loading branch information
ghosind committed Oct 29, 2024
1 parent cc8754e commit 958829e
Show file tree
Hide file tree
Showing 2 changed files with 112 additions and 4 deletions.
64 changes: 63 additions & 1 deletion async.go
Original file line number Diff line number Diff line change
Expand Up @@ -159,10 +159,72 @@ func makeFuncIn(ft reflect.Type, ctx context.Context, params []any) []reflect.Va
if !ft.IsVariadic() {
return makeNonVariadicFuncIn(ft, ctx, params, isTakeContext, isContextParam)
} else {
panic("variadic function unsupported")
return makeVariadicFuncIn(ft, ctx, params, isTakeContext, isContextParam)
}
}

// makeVariadicFuncIn checks the parameters of the variadic function and the params slice from the
// caller, and returns a reflect.Value slice of the input parameters. It'll prepend the context to
// the parameter list if the function's first parameter is a context and the first element in the
// parameter list is not a context.
func makeVariadicFuncIn(
ft reflect.Type,
ctx context.Context,
params []any,
isTakeContext, isContextParam bool,
) []reflect.Value {
ftNumIn := ft.NumIn() - 1
numIn := len(params)
if isTakeContext && !isContextParam {
ftNumIn--
numIn++
}
if len(params) < ftNumIn {
panic(ErrUnmatchedParam)
}
ftNumIn = ft.NumIn() - 1
lastType := ft.In(ftNumIn).Elem()

in := make([]reflect.Value, numIn)
i := 0
if isTakeContext && !isContextParam {
in[i] = reflect.ValueOf(ctx)
i++
}

for j := 0; i < ftNumIn || j < len(params); j++ {
v := params[j]
vt := reflect.TypeOf(v)
vv := reflect.ValueOf(v)
it := lastType
if i < ftNumIn {
it = ft.In(i)
}

if vt != it {
if vt != nil && vt.ConvertibleTo(it) {
vv = vv.Convert(it)
} else if v == nil {
kind := it.Kind()
switch kind {
case reflect.Chan, reflect.Map, reflect.Pointer, reflect.UnsafePointer,
reflect.Interface, reflect.Slice:
vv = reflect.Zero(it)
default:
panic(ErrUnmatchedParam)
}
} else {
panic(ErrUnmatchedParam)
}
}

in[i] = vv
i++
}

return in
}

// makeNonVariadicFuncIn checks the parameters of the non-variadic function and the params slice
// from the caller, and returns a reflect.Value slice of the input parameters. It'll prepend the
// context to the parameter list if the function's first parameter is a context and the first
Expand Down
52 changes: 49 additions & 3 deletions async_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -133,9 +133,9 @@ func TestInvokeAsyncFn(t *testing.T) {
a.NilNow(err)
a.EqualNow(ret, []any{})

a.PanicOfNow(func() {
invokeAsyncFn(func(ctx context.Context, vals ...int) {}, ctx, []any{nil})
}, "variadic function unsupported")
// a.PanicOfNow(func() {
// invokeAsyncFn(func(ctx context.Context, vals ...int) {}, ctx, []any{nil})
// }, "variadic function unsupported")

ret, err = invokeAsyncFn(func() int {
panic(expectErr)
Expand All @@ -144,6 +144,52 @@ func TestInvokeAsyncFn(t *testing.T) {
a.EqualNow(ret, []any{0})
}

func TestInvokeVariadicAsyncFn(t *testing.T) {
a := assert.New(t)
ctx := context.Background()

ret, err := invokeAsyncFn(func(vals ...int) {}, ctx, []any{})
a.NilNow(err)
a.EqualNow(ret, []any{})

ret, err = invokeAsyncFn(func(ctx context.Context, vals ...int) {}, ctx, []any{})
a.NilNow(err)
a.EqualNow(ret, []any{})

ret, err = invokeAsyncFn(func(vals ...int) {}, ctx, []any{1, 2, 3})
a.NilNow(err)
a.EqualNow(ret, []any{})

ret, err = invokeAsyncFn(func(ctx context.Context, vals ...int) {}, ctx, []any{ctx, 1, 2, 3})
a.NilNow(err)
a.EqualNow(ret, []any{})

ret, err = invokeAsyncFn(func(ctx context.Context, s string, vals ...int) {}, ctx, []any{"test", 1})
a.NilNow(err)
a.EqualNow(ret, []any{})

ret, err = invokeAsyncFn(func(vals ...int) int {
if len(vals) > 0 {
return vals[0]
}
return -1
}, ctx, []any{1, 2, 3})
a.NilNow(err)
a.EqualNow(ret, []any{1})

a.PanicNow(func() {
invokeAsyncFn(func(ctx context.Context, s string, vals ...int) {}, ctx, []any{})
})

a.PanicNow(func() {
invokeAsyncFn(func(ctx context.Context, s string, vals ...int) {}, ctx, []any{"a", "b"})
})

a.PanicNow(func() {
invokeAsyncFn(func(ctx context.Context, vals ...int) {}, ctx, []any{"a", "b"})
})
}

func TestInvokeAsyncFnWithParams(t *testing.T) {
a := assert.New(t)
ctx := context.Background()
Expand Down

0 comments on commit 958829e

Please sign in to comment.