Skip to content

Commit 958829e

Browse files
committed
feat: add variadic function support
Signed-off-by: ghosind <ghosind@gmail.com>
1 parent cc8754e commit 958829e

File tree

2 files changed

+112
-4
lines changed

2 files changed

+112
-4
lines changed

async.go

Lines changed: 63 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,10 +159,72 @@ func makeFuncIn(ft reflect.Type, ctx context.Context, params []any) []reflect.Va
159159
if !ft.IsVariadic() {
160160
return makeNonVariadicFuncIn(ft, ctx, params, isTakeContext, isContextParam)
161161
} else {
162-
panic("variadic function unsupported")
162+
return makeVariadicFuncIn(ft, ctx, params, isTakeContext, isContextParam)
163163
}
164164
}
165165

166+
// makeVariadicFuncIn checks the parameters of the variadic function and the params slice from the
167+
// caller, and returns a reflect.Value slice of the input parameters. It'll prepend the context to
168+
// the parameter list if the function's first parameter is a context and the first element in the
169+
// parameter list is not a context.
170+
func makeVariadicFuncIn(
171+
ft reflect.Type,
172+
ctx context.Context,
173+
params []any,
174+
isTakeContext, isContextParam bool,
175+
) []reflect.Value {
176+
ftNumIn := ft.NumIn() - 1
177+
numIn := len(params)
178+
if isTakeContext && !isContextParam {
179+
ftNumIn--
180+
numIn++
181+
}
182+
if len(params) < ftNumIn {
183+
panic(ErrUnmatchedParam)
184+
}
185+
ftNumIn = ft.NumIn() - 1
186+
lastType := ft.In(ftNumIn).Elem()
187+
188+
in := make([]reflect.Value, numIn)
189+
i := 0
190+
if isTakeContext && !isContextParam {
191+
in[i] = reflect.ValueOf(ctx)
192+
i++
193+
}
194+
195+
for j := 0; i < ftNumIn || j < len(params); j++ {
196+
v := params[j]
197+
vt := reflect.TypeOf(v)
198+
vv := reflect.ValueOf(v)
199+
it := lastType
200+
if i < ftNumIn {
201+
it = ft.In(i)
202+
}
203+
204+
if vt != it {
205+
if vt != nil && vt.ConvertibleTo(it) {
206+
vv = vv.Convert(it)
207+
} else if v == nil {
208+
kind := it.Kind()
209+
switch kind {
210+
case reflect.Chan, reflect.Map, reflect.Pointer, reflect.UnsafePointer,
211+
reflect.Interface, reflect.Slice:
212+
vv = reflect.Zero(it)
213+
default:
214+
panic(ErrUnmatchedParam)
215+
}
216+
} else {
217+
panic(ErrUnmatchedParam)
218+
}
219+
}
220+
221+
in[i] = vv
222+
i++
223+
}
224+
225+
return in
226+
}
227+
166228
// makeNonVariadicFuncIn checks the parameters of the non-variadic function and the params slice
167229
// from the caller, and returns a reflect.Value slice of the input parameters. It'll prepend the
168230
// context to the parameter list if the function's first parameter is a context and the first

async_test.go

Lines changed: 49 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -133,9 +133,9 @@ func TestInvokeAsyncFn(t *testing.T) {
133133
a.NilNow(err)
134134
a.EqualNow(ret, []any{})
135135

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

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

147+
func TestInvokeVariadicAsyncFn(t *testing.T) {
148+
a := assert.New(t)
149+
ctx := context.Background()
150+
151+
ret, err := invokeAsyncFn(func(vals ...int) {}, ctx, []any{})
152+
a.NilNow(err)
153+
a.EqualNow(ret, []any{})
154+
155+
ret, err = invokeAsyncFn(func(ctx context.Context, vals ...int) {}, ctx, []any{})
156+
a.NilNow(err)
157+
a.EqualNow(ret, []any{})
158+
159+
ret, err = invokeAsyncFn(func(vals ...int) {}, ctx, []any{1, 2, 3})
160+
a.NilNow(err)
161+
a.EqualNow(ret, []any{})
162+
163+
ret, err = invokeAsyncFn(func(ctx context.Context, vals ...int) {}, ctx, []any{ctx, 1, 2, 3})
164+
a.NilNow(err)
165+
a.EqualNow(ret, []any{})
166+
167+
ret, err = invokeAsyncFn(func(ctx context.Context, s string, vals ...int) {}, ctx, []any{"test", 1})
168+
a.NilNow(err)
169+
a.EqualNow(ret, []any{})
170+
171+
ret, err = invokeAsyncFn(func(vals ...int) int {
172+
if len(vals) > 0 {
173+
return vals[0]
174+
}
175+
return -1
176+
}, ctx, []any{1, 2, 3})
177+
a.NilNow(err)
178+
a.EqualNow(ret, []any{1})
179+
180+
a.PanicNow(func() {
181+
invokeAsyncFn(func(ctx context.Context, s string, vals ...int) {}, ctx, []any{})
182+
})
183+
184+
a.PanicNow(func() {
185+
invokeAsyncFn(func(ctx context.Context, s string, vals ...int) {}, ctx, []any{"a", "b"})
186+
})
187+
188+
a.PanicNow(func() {
189+
invokeAsyncFn(func(ctx context.Context, vals ...int) {}, ctx, []any{"a", "b"})
190+
})
191+
}
192+
147193
func TestInvokeAsyncFnWithParams(t *testing.T) {
148194
a := assert.New(t)
149195
ctx := context.Background()

0 commit comments

Comments
 (0)