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}