blob: 96bb1a31dee379e97a8070d49132191f57b48801 [file] [log] [blame]
Zack Williamse940c7a2019-08-21 14:25:39 -07001package flags
2
3import (
4 "strconv"
5)
6
7type multiTag struct {
8 value string
9 cache map[string][]string
10}
11
12func newMultiTag(v string) multiTag {
13 return multiTag{
14 value: v,
15 }
16}
17
18func (x *multiTag) scan() (map[string][]string, error) {
19 v := x.value
20
21 ret := make(map[string][]string)
22
23 // This is mostly copied from reflect.StructTag.Get
24 for v != "" {
25 i := 0
26
27 // Skip whitespace
28 for i < len(v) && v[i] == ' ' {
29 i++
30 }
31
32 v = v[i:]
33
34 if v == "" {
35 break
36 }
37
38 // Scan to colon to find key
39 i = 0
40
41 for i < len(v) && v[i] != ' ' && v[i] != ':' && v[i] != '"' {
42 i++
43 }
44
45 if i >= len(v) {
46 return nil, newErrorf(ErrTag, "expected `:' after key name, but got end of tag (in `%v`)", x.value)
47 }
48
49 if v[i] != ':' {
50 return nil, newErrorf(ErrTag, "expected `:' after key name, but got `%v' (in `%v`)", v[i], x.value)
51 }
52
53 if i+1 >= len(v) {
54 return nil, newErrorf(ErrTag, "expected `\"' to start tag value at end of tag (in `%v`)", x.value)
55 }
56
57 if v[i+1] != '"' {
58 return nil, newErrorf(ErrTag, "expected `\"' to start tag value, but got `%v' (in `%v`)", v[i+1], x.value)
59 }
60
61 name := v[:i]
62 v = v[i+1:]
63
64 // Scan quoted string to find value
65 i = 1
66
67 for i < len(v) && v[i] != '"' {
68 if v[i] == '\n' {
69 return nil, newErrorf(ErrTag, "unexpected newline in tag value `%v' (in `%v`)", name, x.value)
70 }
71
72 if v[i] == '\\' {
73 i++
74 }
75 i++
76 }
77
78 if i >= len(v) {
79 return nil, newErrorf(ErrTag, "expected end of tag value `\"' at end of tag (in `%v`)", x.value)
80 }
81
82 val, err := strconv.Unquote(v[:i+1])
83
84 if err != nil {
85 return nil, newErrorf(ErrTag, "Malformed value of tag `%v:%v` => %v (in `%v`)", name, v[:i+1], err, x.value)
86 }
87
88 v = v[i+1:]
89
90 ret[name] = append(ret[name], val)
91 }
92
93 return ret, nil
94}
95
96func (x *multiTag) Parse() error {
97 vals, err := x.scan()
98 x.cache = vals
99
100 return err
101}
102
103func (x *multiTag) cached() map[string][]string {
104 if x.cache == nil {
105 cache, _ := x.scan()
106
107 if cache == nil {
108 cache = make(map[string][]string)
109 }
110
111 x.cache = cache
112 }
113
114 return x.cache
115}
116
117func (x *multiTag) Get(key string) string {
118 c := x.cached()
119
120 if v, ok := c[key]; ok {
121 return v[len(v)-1]
122 }
123
124 return ""
125}
126
127func (x *multiTag) GetMany(key string) []string {
128 c := x.cached()
129 return c[key]
130}
131
132func (x *multiTag) Set(key string, value string) {
133 c := x.cached()
134 c[key] = []string{value}
135}
136
137func (x *multiTag) SetMany(key string, value []string) {
138 c := x.cached()
139 c[key] = value
140}