This repository has been archived by the owner on Jan 21, 2023. It is now read-only.
-
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathtag.go
134 lines (111 loc) · 3.28 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
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
// The motivation behind this package is that the StructTag implementation shipped
// with Go's standard library is very limited in detecting a malformed StructTag
// and each time StructTag.Get(key) gets called, it results in the StructTag
// being parsed again. Another problem is that the StructTag can not be
// easily manipulated because it is basically a string.
// This package provides a way to parse the StructTag into a Tag map, which
// allows for fast lookups and easy manipulation of key value pairs within the
// Tag.
//
// Deprecated: This package has been moved to github.com/quartercastle/structtag
//
// // Example of struct using tags to append metadata to fields.
// type Server struct {
// Host string `json:"host" env:"SERVER_HOST" default:"localhost"`
// Port int `json:"port" env:"SERVER_PORT" default:"3000"`
// }
package tag
import (
"errors"
"fmt"
"reflect"
"strconv"
"strings"
)
var (
// ErrInvalidSyntax is returned when the StructTag syntax is invalid.
ErrInvalidSyntax = errors.New("invalid syntax for key value pair")
// ErrInvalidKey is returned if a key is containing invalid characters or
// is missing.
ErrInvalidKey = errors.New("invalid key")
// ErrInvalidValue is returned if a value is not qouted.
ErrInvalidValue = errors.New("invalid value")
// ErrInvalidSeparator is returned if comma is used as separator.
ErrInvalidSeparator = errors.New("invalid separator, key value pairs should be separated by spaces")
)
// Tag is just a map of key value pairs.
type Tag map[string]string
// Merge multiple tags together into a single Tag.
// In case of duplicate keys, the last encountered key will overwrite the existing.
func Merge(tags ...Tag) Tag {
for _, t := range tags {
for k, v := range t {
tags[0][k] = v
}
}
return tags[0]
}
// StructTag converts the Tag into a StructTag.
func (t Tag) StructTag() reflect.StructTag {
var s string
for k, v := range t {
s += fmt.Sprintf(`%s:"%s" `, k, v)
}
return reflect.StructTag(strings.TrimSpace(s))
}
// Parse takes a StructTag and parses it into a Tag or returns an error.
// If the given string contains duplicate keys the last key value pair
// will overwrite the previous.
//
// The parsing logic is a slightly modified version of the StructTag.Lookup
// function from the reflect package included in the standard library.
// https://github.com/golang/go/blob/0377f061687771eddfe8de78d6c40e17d6b21a39/src/reflect/type.go#L1132
func Parse(st reflect.StructTag) (Tag, error) {
tag := Tag{}
for st != "" {
i := 0
for i < len(st) && st[i] == ' ' {
i++
}
st = st[i:]
if st == "" {
break
}
i = 0
for i < len(st) && st[i] > ' ' && st[i] != ':' && st[i] != '"' && st[i] != 0x7f {
if st[i] == ',' {
return tag, ErrInvalidSeparator
}
i++
}
if i == 0 {
return tag, ErrInvalidKey
}
if i+1 >= len(st) || st[i] != ':' {
return tag, ErrInvalidSyntax
}
if st[i+1] != '"' {
return tag, ErrInvalidValue
}
key := string(st[:i])
st = st[i+1:]
i = 1
for i < len(st) && st[i] != '"' {
if st[i] == '\\' {
i++
}
i++
}
if i >= len(st) {
return tag, ErrInvalidValue
}
qvalue := string(st[:i+1])
st = st[i+1:]
value, err := strconv.Unquote(qvalue)
if err != nil {
return tag, ErrInvalidValue
}
tag[key] = value
}
return tag, nil
}