1package 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}
141