| package flags |
| |
| import ( |
| "strconv" |
| ) |
| |
| type multiTag struct { |
| value string |
| cache map[string][]string |
| } |
| |
| func newMultiTag(v string) multiTag { |
| return multiTag{ |
| value: v, |
| } |
| } |
| |
| func (x *multiTag) scan() (map[string][]string, error) { |
| v := x.value |
| |
| ret := make(map[string][]string) |
| |
| // This is mostly copied from reflect.StructTag.Get |
| for v != "" { |
| i := 0 |
| |
| // Skip whitespace |
| for i < len(v) && v[i] == ' ' { |
| i++ |
| } |
| |
| v = v[i:] |
| |
| if v == "" { |
| break |
| } |
| |
| // Scan to colon to find key |
| i = 0 |
| |
| for i < len(v) && v[i] != ' ' && v[i] != ':' && v[i] != '"' { |
| i++ |
| } |
| |
| if i >= len(v) { |
| return nil, newErrorf(ErrTag, "expected `:' after key name, but got end of tag (in `%v`)", x.value) |
| } |
| |
| if v[i] != ':' { |
| return nil, newErrorf(ErrTag, "expected `:' after key name, but got `%v' (in `%v`)", v[i], x.value) |
| } |
| |
| if i+1 >= len(v) { |
| return nil, newErrorf(ErrTag, "expected `\"' to start tag value at end of tag (in `%v`)", x.value) |
| } |
| |
| if v[i+1] != '"' { |
| return nil, newErrorf(ErrTag, "expected `\"' to start tag value, but got `%v' (in `%v`)", v[i+1], x.value) |
| } |
| |
| name := v[:i] |
| v = v[i+1:] |
| |
| // Scan quoted string to find value |
| i = 1 |
| |
| for i < len(v) && v[i] != '"' { |
| if v[i] == '\n' { |
| return nil, newErrorf(ErrTag, "unexpected newline in tag value `%v' (in `%v`)", name, x.value) |
| } |
| |
| if v[i] == '\\' { |
| i++ |
| } |
| i++ |
| } |
| |
| if i >= len(v) { |
| return nil, newErrorf(ErrTag, "expected end of tag value `\"' at end of tag (in `%v`)", x.value) |
| } |
| |
| val, err := strconv.Unquote(v[:i+1]) |
| |
| if err != nil { |
| return nil, newErrorf(ErrTag, "Malformed value of tag `%v:%v` => %v (in `%v`)", name, v[:i+1], err, x.value) |
| } |
| |
| v = v[i+1:] |
| |
| ret[name] = append(ret[name], val) |
| } |
| |
| return ret, nil |
| } |
| |
| func (x *multiTag) Parse() error { |
| vals, err := x.scan() |
| x.cache = vals |
| |
| return err |
| } |
| |
| func (x *multiTag) cached() map[string][]string { |
| if x.cache == nil { |
| cache, _ := x.scan() |
| |
| if cache == nil { |
| cache = make(map[string][]string) |
| } |
| |
| x.cache = cache |
| } |
| |
| return x.cache |
| } |
| |
| func (x *multiTag) Get(key string) string { |
| c := x.cached() |
| |
| if v, ok := c[key]; ok { |
| return v[len(v)-1] |
| } |
| |
| return "" |
| } |
| |
| func (x *multiTag) GetMany(key string) []string { |
| c := x.cached() |
| return c[key] |
| } |
| |
| func (x *multiTag) Set(key string, value string) { |
| c := x.cached() |
| c[key] = []string{value} |
| } |
| |
| func (x *multiTag) SetMany(key string, value []string) { |
| c := x.cached() |
| c[key] = value |
| } |