]>
Commit | Line | Data |
---|---|---|
107c1cdb ND |
1 | package msgpack |
2 | ||
3 | import ( | |
4 | "fmt" | |
5 | "strconv" | |
6 | "strings" | |
7 | ||
8 | "github.com/vmihailenco/msgpack/codes" | |
9 | ) | |
10 | ||
11 | type queryResult struct { | |
12 | query string | |
13 | key string | |
14 | hasAsterisk bool | |
15 | ||
16 | values []interface{} | |
17 | } | |
18 | ||
19 | func (q *queryResult) nextKey() { | |
20 | ind := strings.IndexByte(q.query, '.') | |
21 | if ind == -1 { | |
22 | q.key = q.query | |
23 | q.query = "" | |
24 | return | |
25 | } | |
26 | q.key = q.query[:ind] | |
27 | q.query = q.query[ind+1:] | |
28 | } | |
29 | ||
30 | // Query extracts data specified by the query from the msgpack stream skipping | |
31 | // any other data. Query consists of map keys and array indexes separated with dot, | |
32 | // e.g. key1.0.key2. | |
33 | func (d *Decoder) Query(query string) ([]interface{}, error) { | |
34 | res := queryResult{ | |
35 | query: query, | |
36 | } | |
37 | if err := d.query(&res); err != nil { | |
38 | return nil, err | |
39 | } | |
40 | return res.values, nil | |
41 | } | |
42 | ||
43 | func (d *Decoder) query(q *queryResult) error { | |
44 | q.nextKey() | |
45 | if q.key == "" { | |
46 | v, err := d.decodeInterfaceCond() | |
47 | if err != nil { | |
48 | return err | |
49 | } | |
50 | q.values = append(q.values, v) | |
51 | return nil | |
52 | } | |
53 | ||
54 | code, err := d.PeekCode() | |
55 | if err != nil { | |
56 | return err | |
57 | } | |
58 | ||
59 | switch { | |
60 | case code == codes.Map16 || code == codes.Map32 || codes.IsFixedMap(code): | |
61 | err = d.queryMapKey(q) | |
62 | case code == codes.Array16 || code == codes.Array32 || codes.IsFixedArray(code): | |
63 | err = d.queryArrayIndex(q) | |
64 | default: | |
65 | err = fmt.Errorf("msgpack: unsupported code=%x decoding key=%q", code, q.key) | |
66 | } | |
67 | return err | |
68 | } | |
69 | ||
70 | func (d *Decoder) queryMapKey(q *queryResult) error { | |
71 | n, err := d.DecodeMapLen() | |
72 | if err != nil { | |
73 | return err | |
74 | } | |
75 | if n == -1 { | |
76 | return nil | |
77 | } | |
78 | ||
79 | for i := 0; i < n; i++ { | |
80 | k, err := d.bytesNoCopy() | |
81 | if err != nil { | |
82 | return err | |
83 | } | |
84 | ||
85 | if string(k) == q.key { | |
86 | if err := d.query(q); err != nil { | |
87 | return err | |
88 | } | |
89 | if q.hasAsterisk { | |
90 | return d.skipNext((n - i - 1) * 2) | |
91 | } | |
92 | return nil | |
93 | } | |
94 | ||
95 | if err := d.Skip(); err != nil { | |
96 | return err | |
97 | } | |
98 | } | |
99 | ||
100 | return nil | |
101 | } | |
102 | ||
103 | func (d *Decoder) queryArrayIndex(q *queryResult) error { | |
104 | n, err := d.DecodeArrayLen() | |
105 | if err != nil { | |
106 | return err | |
107 | } | |
108 | if n == -1 { | |
109 | return nil | |
110 | } | |
111 | ||
112 | if q.key == "*" { | |
113 | q.hasAsterisk = true | |
114 | ||
115 | query := q.query | |
116 | for i := 0; i < n; i++ { | |
117 | q.query = query | |
118 | if err := d.query(q); err != nil { | |
119 | return err | |
120 | } | |
121 | } | |
122 | ||
123 | q.hasAsterisk = false | |
124 | return nil | |
125 | } | |
126 | ||
127 | ind, err := strconv.Atoi(q.key) | |
128 | if err != nil { | |
129 | return err | |
130 | } | |
131 | ||
132 | for i := 0; i < n; i++ { | |
133 | if i == ind { | |
134 | if err := d.query(q); err != nil { | |
135 | return err | |
136 | } | |
137 | if q.hasAsterisk { | |
138 | return d.skipNext(n - i - 1) | |
139 | } | |
140 | return nil | |
141 | } | |
142 | ||
143 | if err := d.Skip(); err != nil { | |
144 | return err | |
145 | } | |
146 | } | |
147 | ||
148 | return nil | |
149 | } | |
150 | ||
151 | func (d *Decoder) skipNext(n int) error { | |
152 | for i := 0; i < n; i++ { | |
153 | if err := d.Skip(); err != nil { | |
154 | return err | |
155 | } | |
156 | } | |
157 | return nil | |
158 | } |