form - form.go
1 // Package form provides an easy to use way to parse form values from an HTTP
2 // request into a struct
3 package form // import "vimagination.zapto.org/form"
4
5 import (
6 "net/http"
7 "reflect"
8 "strings"
9 "sync"
10 )
11
12 var interType = reflect.TypeOf((*formParser)(nil)).Elem()
13
14 type processorDetails struct {
15 processor
16 Post, Required bool
17 Index []int
18 }
19
20 type typeMap map[string]processorDetails
21
22 var (
23 tmMu sync.RWMutex
24 typeMaps = make(map[reflect.Type]typeMap)
25 )
26
27 func getTypeMap(t reflect.Type) typeMap {
28 tmMu.RLock()
29 tm, ok := typeMaps[t]
30 tmMu.RUnlock()
31 if ok {
32 return tm
33 }
34 tmMu.Lock()
35 tm = createTypeMap(t)
36 tmMu.Unlock()
37 return tm
38 }
39
40 func basicTypeProcessor(t reflect.Type, tag reflect.StructTag) processor {
41 switch t.Kind() {
42 case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int:
43 return newInum(tag, t.Bits())
44 case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint:
45 return newUnum(tag, t.Bits())
46 case reflect.Float32, reflect.Float64:
47 return newFloat(tag, t.Bits())
48 case reflect.String:
49 return newString(tag)
50 case reflect.Bool:
51 return boolean{}
52 }
53 return nil
54 }
55
56 func createTypeMap(t reflect.Type) typeMap {
57 tm, ok := typeMaps[t]
58 if ok {
59 return tm
60 }
61 tm = make(typeMap)
62 for i := 0; i < t.NumField(); i++ {
63 f := t.Field(i)
64 if f.PkgPath != "" {
65 continue
66 }
67 name := f.Name
68 var required, post bool
69 if n := f.Tag.Get("form"); n == "-" {
70 continue
71 } else if n != "" {
72 p := strings.IndexByte(n, ',')
73 if p >= 0 {
74 if p > 0 {
75 name = n[:p]
76 }
77 rest := n[p:]
78 required = strings.Contains(rest, ",required,") || strings.HasSuffix(rest, ",required")
79 post = strings.Contains(rest, ",post,") || strings.HasSuffix(rest, ",post")
80 } else {
81 name = n
82 }
83 }
84 var p processor
85 if f.Type.Implements(interType) {
86 p = inter(false)
87 } else if reflect.PtrTo(f.Type).Implements(interType) {
88 p = inter(true)
89 } else if k := f.Type.Kind(); k == reflect.Slice || k == reflect.Ptr {
90 et := f.Type.Elem()
91 s := basicTypeProcessor(et, f.Tag)
92 if s == nil {
93 continue
94 }
95 if k == reflect.Slice {
96 p = slice{
97 processor: s,
98 typ: reflect.SliceOf(et),
99 }
100 } else {
101 p = pointer{
102 processor: s,
103 typ: et,
104 }
105 }
106 } else if k == reflect.Struct && f.Anonymous {
107 for n, p := range createTypeMap(f.Type) {
108 if _, ok := tm[n]; !ok {
109 tm[n] = processorDetails{
110 processor: p.processor,
111 Required: p.Required,
112 Post: p.Post,
113 Index: append(append(make([]int, 0, len(p.Index)+1), i), p.Index...),
114 }
115 }
116 }
117 continue
118 } else {
119 p = basicTypeProcessor(f.Type, f.Tag)
120 if p == nil {
121 continue
122 }
123 }
124 tm[name] = processorDetails{
125 processor: p,
126 Required: required,
127 Post: post,
128 Index: []int{i},
129 }
130 }
131 typeMaps[t] = tm
132 return tm
133 }
134
135 // Process parses the form data from the request into the passed value, which
136 // must be a pointer to a struct.
137 //
138 // Form keys are assumed to be the field names unless a 'form' tag is provided
139 // with an alternate name, for example, in the following struct, the int is
140 // parse with key 'A' and the bool is parsed with key 'C'.
141 //
142 // type Example struct {
143 // A int
144 // B bool `form:"C"`
145 // }
146 //
147 // Two options can be added to the form tag to modify the processing. The
148 // 'post' option forces the processer to parse a value from the PostForm field
149 // of the Request, and the 'required' option will have an error thrown if the
150 // key in not set.
151 //
152 // Number types can also have minimums and maximums checked during processing
153 // by setting the 'min' and 'max' tags accordingly.
154 //
155 // In a similar vein, string types can utilise the 'regex' tag to set a
156 // regular expression to be matched against.
157 //
158 // Anonymous structs are traversed, but will not override more local fields.
159 //
160 // Slices of basic types can be processed, and errors returned from any such
161 // processing will be of the Errors type, which each indexed entry
162 // corresponding to the index of the processed data.
163 //
164 // Pointers to basic types can also be processed, with the type being allocated
165 // even if an error occurs.
166 //
167 // Lastly, a custom data processor can be specified by attaching a method to
168 // the field type with the following specification:
169 //
170 // ParseForm([]string) error
171 func Process(r *http.Request, fv interface{}) error {
172 v := reflect.ValueOf(fv)
173 if v.Kind() != reflect.Ptr {
174 return ErrNeedPointer
175 }
176 for v.Kind() == reflect.Ptr {
177 v = v.Elem()
178 }
179 if v.Kind() != reflect.Struct {
180 return ErrNeedStruct
181 }
182 tm := getTypeMap(v.Type())
183 if err := r.ParseForm(); err != nil {
184 return err
185 }
186 var errors ErrorMap
187 for key, pd := range tm {
188 var (
189 val []string
190 ok bool
191 )
192 if pd.Post {
193 val, ok = r.PostForm[key]
194 } else {
195 val, ok = r.Form[key]
196 }
197 if ok {
198 if err := pd.processor.process(v.FieldByIndex(pd.Index), val); err != nil {
199 if errors == nil {
200 errors = make(ErrorMap)
201 }
202 errors[key] = err
203 }
204 } else if pd.Required {
205 if errors == nil {
206 errors = make(ErrorMap)
207 }
208 errors[key] = ErrRequiredMissing
209 }
210 }
211 if len(errors) > 0 {
212 return errors
213 }
214 return nil
215 }