v1.4.0: generic Stream[T] — typed stream messages, drop (T, *Stream, error)
- Stream[T]: Send(v T) type-safe; message type recovered from the instantiated Send signature (reflect has no type-args API) - method signatures reduced to (error), (T, error), (*Stream[T], error); the (T, *Stream, error) first-message form is gone — send it via Send - return T and stream message T now run checkType at bind time (Stream[any] remains the explicit untyped escape hatch) - generated clients: TS onMessage: (data: T); Go <-chan T - fix vacuous panic assertions (fmt.Sprint(nil) -> "<nil>") in hardening/route test catch helpers Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
@@ -2,13 +2,31 @@ package fun
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// streamMsgType 提取 *Stream[T] 的消息类型 T;非 Stream 指针返回 nil。
|
||||
// reflect 没有类型实参 API,但实例化类型的 Send 方法签名已替换——In(1) 即 T
|
||||
func streamMsgType(t reflect.Type) reflect.Type {
|
||||
if t.Kind() != reflect.Ptr || !strings.HasPrefix(t.Elem().Name(), "Stream[") {
|
||||
return nil
|
||||
}
|
||||
sm, ok := t.MethodByName("Send")
|
||||
if !ok || sm.Type.NumIn() != 2 {
|
||||
return nil
|
||||
}
|
||||
if _, ok := t.MethodByName("Inject"); !ok {
|
||||
return nil
|
||||
}
|
||||
return sm.Type.In(1)
|
||||
}
|
||||
|
||||
// Stream 流式响应的业务句柄。
|
||||
// 业务方法返回 *Stream 后,框架调用 Inject 注入推送通道;
|
||||
// 业务方法返回 *Stream[T] 后,框架调用 Inject 注入推送通道;
|
||||
// 未注入时 Send/Close 自动阻塞等待,避免业务 goroutine 与注入之间的竞态。
|
||||
type Stream struct {
|
||||
type Stream[T any] struct {
|
||||
mu sync.Mutex
|
||||
once sync.Once
|
||||
ready chan struct{}
|
||||
@@ -18,7 +36,7 @@ type Stream struct {
|
||||
onClose func()
|
||||
}
|
||||
|
||||
func (s *Stream) getReady() chan struct{} {
|
||||
func (s *Stream[T]) getReady() chan struct{} {
|
||||
s.once.Do(func() {
|
||||
if s.ready == nil {
|
||||
s.ready = make(chan struct{})
|
||||
@@ -28,14 +46,14 @@ func (s *Stream) getReady() chan struct{} {
|
||||
}
|
||||
|
||||
// Inject 注入推送通道与结束信号,由框架在方法返回后调用
|
||||
func (s *Stream) Inject(ch chan any, done chan struct{}) {
|
||||
func (s *Stream[T]) Inject(ch chan any, done chan struct{}) {
|
||||
s.ch = ch
|
||||
s.done = done
|
||||
close(s.getReady())
|
||||
}
|
||||
|
||||
// Send 推送一条消息;连接断开或流已关闭时返回错误
|
||||
func (s *Stream) Send(message any) error {
|
||||
func (s *Stream[T]) Send(message T) error {
|
||||
<-s.getReady()
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
@@ -51,7 +69,7 @@ func (s *Stream) Send(message any) error {
|
||||
}
|
||||
|
||||
// Close 主动结束流,触发 OnClose 回调
|
||||
func (s *Stream) Close() {
|
||||
func (s *Stream[T]) Close() {
|
||||
<-s.getReady()
|
||||
s.mu.Lock()
|
||||
if s.closed {
|
||||
@@ -67,10 +85,10 @@ func (s *Stream) Close() {
|
||||
close(s.ch)
|
||||
}
|
||||
|
||||
// OnClose 注册关闭回调;可在业务方法 return *Stream 之前同步调用。
|
||||
// OnClose 注册关闭回调;可在业务方法 return *Stream[T] 之前同步调用。
|
||||
// 不得等待 Inject:Inject 只有业务方法返回后才发生,等待会形成循环依赖死锁。
|
||||
// 流已关闭时在锁外立即执行回调,避免回调重入 Stream 时自锁。
|
||||
func (s *Stream) OnClose(cb func()) {
|
||||
func (s *Stream[T]) OnClose(cb func()) {
|
||||
if cb == nil {
|
||||
return
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user