|
5 | 5 | "fmt" |
6 | 6 | "net/url" |
7 | 7 | "reflect" |
8 | | - "time" |
9 | 8 | ) |
10 | 9 |
|
11 | 10 | var ( |
@@ -38,49 +37,60 @@ func (e *ErrInvalidParameterValue) Unwrap() error { |
38 | 37 | return e.Err |
39 | 38 | } |
40 | 39 |
|
| 40 | +// ErrCannotSetValue is an error adds extra context to a setter error. |
| 41 | +type ErrCannotSetValue struct { |
| 42 | + Err error |
| 43 | + Parameter string |
| 44 | + Field string |
| 45 | + Value string |
| 46 | + Type reflect.Type |
| 47 | + ParsedValue reflect.Value |
| 48 | +} |
| 49 | + |
| 50 | +// Error returns the full error message. |
| 51 | +func (e *ErrCannotSetValue) Error() string { |
| 52 | + return fmt.Sprintf("cannot set value for field %s (%s) from parameter %s (%s - %v): %s", e.Field, e.Type, e.Parameter, e.Value, e.ParsedValue, e.Err.Error()) |
| 53 | +} |
| 54 | + |
| 55 | +// Unwrap returns the wrapped error. |
| 56 | +func (e *ErrCannotSetValue) Unwrap() error { |
| 57 | + return e.Err |
| 58 | +} |
| 59 | + |
41 | 60 | // Present allows you to determine whether or not a query parameter was present in a request. |
42 | 61 | type Present bool |
43 | 62 |
|
44 | 63 | // DefaultParser is a default parser. |
45 | 64 | var DefaultParser = &Parser{ |
46 | | - // Tag is the name of the struct tag where the query parameter name is set. |
47 | | - Tag: "queryparam", |
48 | | - // Delimiter is the name of the struct tag where a string delimiter override is set. |
| 65 | + Tag: "queryparam", |
49 | 66 | DelimiterTag: "queryparamdelim", |
50 | | - // Delimiter is the default string delimiter. |
51 | | - Delimiter: ",", |
52 | | - // ValueParsers is a map[reflect.Type]ValueParser that defines how we parse query |
53 | | - // parameters based on the destination variable type. |
| 67 | + Delimiter: ",", |
54 | 68 | ValueParsers: DefaultValueParsers(), |
55 | | -} |
56 | | - |
57 | | -// DefaultValueParsers returns a set of default value parsers. |
58 | | -func DefaultValueParsers() map[reflect.Type]ValueParser { |
59 | | - return map[reflect.Type]ValueParser{ |
60 | | - reflect.TypeOf(""): StringValueParser, |
61 | | - reflect.TypeOf([]string{}): StringSliceValueParser, |
62 | | - reflect.TypeOf(0): IntValueParser, |
63 | | - reflect.TypeOf(int32(0)): Int32ValueParser, |
64 | | - reflect.TypeOf(int64(0)): Int64ValueParser, |
65 | | - reflect.TypeOf(float32(0)): Float32ValueParser, |
66 | | - reflect.TypeOf(float64(0)): Float64ValueParser, |
67 | | - reflect.TypeOf(time.Time{}): TimeValueParser, |
68 | | - reflect.TypeOf(false): BoolValueParser, |
69 | | - reflect.TypeOf(Present(false)): PresentValueParser, |
70 | | - } |
| 69 | + ValueSetters: DefaultValueSetters(), |
71 | 70 | } |
72 | 71 |
|
73 | 72 | // Parser is used to parse a URL. |
74 | 73 | type Parser struct { |
75 | | - Tag string |
| 74 | + // Tag is the name of the struct tag where the query parameter name is set. |
| 75 | + Tag string |
| 76 | + // Delimiter is the name of the struct tag where a string delimiter override is set. |
76 | 77 | DelimiterTag string |
77 | | - Delimiter string |
| 78 | + // Delimiter is the default string delimiter. |
| 79 | + Delimiter string |
| 80 | + // ValueParsers is a map[reflect.Type]ValueParser that defines how we parse query |
| 81 | + // parameters based on the destination variable type. |
78 | 82 | ValueParsers map[reflect.Type]ValueParser |
| 83 | + // ValueSetters is a map[reflect.Type]ValueSetter that defines how we set values |
| 84 | + // onto target variables. |
| 85 | + ValueSetters map[reflect.Type]ValueSetter |
79 | 86 | } |
80 | 87 |
|
81 | 88 | // ValueParser is a func used to parse a value. |
82 | 89 | type ValueParser func(value string, delimiter string) (reflect.Value, error) |
83 | 90 |
|
| 91 | +// ValueSetter is a func used to set a value on a target variable. |
| 92 | +type ValueSetter func(value reflect.Value, target reflect.Value) error |
| 93 | + |
84 | 94 | // FieldDelimiter returns a delimiter to be used with the given field. |
85 | 95 | func (p *Parser) FieldDelimiter(field reflect.StructField) string { |
86 | 96 | if customDelimiter := field.Tag.Get(p.DelimiterTag); customDelimiter != "" { |
@@ -129,24 +139,39 @@ func (p *Parser) ParseField(field reflect.StructField, value reflect.Value, urlV |
129 | 139 |
|
130 | 140 | parsedValue, err := valueParser(queryParameterValue, p.FieldDelimiter(field)) |
131 | 141 | if err != nil { |
132 | | - err = &ErrInvalidParameterValue{ |
| 142 | + return &ErrInvalidParameterValue{ |
133 | 143 | Err: err, |
134 | 144 | Value: queryParameterValue, |
135 | 145 | Parameter: queryParameterName, |
136 | 146 | Type: field.Type, |
137 | 147 | Field: field.Name, |
138 | 148 | } |
139 | | - return err |
140 | 149 | } |
141 | 150 |
|
142 | | - // handle edge case value types |
143 | | - switch field.Type { |
144 | | - case reflect.TypeOf(int32(0)): |
145 | | - value.SetInt(parsedValue.Int()) |
146 | | - case reflect.TypeOf(float32(0)): |
147 | | - value.SetFloat(parsedValue.Float()) |
148 | | - default: |
149 | | - value.Set(parsedValue) |
| 151 | + valueSetter, ok := p.ValueSetters[field.Type] |
| 152 | + if !ok { |
| 153 | + valueSetter, ok = p.ValueSetters[GenericType] |
| 154 | + } |
| 155 | + if !ok { |
| 156 | + return &ErrCannotSetValue{ |
| 157 | + Err: ErrUnhandledFieldType, |
| 158 | + Value: queryParameterValue, |
| 159 | + ParsedValue: parsedValue, |
| 160 | + Parameter: queryParameterName, |
| 161 | + Type: field.Type, |
| 162 | + Field: field.Name, |
| 163 | + } |
| 164 | + } |
| 165 | + |
| 166 | + if err := valueSetter(parsedValue, value); err != nil { |
| 167 | + return &ErrCannotSetValue{ |
| 168 | + Err: err, |
| 169 | + Value: queryParameterValue, |
| 170 | + ParsedValue: parsedValue, |
| 171 | + Parameter: queryParameterName, |
| 172 | + Type: field.Type, |
| 173 | + Field: field.Name, |
| 174 | + } |
150 | 175 | } |
151 | 176 |
|
152 | 177 | return nil |
|
0 commit comments