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