| 1 | package steward |
| 2 | |
| 3 | import ( |
| 4 | "strconv" |
| 5 | ) |
| 6 | |
| 7 | type structTag string |
| 8 | |
| 9 | func (tag structTag) Parse() map[string]string { |
| 10 | out := make(map[string]string) |
| 11 | |
| 12 | for tag != "" { |
| 13 | // Skip leading space. |
| 14 | i := 0 |
| 15 | for i < len(tag) && tag[i] == ' ' { |
| 16 | i++ |
| 17 | } |
| 18 | tag = tag[i:] |
| 19 | if tag == "" { |
| 20 | break |
| 21 | } |
| 22 | |
| 23 | // Scan to colon. A space, a quote or a control character is a syntax error. |
| 24 | // Strictly speaking, control chars include the range [0x7f, 0x9f], not just |
| 25 | // [0x00, 0x1f], but in practice, we ignore the multi-byte control characters |
| 26 | // as it is simpler to inspect the tag's bytes than the tag's runes. |
| 27 | i = 0 |
| 28 | for i < len(tag) && tag[i] > ' ' && tag[i] != ':' && tag[i] != '"' && tag[i] != 0x7f { |
| 29 | i++ |
| 30 | } |
| 31 | if i == 0 || i+1 >= len(tag) || tag[i] != ':' || tag[i+1] != '"' { |
| 32 | break |
| 33 | } |
| 34 | name := string(tag[:i]) |
| 35 | tag = tag[i+1:] |
| 36 | |
| 37 | // Scan quoted string to find value. |
| 38 | i = 1 |
| 39 | for i < len(tag) && tag[i] != '"' { |
| 40 | if tag[i] == '\\' { |
| 41 | i++ |
| 42 | } |
| 43 | i++ |
| 44 | } |
| 45 | if i >= len(tag) { |
| 46 | break |
| 47 | } |
| 48 | qvalue := string(tag[:i+1]) |
| 49 | tag = tag[i+1:] |
| 50 | |
| 51 | value, err := strconv.Unquote(qvalue) |
| 52 | if err != nil { |
| 53 | break |
| 54 | } |
| 55 | out[name] = value |
| 56 | } |
| 57 | |
| 58 | return out |
| 59 | } |
| 60 | |