use generic functions for dealing with lists.

This commit is contained in:
fiatjaf 2022-11-08 07:15:08 -03:00
parent c4d52e516f
commit f98f54d3be
No known key found for this signature in database
GPG Key ID: BAD43C4BE5C1A3A1
7 changed files with 50 additions and 82 deletions

View File

@ -7,9 +7,10 @@ import (
"time" "time"
"github.com/valyala/fastjson" "github.com/valyala/fastjson"
"golang.org/x/exp/slices"
) )
type Tags []StringList type Tags [][]string
func (t *Tags) Scan(src interface{}) error { func (t *Tags) Scan(src interface{}) error {
var jtags []byte = make([]byte, 0) var jtags []byte = make([]byte, 0)
@ -27,7 +28,7 @@ func (t *Tags) Scan(src interface{}) error {
return nil return nil
} }
func (tags Tags) ContainsAny(tagName string, values StringList) bool { func (tags Tags) ContainsAny(tagName string, values []string) bool {
for _, tag := range tags { for _, tag := range tags {
if len(tag) < 2 { if len(tag) < 2 {
continue continue
@ -37,7 +38,7 @@ func (tags Tags) ContainsAny(tagName string, values StringList) bool {
continue continue
} }
if values.Contains(tag[1]) { if slices.Contains(values, tag[1]) {
return true return true
} }
} }
@ -146,14 +147,14 @@ func fastjsonArrayToTags(v *fastjson.Value) (Tags, error) {
return nil, err return nil, err
} }
sll := make([]StringList, len(arr)) sll := make([][]string, len(arr))
for i, v := range arr { for i, v := range arr {
subarr, err := v.Array() subarr, err := v.Array()
if err != nil { if err != nil {
return nil, err return nil, err
} }
sl := make(StringList, len(subarr)) sl := make([]string, len(subarr))
for j, subv := range subarr { for j, subv := range subarr {
sb, err := subv.StringBytes() sb, err := subv.StringBytes()
if err != nil { if err != nil {

View File

@ -2,21 +2,23 @@ package nostr
import ( import (
"time" "time"
"golang.org/x/exp/slices"
) )
type Filters []Filter type Filters []Filter
type Filter struct { type Filter struct {
IDs StringList IDs []string
Kinds IntList Kinds []int
Authors StringList Authors []string
Tags TagMap Tags TagMap
Since *time.Time Since *time.Time
Until *time.Time Until *time.Time
Limit int Limit int
} }
type TagMap map[string]StringList type TagMap map[string][]string
func (eff Filters) Match(event *Event) bool { func (eff Filters) Match(event *Event) bool {
for _, filter := range eff { for _, filter := range eff {
@ -32,15 +34,15 @@ func (ef Filter) Matches(event *Event) bool {
return false return false
} }
if ef.IDs != nil && !ef.IDs.ContainsPrefixOf(event.ID) { if ef.IDs != nil && !ContainsPrefixOf(ef.IDs, event.ID) {
return false return false
} }
if ef.Kinds != nil && !ef.Kinds.Contains(event.Kind) { if ef.Kinds != nil && !slices.Contains(ef.Kinds, event.Kind) {
return false return false
} }
if ef.Authors != nil && !ef.Authors.ContainsPrefixOf(event.PubKey) { if ef.Authors != nil && !ContainsPrefixOf(ef.Authors, event.PubKey) {
return false return false
} }
@ -62,15 +64,15 @@ func (ef Filter) Matches(event *Event) bool {
} }
func FilterEqual(a Filter, b Filter) bool { func FilterEqual(a Filter, b Filter) bool {
if !a.Kinds.Equals(b.Kinds) { if !Similar(a.Kinds, b.Kinds) {
return false return false
} }
if !a.IDs.Equals(b.IDs) { if !Similar(a.IDs, b.IDs) {
return false return false
} }
if !a.Authors.Equals(b.Authors) { if !Similar(a.Authors, b.Authors) {
return false return false
} }
@ -82,7 +84,7 @@ func FilterEqual(a Filter, b Filter) bool {
if bv, ok := b.Tags[f]; !ok { if bv, ok := b.Tags[f]; !ok {
return false return false
} else { } else {
if !av.Equals(bv) { if !Similar(av, bv) {
return false return false
} }
} }

View File

@ -109,7 +109,7 @@ func (f Filter) MarshalJSON() ([]byte, error) {
return o.MarshalTo(nil), nil return o.MarshalTo(nil), nil
} }
func stringListToFastjsonArray(arena *fastjson.Arena, sl StringList) *fastjson.Value { func stringListToFastjsonArray(arena *fastjson.Arena, sl []string) *fastjson.Value {
arr := arena.NewArray() arr := arena.NewArray()
for i, v := range sl { for i, v := range sl {
arr.SetArrayItem(i, arena.NewString(v)) arr.SetArrayItem(i, arena.NewString(v))
@ -117,7 +117,7 @@ func stringListToFastjsonArray(arena *fastjson.Arena, sl StringList) *fastjson.V
return arr return arr
} }
func intListToFastjsonArray(arena *fastjson.Arena, il IntList) *fastjson.Value { func intListToFastjsonArray(arena *fastjson.Arena, il []int) *fastjson.Value {
arr := arena.NewArray() arr := arena.NewArray()
for i, v := range il { for i, v := range il {
arr.SetArrayItem(i, arena.NewNumberInt(v)) arr.SetArrayItem(i, arena.NewNumberInt(v))
@ -125,13 +125,13 @@ func intListToFastjsonArray(arena *fastjson.Arena, il IntList) *fastjson.Value {
return arr return arr
} }
func fastjsonArrayToStringList(v *fastjson.Value) (StringList, error) { func fastjsonArrayToStringList(v *fastjson.Value) ([]string, error) {
arr, err := v.Array() arr, err := v.Array()
if err != nil { if err != nil {
return nil, err return nil, err
} }
sl := make(StringList, len(arr)) sl := make([]string, len(arr))
for i, v := range arr { for i, v := range arr {
sb, err := v.StringBytes() sb, err := v.StringBytes()
if err != nil { if err != nil {
@ -143,13 +143,13 @@ func fastjsonArrayToStringList(v *fastjson.Value) (StringList, error) {
return sl, nil return sl, nil
} }
func fastjsonArrayToIntList(v *fastjson.Value) (IntList, error) { func fastjsonArrayToIntList(v *fastjson.Value) ([]int, error) {
arr, err := v.Array() arr, err := v.Array()
if err != nil { if err != nil {
return nil, err return nil, err
} }
il := make(IntList, len(arr)) il := make([]int, len(arr))
for i, v := range arr { for i, v := range arr {
il[i], err = v.Int() il[i], err = v.Int()
if err != nil { if err != nil {

View File

@ -4,6 +4,8 @@ import (
"encoding/json" "encoding/json"
"testing" "testing"
"time" "time"
"golang.org/x/exp/slices"
) )
func TestFilterUnmarshal(t *testing.T) { func TestFilterUnmarshal(t *testing.T) {
@ -16,7 +18,7 @@ func TestFilterUnmarshal(t *testing.T) {
if f.Since == nil || f.Since.Format("2006-01-02") != "2022-02-07" || if f.Since == nil || f.Since.Format("2006-01-02") != "2022-02-07" ||
f.Until != nil || f.Until != nil ||
f.Tags == nil || len(f.Tags) != 2 || !f.Tags["something"].Contains("bab") { f.Tags == nil || len(f.Tags) != 2 || !slices.Contains(f.Tags["something"], "bab") {
t.Error("failed to parse filter correctly") t.Error("failed to parse filter correctly")
} }
} }
@ -25,7 +27,7 @@ func TestFilterMarshal(t *testing.T) {
tm := time.Unix(12345678, 0) tm := time.Unix(12345678, 0)
filterj, err := json.Marshal(Filter{ filterj, err := json.Marshal(Filter{
Kinds: IntList{1, 2, 4}, Kinds: []int{1, 2, 4},
Tags: TagMap{"fruit": {"banana", "mango"}}, Tags: TagMap{"fruit": {"banana", "mango"}},
Until: &tm, Until: &tm,
}) })
@ -40,20 +42,20 @@ func TestFilterMarshal(t *testing.T) {
} }
func TestFilterMatching(t *testing.T) { func TestFilterMatching(t *testing.T) {
if (Filter{Kinds: IntList{4, 5}}).Matches(&Event{Kind: 6}) { if (Filter{Kinds: []int{4, 5}}).Matches(&Event{Kind: 6}) {
t.Error("matched event that shouldn't have matched") t.Error("matched event that shouldn't have matched")
} }
if !(Filter{Kinds: IntList{4, 5}}).Matches(&Event{Kind: 4}) { if !(Filter{Kinds: []int{4, 5}}).Matches(&Event{Kind: 4}) {
t.Error("failed to match event by kind") t.Error("failed to match event by kind")
} }
if !(Filter{ if !(Filter{
Kinds: IntList{4, 5}, Kinds: []int{4, 5},
Tags: TagMap{ Tags: TagMap{
"p": {"ooo"}, "p": {"ooo"},
}, },
IDs: StringList{"prefix"}, IDs: []string{"prefix"},
}).Matches(&Event{ }).Matches(&Event{
Kind: 4, Kind: 4,
Tags: Tags{{"p", "ooo", ",x,x,"}, {"m", "yywyw", "xxx"}}, Tags: Tags{{"p", "ooo", ",x,x,"}, {"m", "yywyw", "xxx"}},
@ -65,15 +67,15 @@ func TestFilterMatching(t *testing.T) {
func TestFilterEquality(t *testing.T) { func TestFilterEquality(t *testing.T) {
if !FilterEqual( if !FilterEqual(
Filter{Kinds: IntList{4, 5}}, Filter{Kinds: []int{4, 5}},
Filter{Kinds: IntList{4, 5}}, Filter{Kinds: []int{4, 5}},
) { ) {
t.Error("kinds filters should be equal") t.Error("kinds filters should be equal")
} }
if !FilterEqual( if !FilterEqual(
Filter{Kinds: IntList{4, 5}, Tags: TagMap{"letter": {"a", "b"}}}, Filter{Kinds: []int{4, 5}, Tags: TagMap{"letter": {"a", "b"}}},
Filter{Kinds: IntList{4, 5}, Tags: TagMap{"letter": {"b", "a"}}}, Filter{Kinds: []int{4, 5}, Tags: TagMap{"letter": {"b", "a"}}},
) { ) {
t.Error("kind+tags filters should be equal") t.Error("kind+tags filters should be equal")
} }
@ -81,24 +83,24 @@ func TestFilterEquality(t *testing.T) {
tm := time.Now() tm := time.Now()
if !FilterEqual( if !FilterEqual(
Filter{ Filter{
Kinds: IntList{4, 5}, Kinds: []int{4, 5},
Tags: TagMap{"letter": {"a", "b"}, "fruit": {"banana"}}, Tags: TagMap{"letter": {"a", "b"}, "fruit": {"banana"}},
Since: &tm, Since: &tm,
IDs: StringList{"aaaa", "bbbb"}, IDs: []string{"aaaa", "bbbb"},
}, },
Filter{ Filter{
Kinds: IntList{5, 4}, Kinds: []int{5, 4},
Tags: TagMap{"letter": {"a", "b"}, "fruit": {"banana"}}, Tags: TagMap{"letter": {"a", "b"}, "fruit": {"banana"}},
Since: &tm, Since: &tm,
IDs: StringList{"aaaa", "bbbb"}, IDs: []string{"aaaa", "bbbb"},
}, },
) { ) {
t.Error("kind+2tags+since+ids filters should be equal") t.Error("kind+2tags+since+ids filters should be equal")
} }
if FilterEqual( if FilterEqual(
Filter{Kinds: IntList{1, 4, 5}}, Filter{Kinds: []int{1, 4, 5}},
Filter{Kinds: IntList{4, 5, 6}}, Filter{Kinds: []int{4, 5, 6}},
) { ) {
t.Error("kinds filters shouldn't be equal") t.Error("kinds filters shouldn't be equal")
} }

1
go.mod
View File

@ -10,6 +10,7 @@ require (
github.com/tyler-smith/go-bip32 v1.0.0 github.com/tyler-smith/go-bip32 v1.0.0
github.com/tyler-smith/go-bip39 v1.1.0 github.com/tyler-smith/go-bip39 v1.1.0
github.com/valyala/fastjson v1.6.3 github.com/valyala/fastjson v1.6.3
golang.org/x/exp v0.0.0-20221106115401-f9659909a136
) )
require ( require (

2
go.sum
View File

@ -34,6 +34,8 @@ golang.org/x/crypto v0.0.0-20170613210332-850760c427c5/go.mod h1:6SG95UA2DQfeDnf
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/exp v0.0.0-20221106115401-f9659909a136 h1:Fq7F/w7MAa1KJ5bt2aJ62ihqp9HDcRuyILskkpIAurw=
golang.org/x/exp v0.0.0-20221106115401-f9659909a136/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=

View File

@ -2,12 +2,11 @@ package nostr
import ( import (
"strings" "strings"
"golang.org/x/exp/constraints"
) )
type StringList []string func Similar[E constraints.Ordered](as, bs []E) bool {
type IntList []int
func (as StringList) Equals(bs StringList) bool {
if len(as) != len(bs) { if len(as) != len(bs) {
return false return false
} }
@ -28,37 +27,7 @@ func (as StringList) Equals(bs StringList) bool {
return true return true
} }
func (as IntList) Equals(bs IntList) bool { func ContainsPrefixOf(haystack []string, needle string) bool {
if len(as) != len(bs) {
return false
}
for _, a := range as {
for _, b := range bs {
if b == a {
goto next
}
}
// didn't find a B that corresponded to the current A
return false
next:
continue
}
return true
}
func (haystack StringList) Contains(needle string) bool {
for _, hay := range haystack {
if hay == needle {
return true
}
}
return false
}
func (haystack StringList) ContainsPrefixOf(needle string) bool {
for _, hay := range haystack { for _, hay := range haystack {
if strings.HasPrefix(needle, hay) { if strings.HasPrefix(needle, hay) {
return true return true
@ -66,12 +35,3 @@ func (haystack StringList) ContainsPrefixOf(needle string) bool {
} }
return false return false
} }
func (haystack IntList) Contains(needle int) bool {
for _, hay := range haystack {
if hay == needle {
return true
}
}
return false
}