Skip to content

Commit 1c3924e

Browse files
Merge pull request #705 from b-kamphorst/refactor-huma-register
refactor: split up huma.Register
2 parents fe78329 + 9d5259b commit 1c3924e

File tree

3 files changed

+929
-772
lines changed

3 files changed

+929
-772
lines changed

formdata.go

Lines changed: 66 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -85,30 +85,51 @@ func (v MimeTypeValidator) Validate(fh *multipart.FileHeader, location string) (
8585
}
8686
}
8787

88-
func (m *MultipartFormFiles[T]) readFile(
89-
fh *multipart.FileHeader,
90-
location string,
91-
validator MimeTypeValidator,
92-
) (FormFile, *ErrorDetail) {
93-
f, err := fh.Open()
94-
if err != nil {
95-
return FormFile{}, &ErrorDetail{Message: "Failed to open file", Location: location}
96-
}
97-
contentType, validationErr := validator.Validate(fh, location)
98-
if validationErr != nil {
99-
return FormFile{}, validationErr
88+
func (m *MultipartFormFiles[T]) Data() *T {
89+
return m.data
90+
}
91+
92+
// Decodes multipart.Form data into *T, returning []*ErrorDetail if any
93+
// Schema is used to check for validation constraints
94+
func (m *MultipartFormFiles[T]) Decode(opMediaType *MediaType) []error {
95+
var (
96+
dataType = reflect.TypeOf(m.data).Elem()
97+
value = reflect.New(dataType)
98+
errors []error
99+
)
100+
for i := 0; i < dataType.NumField(); i++ {
101+
field := value.Elem().Field(i)
102+
structField := dataType.Field(i)
103+
key := structField.Tag.Get("form")
104+
if key == "" {
105+
key = structField.Name
106+
}
107+
fileHeaders := m.Form.File[key]
108+
switch {
109+
case field.Type() == reflect.TypeOf(FormFile{}):
110+
file, err := readSingleFile(fileHeaders, key, opMediaType)
111+
if err != nil {
112+
errors = append(errors, err)
113+
continue
114+
}
115+
field.Set(reflect.ValueOf(file))
116+
case field.Type() == reflect.TypeOf([]FormFile{}):
117+
files, errs := readMultipleFiles(fileHeaders, key, opMediaType)
118+
if errs != nil {
119+
errors = append(errors, errs...)
120+
continue
121+
}
122+
field.Set(reflect.ValueOf(files))
123+
124+
default:
125+
continue
126+
}
100127
}
101-
return FormFile{
102-
File: f,
103-
ContentType: contentType,
104-
IsSet: true,
105-
Size: fh.Size,
106-
Filename: fh.Filename,
107-
}, nil
128+
m.data = value.Interface().(*T)
129+
return errors
108130
}
109131

110-
func (m *MultipartFormFiles[T]) readSingleFile(key string, opMediaType *MediaType) (FormFile, *ErrorDetail) {
111-
fileHeaders := m.Form.File[key]
132+
func readSingleFile(fileHeaders []*multipart.FileHeader, key string, opMediaType *MediaType) (FormFile, *ErrorDetail) {
112133
if len(fileHeaders) == 0 {
113134
if opMediaType.Schema.requiredMap[key] {
114135
return FormFile{}, &ErrorDetail{Message: "File required", Location: key}
@@ -117,16 +138,15 @@ func (m *MultipartFormFiles[T]) readSingleFile(key string, opMediaType *MediaTyp
117138
}
118139
} else if len(fileHeaders) == 1 {
119140
validator := NewMimeTypeValidator(opMediaType.Encoding[key])
120-
return m.readFile(fileHeaders[0], key, validator)
141+
return readFile(fileHeaders[0], key, validator)
121142
}
122143
return FormFile{}, &ErrorDetail{
123144
Message: "Multiple files received but only one was expected",
124145
Location: key,
125146
}
126147
}
127148

128-
func (m *MultipartFormFiles[T]) readMultipleFiles(key string, opMediaType *MediaType) ([]FormFile, []error) {
129-
fileHeaders := m.Form.File[key]
149+
func readMultipleFiles(fileHeaders []*multipart.FileHeader, key string, opMediaType *MediaType) ([]FormFile, []error) {
130150
var (
131151
files = make([]FormFile, len(fileHeaders))
132152
errors []error
@@ -136,7 +156,7 @@ func (m *MultipartFormFiles[T]) readMultipleFiles(key string, opMediaType *Media
136156
}
137157
validator := NewMimeTypeValidator(opMediaType.Encoding[key])
138158
for i, fh := range fileHeaders {
139-
file, err := m.readFile(
159+
file, err := readFile(
140160
fh,
141161
fmt.Sprintf("%s[%d]", key, i),
142162
validator,
@@ -150,47 +170,26 @@ func (m *MultipartFormFiles[T]) readMultipleFiles(key string, opMediaType *Media
150170
return files, errors
151171
}
152172

153-
func (m *MultipartFormFiles[T]) Data() *T {
154-
return m.data
155-
}
156-
157-
// Decodes multipart.Form data into *T, returning []*ErrorDetail if any
158-
// Schema is used to check for validation constraints
159-
func (m *MultipartFormFiles[T]) Decode(opMediaType *MediaType) []error {
160-
var (
161-
dataType = reflect.TypeOf(m.data).Elem()
162-
value = reflect.New(dataType)
163-
errors []error
164-
)
165-
for i := 0; i < dataType.NumField(); i++ {
166-
field := value.Elem().Field(i)
167-
structField := dataType.Field(i)
168-
key := structField.Tag.Get("form")
169-
if key == "" {
170-
key = structField.Name
171-
}
172-
switch {
173-
case field.Type() == reflect.TypeOf(FormFile{}):
174-
file, err := m.readSingleFile(key, opMediaType)
175-
if err != nil {
176-
errors = append(errors, err)
177-
continue
178-
}
179-
field.Set(reflect.ValueOf(file))
180-
case field.Type() == reflect.TypeOf([]FormFile{}):
181-
files, errs := m.readMultipleFiles(key, opMediaType)
182-
if errs != nil {
183-
errors = append(errors, errs...)
184-
continue
185-
}
186-
field.Set(reflect.ValueOf(files))
187-
188-
default:
189-
continue
190-
}
173+
func readFile(
174+
fh *multipart.FileHeader,
175+
location string,
176+
validator MimeTypeValidator,
177+
) (FormFile, *ErrorDetail) {
178+
f, err := fh.Open()
179+
if err != nil {
180+
return FormFile{}, &ErrorDetail{Message: "Failed to open file", Location: location}
191181
}
192-
m.data = value.Interface().(*T)
193-
return errors
182+
contentType, validationErr := validator.Validate(fh, location)
183+
if validationErr != nil {
184+
return FormFile{}, validationErr
185+
}
186+
return FormFile{
187+
File: f,
188+
ContentType: contentType,
189+
IsSet: true,
190+
Size: fh.Size,
191+
Filename: fh.Filename,
192+
}, nil
194193
}
195194

196195
func formDataFieldName(f reflect.StructField) string {
@@ -208,7 +207,7 @@ func multiPartFormFileSchema(t reflect.Type) *Schema {
208207
Properties: make(map[string]*Schema, nFields),
209208
requiredMap: make(map[string]bool, nFields),
210209
}
211-
requiredFields := make([]string, nFields)
210+
requiredFields := make([]string, 0, nFields)
212211
for i := 0; i < nFields; i++ {
213212
f := t.Field(i)
214213
name := formDataFieldName(f)
@@ -227,7 +226,7 @@ func multiPartFormFileSchema(t reflect.Type) *Schema {
227226
}
228227

229228
if _, ok := f.Tag.Lookup("required"); ok && boolTag(f, "required", false) {
230-
requiredFields[i] = name
229+
requiredFields = append(requiredFields, name)
231230
schema.requiredMap[name] = true
232231
}
233232
}

0 commit comments

Comments
 (0)