132 lines
2.2 KiB
Go

package form
import (
"mime/multipart"
"net/http"
"net/url"
"github.com/pkg/errors"
)
type ValidationErrors map[string]error
type Form struct {
fields []*Field
errors ValidationErrors
}
func (f *Form) Handle(r *http.Request) error {
if err := r.ParseForm(); err != nil {
return errors.WithStack(err)
}
if err := f.Parse(r.Form); err != nil {
return errors.WithStack(err)
}
return nil
}
func (f *Form) HandleMultipart(r *http.Request, maxMemory int64) error {
if err := r.ParseMultipartForm(maxMemory); err != nil {
return errors.WithStack(err)
}
if err := f.ParseMultipart(r.MultipartForm); err != nil {
return errors.WithStack(err)
}
return nil
}
func (f *Form) ParseMultipart(multi *multipart.Form) error {
for _, f := range f.fields {
name := f.Name()
values, exists := multi.Value[name]
if !exists {
f.Del("value")
} else {
f.Set("value", values)
}
files, exists := multi.File[name]
if !exists {
f.Del("file")
} else {
f.Set("file", files)
}
}
return nil
}
func (f *Form) Parse(values url.Values) error {
for _, f := range f.fields {
name := f.Name()
if !values.Has(name) {
f.Del("value")
continue
}
value := values.Get(name)
f.Set("value", value)
}
return nil
}
func (f *Form) Validate() ValidationErrors {
var errs ValidationErrors
for _, f := range f.fields {
if err := f.Validate(); err != nil {
if errs == nil {
errs = make(ValidationErrors)
}
errs[f.Name()] = err
}
}
f.errors = errs
return errs
}
func (f *Form) Field(name string) *Field {
for _, field := range f.fields {
if field.Name() == name {
return field
}
}
panic(errors.Errorf("no form field named '%s'", name))
}
func (f *Form) Error(name string) (ValidationError, bool) {
err, exists := f.errors[name]
if !exists {
return nil, false
}
var validationErr ValidationError
if errors.As(err, &validationErr) {
return validationErr, true
}
return NewValidationError("Unexpected error"), true
}
func (f *Form) Extend(fields ...*Field) *Form {
fields = append(f.fields, fields...)
return New(fields...)
}
func New(fields ...*Field) *Form {
return &Form{
fields: fields,
errors: make(ValidationErrors),
}
}