go-nostr/event_aux.go

143 lines
3.2 KiB
Go
Raw Permalink Normal View History

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
}
2022-11-11 10:08:36 -03:00
sll := make(Tags, len(arr))
for i, v := range arr {
subarr, err := v.Array()
if err != nil {
return nil, err
}
2022-11-11 10:08:36 -03:00
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
}
2022-11-11 10:08:36 -03:00
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
}