214 lines
3.7 KiB
Go
214 lines
3.7 KiB
Go
package filter
|
|
|
|
import (
|
|
"encoding/json"
|
|
"slices"
|
|
|
|
"x.realy.lol/event"
|
|
"x.realy.lol/helpers"
|
|
"x.realy.lol/kind"
|
|
"x.realy.lol/timestamp"
|
|
)
|
|
|
|
type S []F
|
|
|
|
type F struct {
|
|
Ids []string
|
|
Kinds []int
|
|
Authors []string
|
|
Tags TagMap
|
|
Since *timestamp.Timestamp
|
|
Until *timestamp.Timestamp
|
|
Limit *int
|
|
Search string
|
|
}
|
|
|
|
type TagMap map[string][]string
|
|
|
|
func (eff S) String() string {
|
|
j, _ := json.Marshal(eff)
|
|
return string(j)
|
|
}
|
|
|
|
func (eff S) Match(event *event.E) bool {
|
|
for _, filter := range eff {
|
|
if filter.Matches(event) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func (eff S) MatchIgnoringTimestampConstraints(event *event.E) bool {
|
|
for _, filter := range eff {
|
|
if filter.MatchesIgnoringTimestampConstraints(event) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func (ef F) String() string {
|
|
j, _ := json.Marshal(ef)
|
|
return string(j)
|
|
}
|
|
|
|
func (ef F) Matches(event *event.E) bool {
|
|
if !ef.MatchesIgnoringTimestampConstraints(event) {
|
|
return false
|
|
}
|
|
|
|
if ef.Since != nil && event.CreatedAt < *ef.Since {
|
|
return false
|
|
}
|
|
|
|
if ef.Until != nil && event.CreatedAt > *ef.Until {
|
|
return false
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
func (ef F) MatchesIgnoringTimestampConstraints(event *event.E) bool {
|
|
if event == nil {
|
|
return false
|
|
}
|
|
|
|
if ef.Ids != nil && !slices.Contains(ef.Ids, event.Id) {
|
|
return false
|
|
}
|
|
|
|
if ef.Kinds != nil && !slices.Contains(ef.Kinds, event.Kind) {
|
|
return false
|
|
}
|
|
|
|
if ef.Authors != nil && !slices.Contains(ef.Authors, event.Pubkey) {
|
|
return false
|
|
}
|
|
|
|
for f, v := range ef.Tags {
|
|
if v != nil && !event.Tags.ContainsAny(f, v) {
|
|
return false
|
|
}
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
func FilterEqual(a F, b F) bool {
|
|
if !helpers.Similar(a.Kinds, b.Kinds) {
|
|
return false
|
|
}
|
|
|
|
if !helpers.Similar(a.Ids, b.Ids) {
|
|
return false
|
|
}
|
|
|
|
if !helpers.Similar(a.Authors, b.Authors) {
|
|
return false
|
|
}
|
|
|
|
if len(a.Tags) != len(b.Tags) {
|
|
return false
|
|
}
|
|
|
|
for f, av := range a.Tags {
|
|
if bv, ok := b.Tags[f]; !ok {
|
|
return false
|
|
} else {
|
|
if !helpers.Similar(av, bv) {
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
|
|
if !helpers.ArePointerValuesEqual(a.Since, b.Since) {
|
|
return false
|
|
}
|
|
|
|
if !helpers.ArePointerValuesEqual(a.Until, b.Until) {
|
|
return false
|
|
}
|
|
|
|
if a.Search != b.Search {
|
|
return false
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
func (ef F) Clone() F {
|
|
clone := F{
|
|
Ids: slices.Clone(ef.Ids),
|
|
Authors: slices.Clone(ef.Authors),
|
|
Kinds: slices.Clone(ef.Kinds),
|
|
Limit: ef.Limit,
|
|
Search: ef.Search,
|
|
}
|
|
|
|
if ef.Tags != nil {
|
|
clone.Tags = make(TagMap, len(ef.Tags))
|
|
for k, v := range ef.Tags {
|
|
clone.Tags[k] = slices.Clone(v)
|
|
}
|
|
}
|
|
|
|
if ef.Since != nil {
|
|
since := *ef.Since
|
|
clone.Since = &since
|
|
}
|
|
|
|
if ef.Until != nil {
|
|
until := *ef.Until
|
|
clone.Until = &until
|
|
}
|
|
|
|
return clone
|
|
}
|
|
|
|
// GetTheoreticalLimit gets the maximum number of events that a normal filter would ever return, for example, if
|
|
// there is a number of "ids" in the filter, the theoretical limit will be that number of ids.
|
|
//
|
|
// It returns -1 if there are no theoretical limits.
|
|
//
|
|
// The given .Limit present in the filter is ignored.
|
|
func GetTheoreticalLimit(filter F) int {
|
|
if len(filter.Ids) > 0 {
|
|
return len(filter.Ids)
|
|
}
|
|
|
|
if len(filter.Kinds) == 0 {
|
|
return -1
|
|
}
|
|
|
|
if len(filter.Authors) > 0 {
|
|
allAreReplaceable := true
|
|
for _, k := range filter.Kinds {
|
|
if !kind.IsReplaceableKind(k) {
|
|
allAreReplaceable = false
|
|
break
|
|
}
|
|
}
|
|
if allAreReplaceable {
|
|
return len(filter.Authors) * len(filter.Kinds)
|
|
}
|
|
|
|
if len(filter.Tags["d"]) > 0 {
|
|
allAreAddressable := true
|
|
for _, k := range filter.Kinds {
|
|
if !kind.IsAddressableKind(k) {
|
|
allAreAddressable = false
|
|
break
|
|
}
|
|
}
|
|
if allAreAddressable {
|
|
return len(filter.Authors) * len(filter.Kinds) * len(filter.Tags["d"])
|
|
}
|
|
}
|
|
}
|
|
|
|
return -1
|
|
}
|
|
|
|
func IntToPointer(i int) (ptr *int) { return &i }
|