-
Notifications
You must be signed in to change notification settings - Fork 16
/
inflate.go
154 lines (137 loc) · 3.91 KB
/
inflate.go
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
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
package restructure
import (
"fmt"
"reflect"
"strconv"
)
var (
posType = reflect.TypeOf(Pos(0))
emptyType = reflect.TypeOf(struct{}{})
stringType = reflect.TypeOf("")
intType = reflect.TypeOf(1)
byteSliceType = reflect.TypeOf([]byte{})
submatchType = reflect.TypeOf(Submatch{})
scalarTypes = []reflect.Type{
emptyType,
stringType,
intType,
byteSliceType,
submatchType,
}
)
// determines whether t is a scalar type or a pointer to a scalar type
func isScalar(t reflect.Type) bool {
if t.Kind() == reflect.Ptr {
t = t.Elem()
}
for _, u := range scalarTypes {
if t == u {
return true
}
}
return false
}
// determines whether t is a struct type or a pointer to a struct type
func isStruct(t reflect.Type) bool {
if t.Kind() == reflect.Ptr {
t = t.Elem()
}
return t.Kind() == reflect.Struct
}
// ensureAlloc replaces nil pointers with newly allocated objects
func ensureAlloc(dest reflect.Value) reflect.Value {
if dest.Kind() == reflect.Ptr {
if dest.IsNil() {
dest.Set(reflect.New(dest.Type().Elem()))
}
return dest.Elem()
}
return dest
}
// inflate the results of a match into a string
func inflateScalar(dest reflect.Value, match *match, captureIndex int, role Role) error {
if captureIndex == -1 {
// This means the field generated a regex but we did not want the results
return nil
}
// Get the subcapture for this field
subcapture := match.captures[captureIndex]
if !subcapture.wasMatched() {
// This means the subcapture was optional and was not matched
return nil
}
// Get the matched bytes
buf := match.input[subcapture.begin:subcapture.end]
// If dest is a nil pointer then allocate a new instance and assign the pointer to dest
dest = ensureAlloc(dest)
// Deal with each recognized type
switch role {
case StringScalarRole:
dest.SetString(string(buf))
return nil
case IntScalarRole:
if intVal, err := strconv.Atoi(string(buf)); err != nil {
return fmt.Errorf("unable to capture into %s", dest.Type().String())
} else {
dest.SetInt(int64(intVal))
return nil
}
case ByteSliceScalarRole:
dest.SetBytes(buf)
return nil
case SubmatchScalarRole:
submatch := dest.Addr().Interface().(*Submatch)
submatch.Begin = Pos(subcapture.begin)
submatch.End = Pos(subcapture.end)
submatch.Bytes = buf
return nil
}
return fmt.Errorf("unable to capture into %s", dest.Type().String())
}
// inflate the position of a match into a Pos
func inflatePos(dest reflect.Value, match *match, captureIndex int) error {
if captureIndex == -1 {
// This means the field generated a regex but we did not want the results
return nil
}
// Get the subcapture for this field
subcapture := match.captures[captureIndex]
if !subcapture.wasMatched() {
// This means the subcapture was optional and was not matched
return nil
}
// If dest is a nil pointer then allocate a new instance and assign the pointer to dest
dest.SetInt(int64(subcapture.begin))
return nil
}
// inflate the results of a match into a struct
func inflateStruct(dest reflect.Value, match *match, structure *Struct) error {
// Get the subcapture for this field
subcapture := match.captures[structure.capture]
if !subcapture.wasMatched() {
return nil
}
// If the field is a nil pointer then allocate an instance and assign pointer to dest
dest = ensureAlloc(dest)
// Inflate values into the struct fields
for _, field := range structure.fields {
switch field.role {
case PosRole:
val := dest.FieldByIndex(field.index)
if err := inflatePos(val, match, field.capture); err != nil {
return err
}
case StringScalarRole, ByteSliceScalarRole, SubmatchScalarRole, IntScalarRole:
val := dest.FieldByIndex(field.index)
if err := inflateScalar(val, match, field.capture, field.role); err != nil {
return err
}
case SubstructRole:
val := dest.FieldByIndex(field.index)
if err := inflateStruct(val, match, field.child); err != nil {
return err
}
}
}
return nil
}