package protosocket import ( "context" "crypto/rand" "encoding/hex" "fmt" "sync" ) type HandlerFunc func(context.Context, Envelope) Envelope type Dispatcher struct { mu sync.RWMutex handlers map[string]HandlerFunc } func NewDispatcher() *Dispatcher { return &Dispatcher{handlers: make(map[string]HandlerFunc)} } func (d *Dispatcher) Register(action string, handler HandlerFunc) { d.mu.Lock() defer d.mu.Unlock() d.handlers[action] = handler } func (d *Dispatcher) Dispatch(ctx context.Context, env Envelope) Envelope { d.mu.RLock() handler, ok := d.handlers[env.Action] d.mu.RUnlock() if !ok { return ErrorResponse(env, "UNSUPPORTED_ACTION", fmt.Sprintf("unsupported action: %s", env.Action), false) } return handler(ctx, env) } func SuccessResponse(req Envelope, payload map[string]any) Envelope { return Envelope{ ProtocolVersion: ProtocolVersion, ID: generateID(), CorrelationID: req.ID, Type: "response", Channel: req.Channel, Action: req.Action, Payload: payload, } } func ErrorResponse(req Envelope, code, message string, retryable bool) Envelope { return Envelope{ ProtocolVersion: ProtocolVersion, ID: generateID(), CorrelationID: req.ID, Type: "error", Channel: req.Channel, Action: req.Action, Error: &EnvelopeError{ Code: code, Message: message, Retryable: retryable, }, } } func NewEventEnvelope(action string, payload map[string]any) Envelope { return Envelope{ ProtocolVersion: ProtocolVersion, ID: generateID(), Type: "event", Channel: "event", Action: action, Payload: payload, } } func generateID() string { b := make([]byte, 16) if _, err := rand.Read(b); err != nil { return "id-fallback" } return hex.EncodeToString(b) }