diff --git a/huma.go b/huma.go index a5cafbb6..940ebcc6 100644 --- a/huma.go +++ b/huma.go @@ -97,15 +97,6 @@ func findParams(registry Registry, op *Operation, t reflect.Type) *findResult[*p return nil } - if f.Type.Kind() == reflect.Pointer { - // TODO: support pointers? The problem is that when we dynamically - // create an instance of the input struct the `params.Every(...)` - // call cannot set them as the value is `reflect.Invalid` unless - // dynamically allocated, but we don't know when to allocate until - // after the `Every` callback has run. Doable, but a bigger change. - panic("pointers are not supported for path/query/header parameters") - } - pfi := ¶mFieldInfo{ Type: f.Type, } @@ -146,6 +137,15 @@ func findParams(registry Registry, op *Operation, t reflect.Type) *findResult[*p return nil } + if f.Type.Kind() == reflect.Pointer { + // TODO: support pointers? The problem is that when we dynamically + // create an instance of the input struct the `params.Every(...)` + // call cannot set them as the value is `reflect.Invalid` unless + // dynamically allocated, but we don't know when to allocate until + // after the `Every` callback has run. Doable, but a bigger change. + panic("pointers are not supported for path/query/header parameters") + } + pfi.Schema = SchemaFromField(registry, f, "") var example any diff --git a/huma_test.go b/huma_test.go index 9c24812d..621aac4e 100644 --- a/huma_test.go +++ b/huma_test.go @@ -2059,6 +2059,38 @@ func TestResolverCompositionCalledOnce(t *testing.T) { assert.Equal(t, http.StatusNoContent, w.Code, w.Body.String()) } +type ResolverWithPointer struct { + Ptr *string +} + +func (r *ResolverWithPointer) Resolve(ctx huma.Context) []error { + r.Ptr = new(string) + *r.Ptr = "String" + return nil +} + +func TestResolverWithPointer(t *testing.T) { + // Allow using pointers in input structs if they are not path/query/header/cookie parameters + r, app := humatest.New(t, huma.DefaultConfig("Test API", "1.0.0")) + huma.Register(app, huma.Operation{ + OperationID: "test", + Method: http.MethodPut, + Path: "/test", + }, func(ctx context.Context, input *struct { + ResolverWithPointer + }) (*struct{}, error) { + // Exactly one call should have been made to the resolver. + assert.Equal(t, "String", *input.Ptr) + return nil, nil + }) + + req, _ := http.NewRequest(http.MethodPut, "/test", strings.NewReader(`{}`)) + req.Header.Set("Content-Type", "application/json") + w := httptest.NewRecorder() + r.ServeHTTP(w, req) + assert.Equal(t, http.StatusNoContent, w.Code, w.Body.String()) +} + func TestParamPointerPanics(t *testing.T) { // For now, we don't support these, so we panic rather than have subtle // bugs that are hard to track down.