package form import ( "net/http" "reflect" ) /* ValidateForm parses a POST form into a pre-defined struct */ func ValidateForm(r *http.Request, p any) (formErr error) { /* asserts */ switch { case r == nil: /* error on nil requestst */ return ENilRequest case r.Method != http.MethodPost: /* only support with POST methods */ return EInvalidMethod case p == nil: /* error on nil form */ return ENilForm } /* assert on form being a pointer */ fv := reflect.ValueOf(p) if fv.Kind() != reflect.Pointer || fv.IsNil() { return EInvalidForm } /* assert on form being a pointer to a struct */ fv = fv.Elem() if fv.Kind() != reflect.Struct { return EInvalidForm } /* parse form in the request */ if err := r.ParseForm(); err != nil { /* TODO: handle multipart forms / file uploads */ return err } /* walk target form fields */ for idx := 0; idx < fv.NumField(); idx++ { f := fv.Field(idx) /* ignore non-pointer form fields or nil fields */ if f.Kind() != reflect.Pointer || f.IsNil() { continue } /* ignore fields of type different from *FormField */ ff, ok := f.Interface().(*FormField) if !ok || ff == nil { continue } ff.Error = nil /* store value in *FormField */ if v, ok := r.PostForm[ff.Name]; ok { ff.Value = v[0] } else { ff.Value = "" } /* assert on required fields */ if ff.Required && ff.Value == "" { ff.Error = append(ff.Error, ERequiredField) formErr = EFormHasErrors continue } /* do not run validators on empty values */ if ff.Value == "" { continue } /* run field validators if any */ for _, fieldValidator := range ff.Validators { if fieldValidator == nil { continue } if err := fieldValidator(ff, r.Context()); err != nil { ff.Error = append(ff.Error, err) formErr = EFormHasErrors break } } } return }