events.go

  1package pubsub
  2
  3import (
  4	"context"
  5	"encoding/json"
  6	"fmt"
  7	"log/slog"
  8
  9	"github.com/charmbracelet/crush/internal/proto"
 10)
 11
 12const (
 13	CreatedEvent EventType = "created"
 14	UpdatedEvent EventType = "updated"
 15	DeletedEvent EventType = "deleted"
 16)
 17
 18type Suscriber[T any] interface {
 19	Subscribe(context.Context) <-chan Event[T]
 20}
 21
 22type (
 23	PayloadType = string
 24
 25	Payload struct {
 26		Type    PayloadType     `json:"type"`
 27		Payload json.RawMessage `json:"payload"`
 28	}
 29
 30	// EventType identifies the type of event
 31	EventType string
 32
 33	// Event represents an event in the lifecycle of a resource
 34	Event[T any] struct {
 35		Type    EventType `json:"type"`
 36		Payload T         `json:"payload"`
 37	}
 38
 39	Publisher[T any] interface {
 40		Publish(EventType, T)
 41	}
 42)
 43
 44const (
 45	PayloadTypeLSPEvent               PayloadType = "lsp_event"
 46	PayloadTypeMCPEvent               PayloadType = "mcp_event"
 47	PayloadTypePermissionRequest      PayloadType = "permission_request"
 48	PayloadTypePermissionNotification PayloadType = "permission_notification"
 49	PayloadTypeMessage                PayloadType = "message"
 50	PayloadTypeSession                PayloadType = "session"
 51	PayloadTypeFile                   PayloadType = "file"
 52	PayloadTypeAgentEvent             PayloadType = "agent_event"
 53)
 54
 55func (t EventType) MarshalText() ([]byte, error) {
 56	return []byte(t), nil
 57}
 58
 59func (t *EventType) UnmarshalText(data []byte) error {
 60	*t = EventType(data)
 61	return nil
 62}
 63
 64func (e Event[T]) MarshalJSON() ([]byte, error) {
 65	type Alias Event[T]
 66
 67	var (
 68		typ string
 69		bts []byte
 70		err error
 71	)
 72	switch any(e.Payload).(type) {
 73	case proto.LSPEvent:
 74		typ = "lsp_event"
 75		bts, err = json.Marshal(e.Payload)
 76	case proto.MCPEvent:
 77		typ = "mcp_event"
 78		bts, err = json.Marshal(e.Payload)
 79	case proto.PermissionRequest:
 80		typ = "permission_request"
 81		bts, err = json.Marshal(e.Payload)
 82	case proto.PermissionNotification:
 83		typ = "permission_notification"
 84		bts, err = json.Marshal(e.Payload)
 85	case proto.Message:
 86		typ = "message"
 87		bts, err = json.Marshal(e.Payload)
 88	case proto.Session:
 89		typ = "session"
 90		bts, err = json.Marshal(e.Payload)
 91	case proto.File:
 92		typ = "file"
 93		bts, err = json.Marshal(e.Payload)
 94	case proto.AgentEvent:
 95		typ = "agent_event"
 96		bts, err = json.Marshal(e.Payload)
 97	default:
 98		panic(fmt.Sprintf("unknown payload type: %T", e.Payload))
 99	}
100
101	if err != nil {
102		return nil, err
103	}
104
105	p, err := json.Marshal(&Payload{
106		Type:    typ,
107		Payload: bts,
108	})
109	if err != nil {
110		return nil, err
111	}
112
113	b, err := json.Marshal(&struct {
114		Payload json.RawMessage `json:"payload"`
115		*Alias
116	}{
117		Payload: json.RawMessage(p),
118		Alias:   (*Alias)(&e),
119	})
120
121	// slog.Info("marshalled event", "event", fmt.Sprintf("%q", string(b)))
122
123	return b, err
124}
125
126func (e *Event[T]) UnmarshalJSON(data []byte) error {
127	// slog.Info("unmarshalling event", "data", fmt.Sprintf("%q", string(data)))
128
129	type Alias Event[T]
130	aux := &struct {
131		Payload json.RawMessage `json:"payload"`
132		*Alias
133	}{
134		Alias: (*Alias)(e),
135	}
136
137	if err := json.Unmarshal(data, &aux); err != nil {
138		return err
139	}
140
141	e.Type = aux.Type
142
143	slog.Info("unmarshalled event payload", "aux", fmt.Sprintf("%q", aux.Payload))
144
145	var wp Payload
146	if err := json.Unmarshal(aux.Payload, &wp); err != nil {
147		return err
148	}
149
150	var pl any
151	switch wp.Type {
152	case "lsp_event":
153		var p proto.LSPEvent
154		if err := json.Unmarshal(wp.Payload, &p); err != nil {
155			return err
156		}
157		pl = p
158	case "mcp_event":
159		var p proto.MCPEvent
160		if err := json.Unmarshal(wp.Payload, &p); err != nil {
161			return err
162		}
163		pl = p
164	case "permission_request":
165		var p proto.PermissionRequest
166		if err := json.Unmarshal(wp.Payload, &p); err != nil {
167			return err
168		}
169		pl = p
170	case "permission_notification":
171		var p proto.PermissionNotification
172		if err := json.Unmarshal(wp.Payload, &p); err != nil {
173			return err
174		}
175		pl = p
176	case "message":
177		var p proto.Message
178		if err := json.Unmarshal(wp.Payload, &p); err != nil {
179			return err
180		}
181		pl = p
182	case "session":
183		var p proto.Session
184		if err := json.Unmarshal(wp.Payload, &p); err != nil {
185			return err
186		}
187		pl = p
188	case "file":
189		var p proto.File
190		if err := json.Unmarshal(wp.Payload, &p); err != nil {
191			return err
192		}
193		pl = p
194	case "agent_event":
195		var p proto.AgentEvent
196		if err := json.Unmarshal(wp.Payload, &p); err != nil {
197			return err
198		}
199		pl = p
200	default:
201		panic(fmt.Sprintf("unknown payload type: %q", wp.Type))
202	}
203
204	e.Payload = T(pl.(T))
205
206	return nil
207}