diff --git a/event.go b/event.go index 207bf86..5d687e9 100644 --- a/event.go +++ b/event.go @@ -3,27 +3,33 @@ package nostr import ( "crypto/sha256" "encoding/hex" - "encoding/json" "fmt" "time" "github.com/btcsuite/btcd/btcec/v2" "github.com/btcsuite/btcd/btcec/v2/schnorr" + "github.com/mailru/easyjson" ) type Event struct { - ID string - PubKey string - CreatedAt time.Time - Kind int - Tags Tags - Content string - Sig string + ID string `json:"id"` + PubKey string `json:"pubkey"` + CreatedAt Timestamp `json:"created_at"` + Kind int `json:"kind"` + Tags Tags `json:"tags"` + Content string `json:"content"` + Sig string `json:"sig"` // anything here will be mashed together with the main event object when serializing extra map[string]any } +type Timestamp int64 + +func (t Timestamp) Time() time.Time { + return time.Unix(int64(t), 0) +} + const ( KindSetMetadata int = 0 KindTextNote int = 1 @@ -44,7 +50,7 @@ const ( // Event Stringer interface, just returns the raw JSON as a string func (evt Event) String() string { - j, _ := json.Marshal(evt) + j, _ := easyjson.Marshal(evt) return string(j) } @@ -67,7 +73,7 @@ func (evt *Event) Serialize() []byte { fmt.Sprintf( "[0,\"%s\",%d,%d,", evt.PubKey, - evt.CreatedAt.Unix(), + evt.CreatedAt, evt.Kind, ))...) diff --git a/event_aux.go b/event_aux.go deleted file mode 100644 index 710b5f8..0000000 --- a/event_aux.go +++ /dev/null @@ -1,142 +0,0 @@ -package nostr - -import ( - "bytes" - "encoding/json" - "fmt" - "time" - - "github.com/valyala/fastjson" -) - -func (evt *Event) UnmarshalJSON(payload []byte) error { - var fastjsonParser fastjson.Parser - parsed, err := fastjsonParser.ParseBytes(payload) - if err != nil { - return fmt.Errorf("failed to parse event: %w", err) - } - - obj, err := parsed.Object() - if err != nil { - return fmt.Errorf("event is not an object: %w", err) - } - - // prepare this to receive any extra property that may serialized along with the event - evt.extra = make(map[string]any) - - var visiterr error - obj.Visit(func(k []byte, v *fastjson.Value) { - key := string(k) - switch key { - case "id": - id, err := v.StringBytes() - if err != nil { - visiterr = fmt.Errorf("invalid 'id' field: %w", err) - } - evt.ID = string(id) - case "pubkey": - id, err := v.StringBytes() - if err != nil { - visiterr = fmt.Errorf("invalid 'pubkey' field: %w", err) - } - evt.PubKey = string(id) - case "created_at": - val, err := v.Int64() - if err != nil { - visiterr = fmt.Errorf("invalid 'created_at' field: %w", err) - } - evt.CreatedAt = time.Unix(val, 0) - case "kind": - kind, err := v.Int64() - if err != nil { - visiterr = fmt.Errorf("invalid 'kind' field: %w", err) - } - evt.Kind = int(kind) - case "tags": - evt.Tags, err = fastjsonArrayToTags(v) - if err != nil { - visiterr = fmt.Errorf("invalid '%s' field: %w", key, err) - } - case "content": - id, err := v.StringBytes() - if err != nil { - visiterr = fmt.Errorf("invalid 'content' field: %w", err) - } - evt.Content = string(id) - case "sig": - id, err := v.StringBytes() - if err != nil { - visiterr = fmt.Errorf("invalid 'sig' field: %w", err) - } - evt.Sig = string(id) - default: - var anyValue any - json.Unmarshal(v.MarshalTo([]byte{}), &anyValue) - evt.extra[key] = anyValue - } - }) - return visiterr -} - -// unmarshaling helper -func fastjsonArrayToTags(v *fastjson.Value) (Tags, error) { - arr, err := v.Array() - if err != nil { - return nil, err - } - - sll := make(Tags, len(arr)) - for i, v := range arr { - subarr, err := v.Array() - if err != nil { - return nil, err - } - - sl := make(Tag, len(subarr)) - for j, subv := range subarr { - sb, err := subv.StringBytes() - if err != nil { - return nil, err - } - sl[j] = string(sb) - } - sll[i] = sl - } - - return sll, nil -} - -// MarshalJSON() returns the JSON byte encoding of the event, as in NIP-01. -func (evt Event) MarshalJSON() ([]byte, error) { - dst := make([]byte, 0) - dst = append(dst, '{') - dst = append(dst, []byte(fmt.Sprintf("\"id\":\"%s\",\"pubkey\":\"%s\",\"created_at\":%d,\"kind\":%d,\"tags\":", - evt.ID, - evt.PubKey, - evt.CreatedAt.Unix(), - evt.Kind, - ))...) - dst = evt.Tags.marshalTo(dst) - dst = append(dst, []byte(",\"content\":")...) - dst = escapeString(dst, evt.Content) - dst = append(dst, []byte(fmt.Sprintf(",\"sig\":\"%s\"", - evt.Sig, - ))...) - // slower marshaling of "any" interface type - if evt.extra != nil { - buf := bytes.NewBuffer(nil) - enc := json.NewEncoder(buf) - enc.SetEscapeHTML(false) - for k, v := range evt.extra { - if e := enc.Encode(v); e == nil { - dst = append(dst, ',') - dst = escapeString(dst, k) - dst = append(dst, ':') - dst = append(dst, buf.Bytes()[:buf.Len()-1]...) - } - buf.Reset() - } - } - dst = append(dst, '}') - return dst, nil -} diff --git a/event_easyjson.go b/event_easyjson.go new file mode 100644 index 0000000..93b00cf --- /dev/null +++ b/event_easyjson.go @@ -0,0 +1,199 @@ +// Code generated by easyjson for marshaling/unmarshaling. DO NOT EDIT. + +package nostr + +import ( + json "encoding/json" + + easyjson "github.com/mailru/easyjson" + jlexer "github.com/mailru/easyjson/jlexer" + jwriter "github.com/mailru/easyjson/jwriter" +) + +// suppress unused package warning +var ( + _ *json.RawMessage + _ *jlexer.Lexer + _ *jwriter.Writer + _ easyjson.Marshaler +) + +func easyjsonF642ad3eDecodeGithubComNbdWtfGoNostr(in *jlexer.Lexer, out *Event) { + isTopLevel := in.IsStart() + if in.IsNull() { + if isTopLevel { + in.Consumed() + } + in.Skip() + return + } + in.Delim('{') + for !in.IsDelim('}') { + key := in.UnsafeFieldName(true) + in.WantColon() + if in.IsNull() { + in.Skip() + in.WantComma() + continue + } + switch key { + case "id": + out.ID = in.UnsafeString() + case "pubkey": + out.PubKey = in.UnsafeString() + case "created_at": + out.CreatedAt = Timestamp(in.Int64()) + case "kind": + out.Kind = in.Int() + case "tags": + if in.IsNull() { + in.Skip() + out.Tags = nil + } else { + in.Delim('[') + if out.Tags == nil { + if !in.IsDelim(']') { + out.Tags = make(Tags, 0, 7) + } else { + out.Tags = Tags{} + } + } else { + out.Tags = (out.Tags)[:0] + } + for !in.IsDelim(']') { + var v1 Tag + if in.IsNull() { + in.Skip() + v1 = nil + } else { + in.Delim('[') + if v1 == nil { + if !in.IsDelim(']') { + v1 = make(Tag, 0, 5) + } else { + v1 = Tag{} + } + } else { + v1 = (v1)[:0] + } + for !in.IsDelim(']') { + var v2 string + v2 = string(in.String()) + v1 = append(v1, v2) + in.WantComma() + } + in.Delim(']') + } + out.Tags = append(out.Tags, v1) + in.WantComma() + } + in.Delim(']') + } + case "content": + out.Content = in.UnsafeString() + case "sig": + out.Sig = in.UnsafeString() + default: + out.extra[key] = in.Interface() + } + in.WantComma() + } + in.Delim('}') + if isTopLevel { + in.Consumed() + } +} + +func easyjsonF642ad3eEncodeGithubComNbdWtfGoNostr(out *jwriter.Writer, in Event) { + out.RawByte('{') + first := true + _ = first + { + const prefix string = ",\"id\":" + out.RawString(prefix[1:]) + out.String(in.ID) + } + { + const prefix string = ",\"pubkey\":" + out.RawString(prefix) + out.String(in.PubKey) + } + { + const prefix string = ",\"created_at\":" + out.RawString(prefix) + out.Int64(int64(in.CreatedAt)) + } + { + const prefix string = ",\"kind\":" + out.RawString(prefix) + out.Int(in.Kind) + } + { + const prefix string = ",\"tags\":" + out.RawString(prefix) + if in.Tags == nil && (out.Flags&jwriter.NilSliceAsEmpty) == 0 { + out.RawString("null") + } else { + out.RawByte('[') + for v3, v4 := range in.Tags { + if v3 > 0 { + out.RawByte(',') + } + if v4 == nil && (out.Flags&jwriter.NilSliceAsEmpty) == 0 { + out.RawString("null") + } else { + out.RawByte('[') + for v5, v6 := range v4 { + if v5 > 0 { + out.RawByte(',') + } + out.String(string(v6)) + } + out.RawByte(']') + } + } + out.RawByte(']') + } + } + { + const prefix string = ",\"content\":" + out.RawString(prefix) + out.String(in.Content) + } + { + const prefix string = ",\"sig\":" + out.RawString(prefix) + out.String(in.Sig) + } + { + for key, value := range in.extra { + out.RawString(",\"" + key + "\":") + out.Raw(json.Marshal(value)) + } + } + out.RawByte('}') +} + +// MarshalJSON supports json.Marshaler interface +func (v Event) MarshalJSON() ([]byte, error) { + w := jwriter.Writer{} + easyjsonF642ad3eEncodeGithubComNbdWtfGoNostr(&w, v) + return w.Buffer.BuildBytes(), w.Error +} + +// MarshalEasyJSON supports easyjson.Marshaler interface +func (v Event) MarshalEasyJSON(w *jwriter.Writer) { + easyjsonF642ad3eEncodeGithubComNbdWtfGoNostr(w, v) +} + +// UnmarshalJSON supports json.Unmarshaler interface +func (v *Event) UnmarshalJSON(data []byte) error { + r := jlexer.Lexer{Data: data} + easyjsonF642ad3eDecodeGithubComNbdWtfGoNostr(&r, v) + return r.Error() +} + +// UnmarshalEasyJSON supports easyjson.Unmarshaler interface +func (v *Event) UnmarshalEasyJSON(l *jlexer.Lexer) { + easyjsonF642ad3eDecodeGithubComNbdWtfGoNostr(l, v) +} diff --git a/filter.go b/filter.go index e784600..ca2ff90 100644 --- a/filter.go +++ b/filter.go @@ -2,7 +2,6 @@ package nostr import ( "encoding/json" - "time" "golang.org/x/exp/slices" ) @@ -14,8 +13,8 @@ type Filter struct { Kinds []int Authors []string Tags TagMap - Since *time.Time - Until *time.Time + Since Timestamp + Until Timestamp Limit int Search string } @@ -64,11 +63,11 @@ func (ef Filter) Matches(event *Event) bool { } } - if ef.Since != nil && time.Time(event.CreatedAt).Before(*ef.Since) { + if event.CreatedAt < ef.Since { return false } - if ef.Until != nil && time.Time(event.CreatedAt).After(*ef.Until) { + if ef.Until != 0 && event.CreatedAt > ef.Until { return false } diff --git a/filter_aux.go b/filter_aux.go deleted file mode 100644 index 56740e1..0000000 --- a/filter_aux.go +++ /dev/null @@ -1,170 +0,0 @@ -package nostr - -import ( - "fmt" - "strings" - "time" - - "github.com/valyala/fastjson" -) - -func (f *Filter) UnmarshalJSON(payload []byte) error { - var fastjsonParser fastjson.Parser - parsed, err := fastjsonParser.ParseBytes(payload) - if err != nil { - return fmt.Errorf("failed to parse filter: %w", err) - } - - obj, err := parsed.Object() - if err != nil { - return fmt.Errorf("filter is not an object") - } - - f.Tags = make(TagMap) - - var visiterr error - obj.Visit(func(k []byte, v *fastjson.Value) { - key := string(k) - switch key { - case "ids": - f.IDs, err = fastjsonArrayToStringList(v) - if err != nil { - visiterr = fmt.Errorf("invalid 'ids' field: %w", err) - } - case "kinds": - f.Kinds, err = fastjsonArrayToIntList(v) - if err != nil { - visiterr = fmt.Errorf("invalid 'kinds' field: %w", err) - } - case "authors": - f.Authors, err = fastjsonArrayToStringList(v) - if err != nil { - visiterr = fmt.Errorf("invalid 'authors' field: %w", err) - } - case "since": - val, err := v.Int64() - if err != nil { - visiterr = fmt.Errorf("invalid 'since' field: %w", err) - } - tm := time.Unix(val, 0) - f.Since = &tm - case "until": - val, err := v.Int64() - if err != nil { - visiterr = fmt.Errorf("invalid 'until' field: %w", err) - } - tm := time.Unix(val, 0) - f.Until = &tm - case "limit": - val, err := v.Int() - if err != nil { - visiterr = fmt.Errorf("invalid 'limit' field: %w", err) - } - f.Limit = val - case "search": - val, err := v.StringBytes() - if err != nil { - visiterr = fmt.Errorf("invalid 'search' field: %w", err) - } - f.Search = string(val) - default: - if strings.HasPrefix(key, "#") { - f.Tags[key[1:]], err = fastjsonArrayToStringList(v) - if err != nil { - visiterr = fmt.Errorf("invalid '%s' field: %w", key, err) - } - } - } - }) - if visiterr != nil { - return visiterr - } - - return nil -} - -func (f Filter) MarshalJSON() ([]byte, error) { - var arena fastjson.Arena - - o := arena.NewObject() - - if f.IDs != nil { - o.Set("ids", stringListToFastjsonArray(&arena, f.IDs)) - } - if f.Kinds != nil { - o.Set("kinds", intListToFastjsonArray(&arena, f.Kinds)) - } - if f.Authors != nil { - o.Set("authors", stringListToFastjsonArray(&arena, f.Authors)) - } - if f.Since != nil { - o.Set("since", arena.NewNumberInt(int(f.Since.Unix()))) - } - if f.Until != nil { - o.Set("until", arena.NewNumberInt(int(f.Until.Unix()))) - } - if f.Tags != nil { - for k, v := range f.Tags { - o.Set("#"+k, stringListToFastjsonArray(&arena, v)) - } - } - if f.Limit != 0 { - o.Set("limit", arena.NewNumberInt(f.Limit)) - } - if f.Search != "" { - o.Set("search", arena.NewString(f.Search)) - } - - return o.MarshalTo(nil), nil -} - -func stringListToFastjsonArray(arena *fastjson.Arena, sl []string) *fastjson.Value { - arr := arena.NewArray() - for i, v := range sl { - arr.SetArrayItem(i, arena.NewString(v)) - } - return arr -} - -func intListToFastjsonArray(arena *fastjson.Arena, il []int) *fastjson.Value { - arr := arena.NewArray() - for i, v := range il { - arr.SetArrayItem(i, arena.NewNumberInt(v)) - } - return arr -} - -func fastjsonArrayToStringList(v *fastjson.Value) ([]string, error) { - arr, err := v.Array() - if err != nil { - return nil, err - } - - sl := make([]string, len(arr)) - for i, v := range arr { - sb, err := v.StringBytes() - if err != nil { - return nil, err - } - sl[i] = string(sb) - } - - return sl, nil -} - -func fastjsonArrayToIntList(v *fastjson.Value) ([]int, error) { - arr, err := v.Array() - if err != nil { - return nil, err - } - - il := make([]int, len(arr)) - for i, v := range arr { - il[i], err = v.Int() - if err != nil { - return nil, err - } - } - - return il, nil -} diff --git a/filter_easyjson.go b/filter_easyjson.go new file mode 100644 index 0000000..fd80947 --- /dev/null +++ b/filter_easyjson.go @@ -0,0 +1,292 @@ +// Code generated by easyjson for marshaling/unmarshaling. DO NOT EDIT. + +package nostr + +import ( + json "encoding/json" + easyjson "github.com/mailru/easyjson" + jlexer "github.com/mailru/easyjson/jlexer" + jwriter "github.com/mailru/easyjson/jwriter" +) + +// suppress unused package warning +var ( + _ *json.RawMessage + _ *jlexer.Lexer + _ *jwriter.Writer + _ easyjson.Marshaler +) + +func easyjson4d398eaaDecodeGithubComNbdWtfGoNostr(in *jlexer.Lexer, out *Filter) { + isTopLevel := in.IsStart() + if in.IsNull() { + if isTopLevel { + in.Consumed() + } + in.Skip() + return + } + in.Delim('{') + for !in.IsDelim('}') { + key := in.UnsafeFieldName(false) + in.WantColon() + if in.IsNull() { + in.Skip() + in.WantComma() + continue + } + switch key { + case "IDs": + if in.IsNull() { + in.Skip() + out.IDs = nil + } else { + in.Delim('[') + if out.IDs == nil { + if !in.IsDelim(']') { + out.IDs = make([]string, 0, 4) + } else { + out.IDs = []string{} + } + } else { + out.IDs = (out.IDs)[:0] + } + for !in.IsDelim(']') { + var v1 string + v1 = string(in.String()) + out.IDs = append(out.IDs, v1) + in.WantComma() + } + in.Delim(']') + } + case "Kinds": + if in.IsNull() { + in.Skip() + out.Kinds = nil + } else { + in.Delim('[') + if out.Kinds == nil { + if !in.IsDelim(']') { + out.Kinds = make([]int, 0, 8) + } else { + out.Kinds = []int{} + } + } else { + out.Kinds = (out.Kinds)[:0] + } + for !in.IsDelim(']') { + var v2 int + v2 = int(in.Int()) + out.Kinds = append(out.Kinds, v2) + in.WantComma() + } + in.Delim(']') + } + case "Authors": + if in.IsNull() { + in.Skip() + out.Authors = nil + } else { + in.Delim('[') + if out.Authors == nil { + if !in.IsDelim(']') { + out.Authors = make([]string, 0, 4) + } else { + out.Authors = []string{} + } + } else { + out.Authors = (out.Authors)[:0] + } + for !in.IsDelim(']') { + var v3 string + v3 = string(in.String()) + out.Authors = append(out.Authors, v3) + in.WantComma() + } + in.Delim(']') + } + case "Tags": + if in.IsNull() { + in.Skip() + } else { + in.Delim('{') + out.Tags = make(TagMap) + for !in.IsDelim('}') { + key := string(in.String()) + in.WantColon() + var v4 []string + if in.IsNull() { + in.Skip() + v4 = nil + } else { + in.Delim('[') + if v4 == nil { + if !in.IsDelim(']') { + v4 = make([]string, 0, 4) + } else { + v4 = []string{} + } + } else { + v4 = (v4)[:0] + } + for !in.IsDelim(']') { + var v5 string + v5 = string(in.String()) + v4 = append(v4, v5) + in.WantComma() + } + in.Delim(']') + } + (out.Tags)[key] = v4 + in.WantComma() + } + in.Delim('}') + } + case "Since": + out.Since = Timestamp(in.Int64()) + case "Until": + out.Until = Timestamp(in.Int64()) + case "Limit": + out.Limit = int(in.Int()) + case "Search": + out.Search = string(in.String()) + default: + in.SkipRecursive() + } + in.WantComma() + } + in.Delim('}') + if isTopLevel { + in.Consumed() + } +} +func easyjson4d398eaaEncodeGithubComNbdWtfGoNostr(out *jwriter.Writer, in Filter) { + out.RawByte('{') + first := true + _ = first + { + const prefix string = ",\"IDs\":" + out.RawString(prefix[1:]) + if in.IDs == nil && (out.Flags&jwriter.NilSliceAsEmpty) == 0 { + out.RawString("null") + } else { + out.RawByte('[') + for v6, v7 := range in.IDs { + if v6 > 0 { + out.RawByte(',') + } + out.String(string(v7)) + } + out.RawByte(']') + } + } + { + const prefix string = ",\"Kinds\":" + out.RawString(prefix) + if in.Kinds == nil && (out.Flags&jwriter.NilSliceAsEmpty) == 0 { + out.RawString("null") + } else { + out.RawByte('[') + for v8, v9 := range in.Kinds { + if v8 > 0 { + out.RawByte(',') + } + out.Int(int(v9)) + } + out.RawByte(']') + } + } + { + const prefix string = ",\"Authors\":" + out.RawString(prefix) + if in.Authors == nil && (out.Flags&jwriter.NilSliceAsEmpty) == 0 { + out.RawString("null") + } else { + out.RawByte('[') + for v10, v11 := range in.Authors { + if v10 > 0 { + out.RawByte(',') + } + out.String(string(v11)) + } + out.RawByte(']') + } + } + { + const prefix string = ",\"Tags\":" + out.RawString(prefix) + if in.Tags == nil && (out.Flags&jwriter.NilMapAsEmpty) == 0 { + out.RawString(`null`) + } else { + out.RawByte('{') + v12First := true + for v12Name, v12Value := range in.Tags { + if v12First { + v12First = false + } else { + out.RawByte(',') + } + out.String(string(v12Name)) + out.RawByte(':') + if v12Value == nil && (out.Flags&jwriter.NilSliceAsEmpty) == 0 { + out.RawString("null") + } else { + out.RawByte('[') + for v13, v14 := range v12Value { + if v13 > 0 { + out.RawByte(',') + } + out.String(string(v14)) + } + out.RawByte(']') + } + } + out.RawByte('}') + } + } + { + const prefix string = ",\"Since\":" + out.RawString(prefix) + out.Int64(int64(in.Since)) + } + { + const prefix string = ",\"Until\":" + out.RawString(prefix) + out.Int64(int64(in.Until)) + } + { + const prefix string = ",\"Limit\":" + out.RawString(prefix) + out.Int(int(in.Limit)) + } + { + const prefix string = ",\"Search\":" + out.RawString(prefix) + out.String(string(in.Search)) + } + out.RawByte('}') +} + +// MarshalJSON supports json.Marshaler interface +func (v Filter) MarshalJSON() ([]byte, error) { + w := jwriter.Writer{} + easyjson4d398eaaEncodeGithubComNbdWtfGoNostr(&w, v) + return w.Buffer.BuildBytes(), w.Error +} + +// MarshalEasyJSON supports easyjson.Marshaler interface +func (v Filter) MarshalEasyJSON(w *jwriter.Writer) { + easyjson4d398eaaEncodeGithubComNbdWtfGoNostr(w, v) +} + +// UnmarshalJSON supports json.Unmarshaler interface +func (v *Filter) UnmarshalJSON(data []byte) error { + r := jlexer.Lexer{Data: data} + easyjson4d398eaaDecodeGithubComNbdWtfGoNostr(&r, v) + return r.Error() +} + +// UnmarshalEasyJSON supports easyjson.Unmarshaler interface +func (v *Filter) UnmarshalEasyJSON(l *jlexer.Lexer) { + easyjson4d398eaaDecodeGithubComNbdWtfGoNostr(l, v) +} diff --git a/go.mod b/go.mod index 50a8741..d4b97bc 100644 --- a/go.mod +++ b/go.mod @@ -7,6 +7,7 @@ require ( github.com/btcsuite/btcd/btcec/v2 v2.2.0 github.com/btcsuite/btcd/btcutil v1.1.3 github.com/gorilla/websocket v1.4.2 + github.com/mailru/easyjson v0.7.7 github.com/tyler-smith/go-bip32 v1.0.0 github.com/tyler-smith/go-bip39 v1.1.0 github.com/valyala/fastjson v1.6.3 @@ -20,5 +21,6 @@ require ( github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 // indirect github.com/decred/dcrd/crypto/blake256 v1.0.0 // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect + github.com/josharian/intern v1.0.0 // indirect golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 // indirect ) diff --git a/go.sum b/go.sum index c96b8f9..d1e5afa 100644 --- a/go.sum +++ b/go.sum @@ -57,8 +57,12 @@ github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/ad github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= +github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=