forked from go-restruct/restruct
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathtag.go
99 lines (90 loc) · 2.31 KB
/
tag.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
package restruct
import (
"encoding/binary"
"errors"
"reflect"
"strconv"
"strings"
)
// tagOptions represents a parsed struct tag.
type tagOptions struct {
Ignore bool
Type reflect.Type
SizeOf string
Skip int
Order binary.ByteOrder
BitSize uint8 // size in bits - only for int types
}
// mustParseTag calls ParseTag but panics if there is an error, to help make
// sure programming errors surface quickly.
func mustParseTag(tag string) tagOptions {
opt, err := parseTag(tag)
if err != nil {
panic(err)
}
return opt
}
// parseTag parses a struct tag into a TagOptions structure.
func parseTag(tag string) (tagOptions, error) {
parts := strings.Split(tag, ",")
if len(tag) == 0 || len(parts) == 0 {
return tagOptions{}, nil
}
// Handle `struct:"-"`
if parts[0] == "-" {
if len(parts) > 1 {
return tagOptions{}, errors.New("extra options on ignored field")
}
return tagOptions{Ignore: true}, nil
}
result := tagOptions{}
for _, part := range parts {
switch part {
case "lsb", "little":
result.Order = binary.LittleEndian
continue
case "msb", "big", "network":
result.Order = binary.BigEndian
continue
default:
if strings.HasPrefix(part, "sizeof=") {
result.SizeOf = part[7:]
continue
} else if strings.HasPrefix(part, "skip=") {
var err error
result.Skip, err = strconv.Atoi(part[5:])
if err != nil {
return tagOptions{}, errors.New("bad skip amount")
}
} else {
// Here is where the type is parsed from the tag
dataType := strings.Split(part, ":")
if len(dataType) > 2 {
return tagOptions{}, errors.New("extra options on type field")
}
// parse the datatype part
typ, err := parseType(dataType[0])
if err != nil {
return tagOptions{}, err
}
result.Type = typ
// parse de bitfield type
if len(dataType) > 1 {
if len(dataType[1]) > 0 {
bsize, err := strconv.Atoi(dataType[1])
if err != nil || bsize == 0 {
return tagOptions{}, errors.New("Bad value on bitfield")
}
result.BitSize = uint8(bsize)
// Caution!! reflect.Type.Bits() can panic if called on non int,float or complex
if result.BitSize >= uint8(typ.Bits()) {
return tagOptions{}, errors.New("Too high value on bitfield")
}
}
}
continue
}
}
}
return result, nil
}