jsoncodec.go raw
1 package nostr
2
3 // This is torly's hand-written JSON codec for the wire path. It follows the
4 // Moxie/Nostr decoder: a single pass over the bytes, object keys dispatched
5 // explicitly, and no encoding/json. Object keys are compared as strings rather
6 // than by key[0], and an empty key is rejected before any indexing; `{"":1}`
7 // is legal JSON and previously caused an index-out-of-range panic.
8 //
9 // String decoding validates every escape, rejects C0 controls, and bounds the
10 // surrogate-pair lookahead correctly (i+10 < len, not <=).
11
12 import (
13 "errors"
14 "fmt"
15 "strconv"
16 "strings"
17 "unicode/utf8"
18 )
19
20 var (
21 errJSONUnexpected = errors.New("nostr: unexpected JSON")
22 errJSONEmptyKey = errors.New("nostr: empty JSON object key")
23 )
24
25 type jparser struct {
26 s []byte
27 i int
28 }
29
30 func (p *jparser) ws() {
31 for p.i < len(p.s) {
32 switch p.s[p.i] {
33 case ' ', '\t', '\n', '\r':
34 p.i++
35 default:
36 return
37 }
38 }
39 }
40
41 func (p *jparser) eof() bool { return p.i >= len(p.s) }
42
43 func (p *jparser) expect(c byte) error {
44 if p.i >= len(p.s) || p.s[p.i] != c {
45 return fmt.Errorf("%w: expected %q at %d", errJSONUnexpected, c, p.i)
46 }
47 p.i++
48 return nil
49 }
50
51 func (p *jparser) finish() error {
52 p.ws()
53 if !p.eof() {
54 return fmt.Errorf("%w: trailing data at %d", errJSONUnexpected, p.i)
55 }
56 return nil
57 }
58
59 // ParseJSONString parses exactly one JSON string literal.
60 func ParseJSONString(raw []byte) (string, error) {
61 p := &jparser{s: raw}
62 p.ws()
63 v, err := p.parseString()
64 if err != nil {
65 return "", err
66 }
67 if err := p.finish(); err != nil {
68 return "", err
69 }
70 return v, nil
71 }
72
73 // SplitArray splits a JSON array into its raw element slices. It is used for
74 // relay messages such as ["EVENT",{...}] without allocating an interface tree.
75 func SplitArray(raw []byte) ([][]byte, error) {
76 p := &jparser{s: raw}
77 p.ws()
78 if err := p.expect('['); err != nil {
79 return nil, err
80 }
81 p.ws()
82 if !p.eof() && p.s[p.i] == ']' {
83 p.i++
84 if err := p.finish(); err != nil {
85 return nil, err
86 }
87 return nil, nil
88 }
89 var out [][]byte
90 for {
91 p.ws()
92 start := p.i
93 if err := p.skipValue(); err != nil {
94 return nil, err
95 }
96 part := make([]byte, p.i-start)
97 copy(part, p.s[start:p.i])
98 out = append(out, part)
99 p.ws()
100 if p.eof() {
101 return nil, fmt.Errorf("%w: unterminated array", errJSONUnexpected)
102 }
103 switch p.s[p.i] {
104 case ',':
105 p.i++
106 case ']':
107 p.i++
108 if err := p.finish(); err != nil {
109 return nil, err
110 }
111 return out, nil
112 default:
113 return nil, fmt.Errorf("%w: expected , or ] at %d", errJSONUnexpected, p.i)
114 }
115 }
116 }
117
118 func (p *jparser) parseString() (string, error) {
119 if err := p.expect('"'); err != nil {
120 return "", err
121 }
122 out := make([]byte, 0, 32)
123 for p.i < len(p.s) {
124 c := p.s[p.i]
125 switch {
126 case c == '"':
127 p.i++
128 if !utf8.Valid(out) {
129 return "", fmt.Errorf("%w: invalid UTF-8 in string", errJSONUnexpected)
130 }
131 return string(out), nil
132 case c == '\\':
133 p.i++
134 if p.i >= len(p.s) {
135 return "", fmt.Errorf("%w: unterminated escape", errJSONUnexpected)
136 }
137 switch p.s[p.i] {
138 case '"', '\\', '/':
139 out = append(out, p.s[p.i])
140 p.i++
141 case 'b':
142 out = append(out, '\b')
143 p.i++
144 case 'f':
145 out = append(out, '\f')
146 p.i++
147 case 'n':
148 out = append(out, '\n')
149 p.i++
150 case 'r':
151 out = append(out, '\r')
152 p.i++
153 case 't':
154 out = append(out, '\t')
155 p.i++
156 case 'u':
157 r, err := p.parseUnicodeEscape()
158 if err != nil {
159 return "", err
160 }
161 out = append(out, string(rune(r))...)
162 default:
163 return "", fmt.Errorf("%w: bad escape \\%c", errJSONUnexpected, p.s[p.i])
164 }
165 case c < 0x20:
166 return "", fmt.Errorf("%w: control byte in string", errJSONUnexpected)
167 default:
168 out = append(out, c)
169 p.i++
170 }
171 }
172 return "", fmt.Errorf("%w: unterminated string", errJSONUnexpected)
173 }
174
175 // parseUnicodeEscape is called with p.i at 'u'. It consumes the four hex
176 // digits and an optional low surrogate, with a bounds check that cannot run
177 // past the end of the input.
178 func (p *jparser) parseUnicodeEscape() (rune, error) {
179 if p.i+4 >= len(p.s) {
180 return 0, fmt.Errorf("%w: short \\u escape", errJSONUnexpected)
181 }
182 hi, ok := hex4(p.s[p.i+1 : p.i+5])
183 if !ok {
184 return 0, fmt.Errorf("%w: bad \\u escape", errJSONUnexpected)
185 }
186 p.i += 5 // 'u' + four digits
187 if hi >= 0xD800 && hi <= 0xDBFF {
188 // A high surrogate must be followed by a low surrogate: six more
189 // bytes, \uXXXX. The strict < is what prevents s[i+10] on truncation.
190 if p.i+6 > len(p.s) || p.s[p.i] != '\\' || p.s[p.i+1] != 'u' {
191 return 0, fmt.Errorf("%w: unpaired surrogate", errJSONUnexpected)
192 }
193 lo, ok := hex4(p.s[p.i+2 : p.i+6])
194 if !ok || lo < 0xDC00 || lo > 0xDFFF {
195 return 0, fmt.Errorf("%w: bad low surrogate", errJSONUnexpected)
196 }
197 p.i += 6
198 return rune(0x10000 + (hi-0xD800)*0x400 + (lo - 0xDC00)), nil
199 }
200 if hi >= 0xDC00 && hi <= 0xDFFF {
201 return 0, fmt.Errorf("%w: lone low surrogate", errJSONUnexpected)
202 }
203 return rune(hi), nil
204 }
205
206 func hex4(b []byte) (int, bool) {
207 if len(b) != 4 {
208 return 0, false
209 }
210 v := 0
211 for _, c := range b {
212 var d int
213 switch {
214 case c >= '0' && c <= '9':
215 d = int(c - '0')
216 case c >= 'a' && c <= 'f':
217 d = int(c-'a') + 10
218 case c >= 'A' && c <= 'F':
219 d = int(c-'A') + 10
220 default:
221 return 0, false
222 }
223 v = v<<4 | d
224 }
225 return v, true
226 }
227
228 func (p *jparser) parseInt64() (int64, error) {
229 p.ws()
230 start := p.i
231 if p.i < len(p.s) && p.s[p.i] == '-' {
232 p.i++
233 }
234 digits := false
235 for p.i < len(p.s) && p.s[p.i] >= '0' && p.s[p.i] <= '9' {
236 p.i++
237 digits = true
238 }
239 if !digits {
240 return 0, fmt.Errorf("%w: expected integer at %d", errJSONUnexpected, start)
241 }
242 if p.i < len(p.s) && (p.s[p.i] == '.' || p.s[p.i] == 'e' || p.s[p.i] == 'E') {
243 return 0, fmt.Errorf("%w: non-integer number at %d", errJSONUnexpected, start)
244 }
245 return strconv.ParseInt(string(p.s[start:p.i]), 10, 64)
246 }
247
248 func (p *jparser) parseStringArray() ([]string, error) {
249 p.ws()
250 if err := p.expect('['); err != nil {
251 return nil, err
252 }
253 p.ws()
254 if !p.eof() && p.s[p.i] == ']' {
255 p.i++
256 return nil, nil
257 }
258 var out []string
259 for {
260 p.ws()
261 v, err := p.parseString()
262 if err != nil {
263 return nil, err
264 }
265 out = append(out, v)
266 p.ws()
267 if p.eof() {
268 return nil, fmt.Errorf("%w: unterminated array", errJSONUnexpected)
269 }
270 switch p.s[p.i] {
271 case ',':
272 p.i++
273 case ']':
274 p.i++
275 return out, nil
276 default:
277 return nil, fmt.Errorf("%w: expected , or ]", errJSONUnexpected)
278 }
279 }
280 }
281
282 func (p *jparser) parseIntArray() ([]int, error) {
283 p.ws()
284 if err := p.expect('['); err != nil {
285 return nil, err
286 }
287 p.ws()
288 if !p.eof() && p.s[p.i] == ']' {
289 p.i++
290 return nil, nil
291 }
292 var out []int
293 for {
294 n, err := p.parseInt64()
295 if err != nil {
296 return nil, err
297 }
298 out = append(out, int(n))
299 p.ws()
300 if p.eof() {
301 return nil, fmt.Errorf("%w: unterminated array", errJSONUnexpected)
302 }
303 switch p.s[p.i] {
304 case ',':
305 p.i++
306 case ']':
307 p.i++
308 return out, nil
309 default:
310 return nil, fmt.Errorf("%w: expected , or ]", errJSONUnexpected)
311 }
312 }
313 }
314
315 func (p *jparser) parseTags() (Tags, error) {
316 p.ws()
317 if err := p.expect('['); err != nil {
318 return nil, err
319 }
320 p.ws()
321 if !p.eof() && p.s[p.i] == ']' {
322 p.i++
323 return nil, nil
324 }
325 var tags Tags
326 for {
327 p.ws()
328 inner, err := p.parseStringArray()
329 if err != nil {
330 return nil, err
331 }
332 tags = append(tags, Tag(inner))
333 p.ws()
334 if p.eof() {
335 return nil, fmt.Errorf("%w: unterminated tags", errJSONUnexpected)
336 }
337 switch p.s[p.i] {
338 case ',':
339 p.i++
340 case ']':
341 p.i++
342 return tags, nil
343 default:
344 return nil, fmt.Errorf("%w: expected , or ] in tags", errJSONUnexpected)
345 }
346 }
347 }
348
349 func (p *jparser) skipValue() error {
350 p.ws()
351 if p.eof() {
352 return fmt.Errorf("%w: missing value", errJSONUnexpected)
353 }
354 switch p.s[p.i] {
355 case '"':
356 _, err := p.parseString()
357 return err
358 case '{':
359 p.i++
360 p.ws()
361 if !p.eof() && p.s[p.i] == '}' {
362 p.i++
363 return nil
364 }
365 for {
366 p.ws()
367 if _, err := p.parseString(); err != nil {
368 return err
369 }
370 p.ws()
371 if err := p.expect(':'); err != nil {
372 return err
373 }
374 if err := p.skipValue(); err != nil {
375 return err
376 }
377 p.ws()
378 if p.eof() {
379 return fmt.Errorf("%w: unterminated object", errJSONUnexpected)
380 }
381 switch p.s[p.i] {
382 case ',':
383 p.i++
384 case '}':
385 p.i++
386 return nil
387 default:
388 return fmt.Errorf("%w: expected , or }", errJSONUnexpected)
389 }
390 }
391 case '[':
392 p.i++
393 p.ws()
394 if !p.eof() && p.s[p.i] == ']' {
395 p.i++
396 return nil
397 }
398 for {
399 if err := p.skipValue(); err != nil {
400 return err
401 }
402 p.ws()
403 if p.eof() {
404 return fmt.Errorf("%w: unterminated array", errJSONUnexpected)
405 }
406 switch p.s[p.i] {
407 case ',':
408 p.i++
409 case ']':
410 p.i++
411 return nil
412 default:
413 return fmt.Errorf("%w: expected , or ]", errJSONUnexpected)
414 }
415 }
416 case 't':
417 return p.literal("true")
418 case 'f':
419 return p.literal("false")
420 case 'n':
421 return p.literal("null")
422 default:
423 start := p.i
424 if p.s[p.i] == '-' {
425 p.i++
426 }
427 digits := false
428 for p.i < len(p.s) && ((p.s[p.i] >= '0' && p.s[p.i] <= '9') || p.s[p.i] == '.' || p.s[p.i] == 'e' || p.s[p.i] == 'E' || p.s[p.i] == '+' || p.s[p.i] == '-') {
429 p.i++
430 digits = true
431 }
432 if !digits {
433 return fmt.Errorf("%w: bad value at %d", errJSONUnexpected, start)
434 }
435 return nil
436 }
437 }
438
439 func (p *jparser) literal(v string) error {
440 if p.i+len(v) > len(p.s) || string(p.s[p.i:p.i+len(v)]) != v {
441 return fmt.Errorf("%w: expected %s", errJSONUnexpected, v)
442 }
443 p.i += len(v)
444 return nil
445 }
446
447 // ParseEvent decodes one NIP-01 event object. Unlike encoding/json, this
448 // decoder never indexes an object key without checking it: an empty key is an
449 // error, not a panic.
450 func ParseEvent(data []byte) (*Event, error) {
451 p := &jparser{s: data}
452 p.ws()
453 if err := p.expect('{'); err != nil {
454 return nil, err
455 }
456 e := &Event{}
457 var haveID, havePub, haveCreated, haveKind, haveTags, haveContent, haveSig bool
458 first := true
459 for {
460 p.ws()
461 if p.eof() {
462 return nil, fmt.Errorf("%w: unterminated event object", errJSONUnexpected)
463 }
464 if p.s[p.i] == '}' {
465 p.i++
466 break
467 }
468 if !first {
469 if err := p.expect(','); err != nil {
470 return nil, err
471 }
472 p.ws()
473 }
474 key, err := p.parseString()
475 if err != nil {
476 return nil, err
477 }
478 if key == "" {
479 return nil, errJSONEmptyKey
480 }
481 p.ws()
482 if err := p.expect(':'); err != nil {
483 return nil, err
484 }
485 p.ws()
486 switch key {
487 case "id":
488 v, err := p.parseString()
489 if err != nil {
490 return nil, err
491 }
492 if err := Decode32(v, e.ID[:]); err != nil {
493 return nil, fmt.Errorf("%w: %q", ErrBadID, v)
494 }
495 haveID = true
496 case "pubkey":
497 v, err := p.parseString()
498 if err != nil {
499 return nil, err
500 }
501 if err := Decode32(v, e.PubKey[:]); err != nil {
502 return nil, fmt.Errorf("%w: %q", ErrBadPubKey, v)
503 }
504 havePub = true
505 case "created_at":
506 n, err := p.parseInt64()
507 if err != nil {
508 return nil, fmt.Errorf("%w: %v", ErrBadCreated, err)
509 }
510 e.CreatedAt = n
511 haveCreated = true
512 case "kind":
513 n, err := p.parseInt64()
514 if err != nil || n < 0 || n > MaxKind {
515 return nil, fmt.Errorf("%w: %d", ErrBadKind, n)
516 }
517 e.Kind = int(n)
518 haveKind = true
519 case "tags":
520 tags, err := p.parseTags()
521 if err != nil {
522 return nil, err
523 }
524 e.Tags = tags
525 haveTags = true
526 case "content":
527 v, err := p.parseString()
528 if err != nil {
529 return nil, err
530 }
531 e.Content = v
532 haveContent = true
533 case "sig":
534 v, err := p.parseString()
535 if err != nil {
536 return nil, err
537 }
538 if err := DecodeRaw(v, e.Sig[:]); err != nil {
539 return nil, fmt.Errorf("%w: %q", ErrBadSig, v)
540 }
541 haveSig = true
542 default:
543 if err := p.skipValue(); err != nil {
544 return nil, err
545 }
546 }
547 first = false
548 }
549 if err := p.finish(); err != nil {
550 return nil, err
551 }
552 if !(haveID && havePub && haveCreated && haveKind && haveTags && haveContent && haveSig) {
553 return nil, errors.New("nostr: event is missing a required field")
554 }
555 return e, nil
556 }
557
558 // ExtractID scans an object for a valid id without requiring the rest of the
559 // event to be well formed. It is used to address an OK response to a malformed
560 // event. An empty key is skipped, never indexed.
561 func ExtractID(data []byte) string {
562 p := &jparser{s: data}
563 p.ws()
564 if err := p.expect('{'); err != nil {
565 return ""
566 }
567 for {
568 p.ws()
569 if p.eof() || p.s[p.i] == '}' {
570 return ""
571 }
572 key, err := p.parseString()
573 if err != nil {
574 return ""
575 }
576 p.ws()
577 if err := p.expect(':'); err != nil {
578 return ""
579 }
580 p.ws()
581 if key == "id" {
582 v, err := p.parseString()
583 if err != nil || !IsHex64(v) {
584 return ""
585 }
586 return v
587 }
588 if err := p.skipValue(); err != nil {
589 return ""
590 }
591 p.ws()
592 if p.eof() {
593 return ""
594 }
595 if p.s[p.i] == ',' {
596 p.i++
597 continue
598 }
599 return ""
600 }
601 }
602
603 // parseGraphQuery decodes the _graph filter extension. Both the canonical
604 // four-element array and the legacy object form are accepted.
605 func (p *jparser) parseGraphQuery() (*GraphQuery, error) {
606 p.ws()
607 if p.eof() {
608 return nil, fmt.Errorf("%w: missing _graph value", errJSONUnexpected)
609 }
610 switch p.s[p.i] {
611 case '[':
612 return p.parseGraphArray()
613 case '{':
614 return p.parseGraphObject()
615 default:
616 return nil, fmt.Errorf("%w: _graph must be an array or object", errJSONUnexpected)
617 }
618 }
619
620 // parseGraphArray decodes ["<pubkey>", <depth>, "<edge>", "<direction>"].
621 func (p *jparser) parseGraphArray() (*GraphQuery, error) {
622 if err := p.expect('['); err != nil {
623 return nil, err
624 }
625 q := &GraphQuery{}
626 for i := 0; i < 4; i++ {
627 p.ws()
628 if p.eof() {
629 return nil, fmt.Errorf("%w: unterminated _graph array", errJSONUnexpected)
630 }
631 switch i {
632 case 0:
633 v, err := p.parseString()
634 if err != nil {
635 return nil, fmt.Errorf("_graph seed: %w", err)
636 }
637 q.Seed = v
638 case 1:
639 n, err := p.parseInt64()
640 if err != nil {
641 return nil, fmt.Errorf("_graph depth: %w", err)
642 }
643 if n > GraphMaxDepth {
644 return nil, errGraphDepth
645 }
646 q.Depth = int(n)
647 case 2:
648 v, err := p.parseString()
649 if err != nil {
650 return nil, fmt.Errorf("_graph edge: %w", err)
651 }
652 q.Edge = v
653 case 3:
654 v, err := p.parseString()
655 if err != nil {
656 return nil, fmt.Errorf("_graph direction: %w", err)
657 }
658 q.Direction = v
659 }
660 p.ws()
661 if p.eof() {
662 return nil, fmt.Errorf("%w: unterminated _graph array", errJSONUnexpected)
663 }
664 if i < 3 {
665 if err := p.expect(','); err != nil {
666 return nil, err
667 }
668 } else if p.s[p.i] != ']' {
669 return nil, fmt.Errorf("%w: _graph needs exactly four elements", errJSONUnexpected)
670 }
671 }
672 p.i++ // ']'
673 if err := q.normalize(); err != nil {
674 return nil, err
675 }
676 return q, nil
677 }
678
679 // parseGraphObject decodes the object form. Unknown keys are skipped, which
680 // keeps the extension forward-compatible without making an empty key legal.
681 func (p *jparser) parseGraphObject() (*GraphQuery, error) {
682 if err := p.expect('{'); err != nil {
683 return nil, err
684 }
685 q := &GraphQuery{}
686 first := true
687 for {
688 p.ws()
689 if p.eof() {
690 return nil, fmt.Errorf("%w: unterminated _graph object", errJSONUnexpected)
691 }
692 if p.s[p.i] == '}' {
693 p.i++
694 break
695 }
696 if !first {
697 if err := p.expect(','); err != nil {
698 return nil, err
699 }
700 p.ws()
701 }
702 key, err := p.parseString()
703 if err != nil {
704 return nil, err
705 }
706 if key == "" {
707 return nil, errJSONEmptyKey
708 }
709 p.ws()
710 if err := p.expect(':'); err != nil {
711 return nil, err
712 }
713 p.ws()
714 switch strings.ToLower(key) {
715 case "pubkey", "seed":
716 v, err := p.parseString()
717 if err != nil {
718 return nil, fmt.Errorf("_graph %s: %w", key, err)
719 }
720 q.Seed = v
721 case "depth":
722 n, err := p.parseInt64()
723 if err != nil {
724 return nil, fmt.Errorf("_graph depth: %w", err)
725 }
726 if n > GraphMaxDepth {
727 return nil, errGraphDepth
728 }
729 q.Depth = int(n)
730 case "edge":
731 v, err := p.parseString()
732 if err != nil {
733 return nil, fmt.Errorf("_graph edge: %w", err)
734 }
735 q.Edge = v
736 case "direction":
737 v, err := p.parseString()
738 if err != nil {
739 return nil, fmt.Errorf("_graph direction: %w", err)
740 }
741 q.Direction = v
742 default:
743 if err := p.skipValue(); err != nil {
744 return nil, err
745 }
746 }
747 first = false
748 }
749 if err := q.normalize(); err != nil {
750 return nil, err
751 }
752 return q, nil
753 }
754
755 // ParseFilter decodes one NIP-01 filter object. As with events, keys are
756 // compared as strings and an empty key is an explicit error.
757 func ParseFilter(raw []byte) (*Filter, error) {
758 p := &jparser{s: raw}
759 p.ws()
760 if err := p.expect('{'); err != nil {
761 return nil, fmt.Errorf("%w: %v", errBadFilter, err)
762 }
763 f := &Filter{}
764 first := true
765 for {
766 p.ws()
767 if p.eof() {
768 return nil, fmt.Errorf("%w: unterminated filter", errBadFilter)
769 }
770 if p.s[p.i] == '}' {
771 p.i++
772 break
773 }
774 if !first {
775 if err := p.expect(','); err != nil {
776 return nil, fmt.Errorf("%w: %v", errBadFilter, err)
777 }
778 p.ws()
779 }
780 key, err := p.parseString()
781 if err != nil {
782 return nil, fmt.Errorf("%w: %v", errBadFilter, err)
783 }
784 if key == "" {
785 return nil, fmt.Errorf("%w: %v", errBadFilter, errJSONEmptyKey)
786 }
787 p.ws()
788 if err := p.expect(':'); err != nil {
789 return nil, fmt.Errorf("%w: %v", errBadFilter, err)
790 }
791 p.ws()
792 switch key {
793 case "ids":
794 values, err := p.parseStringArray()
795 if err != nil {
796 return nil, fmt.Errorf("%w: ids: %v", errBadFilter, err)
797 }
798 for _, id := range values {
799 if !IsHex64(id) {
800 return nil, fmt.Errorf("%w: id %q is not 64 lowercase hex characters", errBadFilter, id)
801 }
802 }
803 f.IDs = values
804 case "authors":
805 values, err := p.parseStringArray()
806 if err != nil {
807 return nil, fmt.Errorf("%w: authors: %v", errBadFilter, err)
808 }
809 for _, a := range values {
810 if !IsHex64(a) {
811 return nil, fmt.Errorf("%w: author %q is not 64 lowercase hex characters", errBadFilter, a)
812 }
813 }
814 f.Authors = values
815 case "kinds":
816 values, err := p.parseIntArray()
817 if err != nil {
818 return nil, fmt.Errorf("%w: kinds: %v", errBadFilter, err)
819 }
820 for _, k := range values {
821 if k < 0 || k > MaxKind {
822 return nil, fmt.Errorf("%w: kind %d", errBadFilter, k)
823 }
824 }
825 f.Kinds = values
826 case "since":
827 n, err := p.parseInt64()
828 if err != nil {
829 return nil, fmt.Errorf("%w: since: %v", errBadFilter, err)
830 }
831 f.Since = n
832 case "until":
833 n, err := p.parseInt64()
834 if err != nil {
835 return nil, fmt.Errorf("%w: until: %v", errBadFilter, err)
836 }
837 f.Until = n
838 case "limit":
839 n, err := p.parseInt64()
840 if err != nil {
841 return nil, fmt.Errorf("%w: limit: %v", errBadFilter, err)
842 }
843 if n > 0 {
844 if n > 1<<31-1 {
845 n = 1<<31 - 1
846 }
847 f.Limit = int(n)
848 }
849 case "search":
850 v, err := p.parseString()
851 if err != nil {
852 return nil, fmt.Errorf("%w: search: %v", errBadFilter, err)
853 }
854 f.Search = v
855 case "_graph":
856 q, err := p.parseGraphQuery()
857 if err != nil {
858 return nil, fmt.Errorf("%w: _graph: %v", errBadFilter, err)
859 }
860 f.Graph = q
861 default:
862 if len(key) >= 2 && key[0] == '#' {
863 values, err := p.parseStringArray()
864 if err != nil {
865 return nil, fmt.Errorf("%w: %s: %v", errBadFilter, key, err)
866 }
867 if f.Tags == nil {
868 f.Tags = make(map[string][]string, 1)
869 }
870 f.Tags[key[1:]] = values
871 } else if err := p.skipValue(); err != nil {
872 return nil, fmt.Errorf("%w: %s: %v", errBadFilter, key, err)
873 }
874 }
875 first = false
876 }
877 if err := p.finish(); err != nil {
878 return nil, fmt.Errorf("%w: %v", errBadFilter, err)
879 }
880 return f, nil
881 }
882