From 274bd84d12e1cea16f358bf906d0da9ec2da4204 Mon Sep 17 00:00:00 2001
From: a <a@tuxpa.in>
Date: Mon, 4 Dec 2023 11:48:42 -0600
Subject: [PATCH] tests passed

---
 pkg/jsonrpc/message.go | 59 ++++++++++++++++++++++++++++
 pkg/server/server.go   | 87 ++++++++++++++++++------------------------
 2 files changed, 96 insertions(+), 50 deletions(-)
 create mode 100644 pkg/jsonrpc/message.go

diff --git a/pkg/jsonrpc/message.go b/pkg/jsonrpc/message.go
new file mode 100644
index 0000000..dfaeacb
--- /dev/null
+++ b/pkg/jsonrpc/message.go
@@ -0,0 +1,59 @@
+package jsonrpc
+
+import (
+	"encoding/json"
+	"io"
+
+	"github.com/go-faster/jx"
+)
+
+// MessageStream is a writer used to write jsonrpc message to a stream
+type MessageStream struct {
+	w  io.Writer
+	jx *jx.Writer
+}
+
+func NewStream(w io.Writer) (*MessageStream, error) {
+	enc := jx.GetWriter()
+	defer jx.PutWriter(enc)
+	enc.Grow(4096)
+	enc.ResetWriter(w)
+	enc.ObjStart()
+	enc.FieldStart("jsonrpc")
+	enc.Str("2.0")
+	enc.Close()
+	return &MessageStream{
+		w:  w,
+		jx: enc,
+	}, nil
+}
+
+func (m *MessageStream) Field(name string, value json.RawMessage) error {
+	m.jx.ResetWriter(m.w)
+	m.jx.Comma()
+	m.jx.FieldStart(name)
+	m.jx.Raw(value)
+	return m.jx.Close()
+}
+
+// Result returns a writecloser that writes to a result field
+func (m *MessageStream) Result() (io.Writer, error) {
+	m.jx.ResetWriter(m.w)
+	m.jx.Comma()
+	m.jx.FieldStart("result")
+	m.jx.Close()
+	return &MessageWriter{w: m.w}, nil
+}
+
+func (m *MessageStream) Close() error {
+	_, err := m.w.Write([]byte("}"))
+	return err
+}
+
+type MessageWriter struct {
+	w io.Writer
+}
+
+func (m *MessageWriter) Write(p []byte) (n int, err error) {
+	return m.w.Write(p)
+}
diff --git a/pkg/server/server.go b/pkg/server/server.go
index ac33a89..67ea3ed 100644
--- a/pkg/server/server.go
+++ b/pkg/server/server.go
@@ -294,63 +294,50 @@ type callEnv struct {
 
 func (c *callResponder) send(ctx context.Context, env *callEnv) (err error) {
 	w := c.remote
-	enc := jx.GetWriter()
-	defer jx.PutWriter(enc)
-	enc.Grow(4096)
-	enc.ResetWriter(w)
-	enc.ObjStart()
-	enc.FieldStart("jsonrpc")
-	enc.Str("2.0")
+	s, err := jsonrpc.NewStream(w)
+	if err != nil {
+		return err
+	}
+	defer s.Close()
 	if env.id != nil {
-		enc.Comma()
-		enc.FieldStart("id")
-		enc.Raw(env.id.RawMessage())
+		s.Field("id", env.id.RawMessage())
 	}
 	if env.err != nil {
-		enc.Comma()
-		enc.FieldStart("error")
-		enc.Raw(jsonrpc.MarshalError(env.err))
-	} else {
-		// if there is no error, we try to marshal the result
-		enc.Comma()
-		enc.FieldStart("result")
-		enc.Close()
-		enc.ResetWriter(w)
-		if env.v != nil {
-			switch cast := (env.v).(type) {
-			case json.RawMessage:
-				if len(cast) == 0 {
-					enc.Null()
-				} else {
-					enc.Raw(cast)
-				}
-			case *io.PipeReader:
-				_, err := io.Copy(w, cast)
-				if err != nil {
-					return err
-				}
-				cast.Close()
-			case func(e io.Writer) error:
-				err = cast(w)
-			case func(e *jx.Writer) error:
-				err = cast(enc)
-			default:
-				err = jjson.Encode(w, cast)
-			}
-		} else {
-			enc.Null()
-		}
-	}
-	if env.err == nil && err != nil {
-		enc.Comma()
-		enc.FieldStart("error")
-		enc.Raw(jsonrpc.MarshalError(err))
+		s.Field("error", jsonrpc.MarshalError(env.err))
+		return nil
 	}
-	enc.ObjEnd()
-	err = enc.Close()
+	// if there is no error, we try to marshal the result
+	wr, err := s.Result()
 	if err != nil {
 		return err
 	}
+	if env.v == nil {
+		_, err := wr.Write(jsonrpc.Null)
+		if err != nil {
+			return err
+		}
+		return nil
+	}
+	switch cast := (env.v).(type) {
+	case json.RawMessage:
+		if len(cast) == 0 {
+		} else {
+			_, err := wr.Write(cast)
+			if err != nil {
+				return err
+			}
+		}
+	case *io.PipeReader:
+		_, err := io.Copy(wr, cast)
+		if err != nil {
+			return err
+		}
+		cast.Close()
+	case func(e io.Writer) error:
+		err = cast(wr)
+	default:
+		err = jjson.Encode(w, cast)
+	}
 	return nil
 }
 
-- 
GitLab