feat(message): add JSON marshal/unmarshal for Message struct

Ayman Bagabas created

Change summary

internal/message/content.go | 46 +++++++++++++++++++++++++++++++++++++++
1 file changed, 46 insertions(+)

Detailed changes

internal/message/content.go 🔗

@@ -2,6 +2,7 @@ package message
 
 import (
 	"encoding/base64"
+	"encoding/json"
 	"slices"
 	"time"
 
@@ -142,6 +143,51 @@ type Message struct {
 	UpdatedAt int64         `json:"updated_at"`
 }
 
+// MarshalJSON implements the [json.Marshaler] interface.
+func (m Message) MarshalJSON() ([]byte, error) {
+	// We need to handle the Parts specially since they're ContentPart interfaces
+	// which can't be directly marshaled by the standard JSON package.
+	parts, err := marshallParts(m.Parts)
+	if err != nil {
+		return nil, err
+	}
+
+	// Create an alias to avoid infinite recursion
+	type Alias Message
+	return json.Marshal(&struct {
+		Parts json.RawMessage `json:"parts"`
+		*Alias
+	}{
+		Parts: json.RawMessage(parts),
+		Alias: (*Alias)(&m),
+	})
+}
+
+// UnmarshalJSON implements the [json.Unmarshaler] interface.
+func (m *Message) UnmarshalJSON(data []byte) error {
+	// Create an alias to avoid infinite recursion
+	type Alias Message
+	aux := &struct {
+		Parts json.RawMessage `json:"parts"`
+		*Alias
+	}{
+		Alias: (*Alias)(m),
+	}
+
+	if err := json.Unmarshal(data, &aux); err != nil {
+		return err
+	}
+
+	// Unmarshal the parts using our custom function
+	parts, err := unmarshallParts([]byte(aux.Parts))
+	if err != nil {
+		return err
+	}
+
+	m.Parts = parts
+	return nil
+}
+
 func (m *Message) Content() TextContent {
 	for _, part := range m.Parts {
 		if c, ok := part.(TextContent); ok {