filters.go raw
1 package filter
2
3 import (
4 "next.orly.dev/pkg/nostr/encoders/event"
5 "next.orly.dev/pkg/lol/errorf"
6 )
7
8 type S []*F
9
10 func NewS(ff ...*F) *S {
11 s := S(ff)
12 return &s
13 }
14
15 // Match checks if a set of filters.T matches on an event.F.
16 func (s *S) Match(event *event.E) bool {
17 for _, f := range *s {
18 if f.Matches(event) {
19 return true
20 }
21 }
22 return false
23 }
24
25 // Marshal encodes a slice of filters as a JSON array of objects.
26 // It appends the result to dst and returns the resulting slice.
27 func (s *S) Marshal(dst []byte) (b []byte) {
28 if s == nil {
29 panic("nil filter slice")
30 }
31 b = dst
32 b = append(b, '[')
33 first := false
34 for _, f := range *s {
35 if f == nil {
36 continue
37 }
38 if first {
39 b = append(b, ',')
40 } else {
41 first = true
42 }
43 b = f.Marshal(b)
44 }
45 b = append(b, ']')
46 return
47 }
48
49 // Unmarshal decodes one or more filters from JSON.
50 // This handles both array-wrapped filters [{},...] and unwrapped filters {},...
51 func (s *S) Unmarshal(b []byte) (r []byte, err error) {
52 r = b
53 if len(r) == 0 {
54 return
55 }
56
57 // Check if filters are wrapped in an array
58 isArrayWrapped := r[0] == '['
59 if isArrayWrapped {
60 r = r[1:]
61 // Handle empty array "[]"
62 if len(r) > 0 && r[0] == ']' {
63 r = r[1:]
64 return
65 }
66 }
67
68 for {
69 if len(r) == 0 {
70 return
71 }
72 f := New()
73 var rem []byte
74 if rem, err = f.Unmarshal(r); err != nil {
75 return
76 }
77 *s = append(*s, f)
78 r = rem
79 if len(r) == 0 {
80 return
81 }
82 if r[0] == ',' {
83 // Next element
84 r = r[1:]
85 continue
86 }
87 if r[0] == ']' {
88 // End of array or envelope
89 if isArrayWrapped {
90 // Consume the closing bracket of the filter array
91 r = r[1:]
92 }
93 // Otherwise leave it for the envelope parser
94 return
95 }
96 // Unexpected token
97 err = errorf.E(
98 "filters.Unmarshal: expected ',' or ']' after filter, got: %q",
99 r[0],
100 )
101 return
102 }
103 }
104 func (s *S) MatchIgnoringTimestampConstraints(ev *event.E) bool {
105 for _, ff := range *s {
106 if ff.MatchesIgnoringTimestampConstraints(ev) {
107 return true
108 }
109 }
110 return false
111 }
112