-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdecode_struct.go
More file actions
129 lines (110 loc) · 3.89 KB
/
Copy pathdecode_struct.go
File metadata and controls
129 lines (110 loc) · 3.89 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
package instruct
import (
"fmt"
"reflect"
"github.com/rrgmc/instruct/types"
)
// decodeStruct uses the structInfo to decode the input to the struct.
func (d *Decoder[IT, DC]) decodeStruct(si *structInfo, input IT, dataValue reflect.Value, decodeOptions DecodeOptions[IT, DC]) error {
reflectEnsurePointerValue(&dataValue)
dataValue = reflectValueElem(dataValue)
if err := si.checkSameType(dataValue.Type()); err != nil {
return err
}
// execute the struct operation (using StructOption or inner struct tags). Only executed if "when" is
// configured as "before".
err := d.executeStructOperation(SOOptionWhenBefore, dataValue, si, input, decodeOptions)
if err != nil {
return err
}
for _, sifield := range si.fields {
fieldValue := dataValue.FieldByIndex(sifield.field.Index)
dataWasSet := false
switch sifield.tag.Operation {
case OperationIgnore: // ignore
dataWasSet = true
case OperationRecurse:
// recurse into inner struct
if err := d.decodeStruct(sifield, input, fieldValue, decodeOptions); err != nil {
return err
}
dataWasSet = true
default:
var err error
// execute operation (query, header, etc.)
dataWasSet, err = d.executeOperation(fieldValue, sifield, input, decodeOptions)
if err != nil {
return err
}
}
if !dataWasSet && sifield.tag.Required {
return types.RequiredError{
Operation: sifield.tag.Operation,
FieldName: sifield.fullFieldName(),
TagName: sifield.tag.Name,
}
}
}
// execute the struct operation (using StructOption or inner struct tags). Only executed if "when" is
// configured as "after".
err = d.executeStructOperation(SOOptionWhenAfter, dataValue, si, input, decodeOptions)
if err != nil {
return err
}
return nil
}
// executeStructOperation execute the struct operation (using StructOption or inner struct tags).
func (d *Decoder[IT, DC]) executeStructOperation(when string, dataValue reflect.Value, si *structInfo,
input IT, decodeOptions DecodeOptions[IT, DC]) error {
if si.tag == nil || !si.tag.IsSO || soOptionValue(si.tag.SOWhen) != when {
return nil
}
dataWasSet, err := d.executeOperation(dataValue, si, input, decodeOptions)
if err != nil {
return err
}
if !dataWasSet && si.tag.Required {
fn := si.fullFieldName()
if fn == "" {
fn = si.typ.String()
}
return types.RequiredError{
IsStructOption: true,
Operation: si.tag.Operation,
FieldName: structFieldName(si.typ, si.fullFieldName()),
TagName: si.tag.Name,
}
}
return nil
}
// executeOperation executes an operation (query, header, etc) on a struct field.
// If the decode interface return IgnoreDecodeValue, the value is not set to it.
func (d *Decoder[IT, DC]) executeOperation(field reflect.Value, sifield *structInfo, input IT,
decodeOptions DecodeOptions[IT, DC]) (bool, error) {
// check if the operation exists
operation, opok := d.options.DecodeOperations[sifield.tag.Operation]
if !opok {
return false, fmt.Errorf("unknown operation '%s' for field %s", sifield.tag.Operation, sifield.field.Name)
}
// only check slices/arrays for primitive types, otherwise "type UUID [16]byte" would be checked as an array
isPrimitive := field.Type().PkgPath() == ""
isList := isPrimitive && (field.Kind() == reflect.Slice || field.Kind() == reflect.Array)
// call the decoder interface.
dataWasSet, value, err := operation.Decode(decodeOptions.Ctx, input, isList, field, sifield.tag)
if err != nil {
return false, err
}
if dataWasSet && value != IgnoreDecodeValue {
if sifield.field.Type == nil {
// struct option can't be set as a value
return false, types.OperationNotSupportedError{
Operation: sifield.tag.Operation,
FieldName: structFieldName(sifield.typ, sifield.fullFieldName()),
}
}
if err = d.options.Resolver.Resolve(field, value); err != nil {
return false, fmt.Errorf("error resolving field '%s': %w", sifield.fullFieldName(), err)
}
}
return dataWasSet, nil
}