From 657c6edf6c52650e5f5066b9ae63f49c500112e4 Mon Sep 17 00:00:00 2001
From: fiatjaf <fiatjaf@gmail.com>
Date: Mon, 10 Mar 2025 17:33:32 -0300
Subject: [PATCH] remove sonic from the default build, guard it under "sonic"
 tag for now.

---
 .github/workflows/test.yml |  2 +-
 envelopes.go               |  7 ++++-
 envelopes_default.go       | 54 ++++++++++++++++++++++++++++++++++++++
 envelopes_sonic.go         | 10 +++++--
 4 files changed, 69 insertions(+), 4 deletions(-)
 create mode 100644 envelopes_default.go

diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 14827b9..9877a4c 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -18,4 +18,4 @@ jobs:
         with:
           go-version-file: ./go.mod
 
-      - run: go test ./...
+      - run: go test ./... -tags=sonic
diff --git a/envelopes.go b/envelopes.go
index c838e4e..c75402c 100644
--- a/envelopes.go
+++ b/envelopes.go
@@ -26,7 +26,12 @@ var (
 	UnknownLabel = errors.New("unknown envelope label")
 )
 
-// ParseMessage parses a message into an Envelope.
+type MessageParser interface {
+	// ParseMessage parses a message into an Envelope.
+	ParseMessage([]byte) (Envelope, error)
+}
+
+// Deprecated: use NewMessageParser instead
 func ParseMessage(message []byte) Envelope {
 	firstComma := bytes.Index(message, []byte{','})
 	if firstComma == -1 {
diff --git a/envelopes_default.go b/envelopes_default.go
new file mode 100644
index 0000000..379e647
--- /dev/null
+++ b/envelopes_default.go
@@ -0,0 +1,54 @@
+//go:build !sonic
+
+package nostr
+
+import (
+	"bytes"
+	"errors"
+)
+
+func NewMessageParser() MessageParser {
+	return messageParser{}
+}
+
+type messageParser struct{}
+
+func (messageParser) ParseMessage(message []byte) (Envelope, error) {
+	firstComma := bytes.Index(message, []byte{','})
+	if firstComma == -1 {
+		return nil, errors.New("malformed json")
+	}
+	label := message[0:firstComma]
+
+	var v Envelope
+	switch {
+	case bytes.Contains(label, labelEvent):
+		v = &EventEnvelope{}
+	case bytes.Contains(label, labelReq):
+		v = &ReqEnvelope{}
+	case bytes.Contains(label, labelCount):
+		v = &CountEnvelope{}
+	case bytes.Contains(label, labelNotice):
+		x := NoticeEnvelope("")
+		v = &x
+	case bytes.Contains(label, labelEose):
+		x := EOSEEnvelope("")
+		v = &x
+	case bytes.Contains(label, labelOk):
+		v = &OKEnvelope{}
+	case bytes.Contains(label, labelAuth):
+		v = &AuthEnvelope{}
+	case bytes.Contains(label, labelClosed):
+		v = &ClosedEnvelope{}
+	case bytes.Contains(label, labelClose):
+		x := CloseEnvelope("")
+		v = &x
+	default:
+		return nil, UnknownLabel
+	}
+
+	if err := v.UnmarshalJSON(message); err != nil {
+		return nil, err
+	}
+	return v, nil
+}
diff --git a/envelopes_sonic.go b/envelopes_sonic.go
index 0ff6942..369cc9a 100644
--- a/envelopes_sonic.go
+++ b/envelopes_sonic.go
@@ -1,3 +1,5 @@
+//go:build sonic
+
 package nostr
 
 import (
@@ -360,6 +362,8 @@ func (sv *sonicVisitor) OnString(v string) error {
 			sv.whereWeAre = inClosed
 		case "NOTICE":
 			sv.whereWeAre = inNotice
+		default:
+			return UnknownLabel
 		}
 
 		// in an envelope
@@ -483,9 +487,9 @@ type sonicMessageParser struct {
 	reusableIntArray    []int
 }
 
-// NewSonicMessageParser returns a sonicMessageParser object that is intended to be reused many times.
+// NewMessageParser returns a sonicMessageParser object that is intended to be reused many times.
 // It is not goroutine-safe.
-func NewSonicMessageParser() sonicMessageParser {
+func NewMessageParser() sonicMessageParser {
 	return sonicMessageParser{
 		reusableFilterArray: make([]Filter, 0, 1000),
 		reusableTagArray:    make([]Tag, 0, 10000),
@@ -494,6 +498,8 @@ func NewSonicMessageParser() sonicMessageParser {
 	}
 }
 
+var NewSonicMessageParser = NewMessageParser
+
 func (smp *sonicMessageParser) doneWithFilterSlice(slice []Filter) {
 	if unsafe.SliceData(smp.reusableFilterArray) == unsafe.SliceData(slice) {
 		smp.reusableFilterArray = slice[len(slice):]