BREAKING CHANGES: - Guard interface: Guard(ctx Ctx) error; returning error short-circuits (subsequent guards and the method no longer execute) - Wired[T]() (*T, error); New() may return error; failed wiring is sticky - BindService/BindGuard/BindRoute return error; routes accept guards (guards receive merged query/form params as Ctx.State) - registration panics after Start; Ctx.Ip honors X-Forwarded-For/X-Real-IP - internal errors sanitized to fixed client messages FIXES: - int64 precision loss: /cell data decoded from raw JSON bytes and responses serialized with json.Number (no float64 round-trip) - enum values range-checked before uint8 conversion (256 no longer truncates to 0 and slips through) - logger: files failing name parsing are no longer deleted; log channel never blocks request goroutines; ConfigLogger is race-free; logWriterWorker unlock bug fixed - stream writer panics recovered (process no longer crashes); streamDone closed exactly once - generated Go client emits definitions for pointer-to-enum/struct fields - anonymous structs rejected at registration; isPrivate safe on empty names - removed dead Result.Id and RequestInfo.Type ADDITIONS: - graceful shutdown (Shutdown), StartOn, server timeouts by default (Read 60s/Idle 120s/Write off), SetTimeouts/SetMaxConcurrency - big-int-safe JSON in the generated TS client (>2^53 as BigInt) - example/ demo services and cmd/genexample artifact generator
223 lines
6.4 KiB
Go
223 lines
6.4 KiB
Go
package fun
|
|
|
|
import (
|
|
"fmt"
|
|
"reflect"
|
|
)
|
|
|
|
// boxEntry 依赖容器条目:装配完成的单例,或粘性初始化错误。
|
|
// 装配失败的类型记录错误后不再重试,也不把半初始化实例暴露给后续装配
|
|
type boxEntry struct {
|
|
val reflect.Value
|
|
err error
|
|
}
|
|
|
|
// Wired 创建并注册一个依赖实例;auto 标签字段递归注入依赖,存在 New() 则调用。
|
|
// New 支持 () 与 () error 两种签名,返回非 nil error 即装配失败。
|
|
// 失败以 error 返回;同类型再次 Wired 返回同一错误(粘性),避免启动期反复重连
|
|
func Wired[T any]() (*T, error) {
|
|
t := reflect.TypeFor[T]()
|
|
if t.Kind() != reflect.Struct {
|
|
panic("Fun: " + t.Name() + " It must be a structure")
|
|
}
|
|
if t.Name() == "" {
|
|
panic("Fun: Wired requires a named struct type")
|
|
}
|
|
if isPrivate(t.Name()) {
|
|
panic("Fun:" + t.Name() + " cannot be Private")
|
|
}
|
|
pt := reflect.TypeFor[*T]()
|
|
checkNewSignature(pt, t.Name())
|
|
f := GetFun()
|
|
f.mu.Lock()
|
|
defer f.mu.Unlock()
|
|
if entry, ok := f.boxes.Load(pt); ok {
|
|
e := entry.(boxEntry)
|
|
if e.err != nil {
|
|
return nil, e.err
|
|
}
|
|
return e.val.Interface().(*T), nil
|
|
}
|
|
data := new(T)
|
|
v := reflect.ValueOf(data)
|
|
// 先入容器再装配:循环依赖(A→B→A)靠占位引用解开;
|
|
// 失败时下面覆盖为粘性错误,容器中不留可用半成品
|
|
f.boxes.Store(pt, boxEntry{val: v})
|
|
if err := f.wireStruct(v.Elem()); err != nil {
|
|
f.boxes.Store(pt, boxEntry{err: err})
|
|
return nil, err
|
|
}
|
|
if err := callNewIfPresent(v); err != nil {
|
|
f.boxes.Store(pt, boxEntry{err: err})
|
|
return nil, err
|
|
}
|
|
return data, nil
|
|
}
|
|
|
|
// wireStruct 注入 auto 标签字段;依赖缺失时递归装配(须持有 f.mu)
|
|
func (f *Fun) wireStruct(structValue reflect.Value) error {
|
|
t := structValue.Type()
|
|
for i := 0; i < t.NumField(); i++ {
|
|
c := t.Field(i)
|
|
if _, isAuto := newTag(c.Tag).getTag("auto"); !isAuto {
|
|
continue
|
|
}
|
|
if c.Anonymous {
|
|
panic("Fun:" + c.Name + " cannot be Anonymous")
|
|
}
|
|
if entry, loaded := f.boxes.Load(c.Type); loaded {
|
|
e := entry.(boxEntry)
|
|
if e.err != nil {
|
|
return e.err
|
|
}
|
|
structValue.Field(i).Set(e.val)
|
|
continue
|
|
}
|
|
if err := f.autowired(structValue.Field(i)); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// autowired 递归创建依赖实例并注入 auto 字段(须持有 f.mu)。
|
|
// 实例先入容器再装配字段,供循环依赖拿到占位引用;失败时覆盖为粘性错误
|
|
func (f *Fun) autowired(fieldValue reflect.Value) error {
|
|
if fieldValue.Kind() != reflect.Ptr || fieldValue.Type().Elem().Kind() != reflect.Struct {
|
|
panic("Fun: auto field " + fieldValue.Type().String() + " must be a pointer to a struct")
|
|
}
|
|
if isPrivate(fieldValue.Type().Elem().Name()) {
|
|
panic("Fun:" + fieldValue.Type().Elem().Name() + " cannot be Private")
|
|
}
|
|
pt := fieldValue.Type()
|
|
checkNewSignature(pt, pt.Elem().Name())
|
|
instance := reflect.New(pt.Elem())
|
|
f.boxes.Store(pt, boxEntry{val: instance})
|
|
fieldValue.Set(instance)
|
|
if err := f.wireStruct(instance.Elem()); err != nil {
|
|
f.boxes.Store(pt, boxEntry{err: err})
|
|
return err
|
|
}
|
|
if err := callNewIfPresent(instance); err != nil {
|
|
f.boxes.Store(pt, boxEntry{err: err})
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// checkNewSignature 校验 New 方法签名:无参数,返回 () 或 (error)。
|
|
// 在指针类型上查找,兼容值接收器与指针接收器两种定义
|
|
func checkNewSignature(pt reflect.Type, name string) {
|
|
m, found := pt.MethodByName("New")
|
|
if !found {
|
|
return
|
|
}
|
|
mt := m.Type
|
|
if mt.NumIn() != 1 { // 仅接收者
|
|
panic("Fun:" + name + " New method must have no parameters")
|
|
}
|
|
if mt.NumOut() == 0 {
|
|
return
|
|
}
|
|
if mt.NumOut() == 1 && mt.Out(0) == errorType {
|
|
return
|
|
}
|
|
panic("Fun:" + name + " New method must return nothing or error")
|
|
}
|
|
|
|
// callNewIfPresent 调用指针上的 New()(若存在),支持 () 与 () error 两种签名
|
|
func callNewIfPresent(ptr reflect.Value) error {
|
|
m := ptr.MethodByName("New")
|
|
if !m.IsValid() {
|
|
return nil
|
|
}
|
|
out := m.Call(nil)
|
|
if len(out) == 1 {
|
|
if err, ok := out[0].Interface().(error); ok {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// boxWired 注册期预初始化服务结构体字段中的 Box 依赖(须持有 f.mu)。
|
|
// 字段对应类型装配失败时向上返回 error
|
|
func boxWired(service any, f *Fun) error {
|
|
svcType := reflect.TypeOf(service).Elem()
|
|
serviceInstance := reflect.New(svcType).Elem()
|
|
for i := 0; i < serviceInstance.NumField(); i++ {
|
|
field := serviceInstance.Field(i)
|
|
if field.Type() == ctxType {
|
|
continue
|
|
}
|
|
if field.Type().Kind() == reflect.Ptr && field.Type().Elem().Kind() == reflect.Struct {
|
|
if entry, isWired := f.boxes.Load(field.Type()); isWired {
|
|
if e := entry.(boxEntry); e.err != nil {
|
|
return e.err
|
|
}
|
|
continue
|
|
}
|
|
if err := f.autowired(field); err != nil {
|
|
return fmt.Errorf("fun: wire %s.%s: %w", svcType.Name(), svcType.Field(i).Name, err)
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// serviceWired 每请求把 Ctx 与 Box 依赖注入到新创建的服务实例(只读容器,无锁)
|
|
func (f *Fun) serviceWired(serviceInstance reflect.Value, ctx *Ctx) {
|
|
for i := 0; i < serviceInstance.NumField(); i++ {
|
|
field := serviceInstance.Field(i)
|
|
if !field.CanSet() {
|
|
continue
|
|
}
|
|
if field.Type() == ctxType {
|
|
field.Set(reflect.ValueOf(*ctx))
|
|
} else if dependency, ok := f.boxes.Load(field.Type()); ok {
|
|
if e := dependency.(boxEntry); e.err == nil {
|
|
field.Set(e.val)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// checkGuard 校验 Guard 类型:必须是指向结构体的指针
|
|
func checkGuard(guard Guard) {
|
|
t := reflect.TypeOf(guard)
|
|
if t.Kind() != reflect.Ptr || t.Elem().Kind() != reflect.Struct {
|
|
panic("Fun: guard must be a pointer to a struct")
|
|
}
|
|
if isPrivate(t.Elem().Name()) {
|
|
panic("Fun:" + t.Elem().Name() + " cannot be Private")
|
|
}
|
|
}
|
|
|
|
// serviceGuardWired 创建 Guard 实例并注入 Box 依赖,返回 guard 引用(须持有 f.mu)
|
|
func serviceGuardWired(guard Guard, f *Fun) (*any, error) {
|
|
t := reflect.TypeOf(guard).Elem()
|
|
guardInstance := reflect.New(t).Elem()
|
|
for i := 0; i < guardInstance.NumField(); i++ {
|
|
field := guardInstance.Field(i)
|
|
if !field.CanSet() {
|
|
continue
|
|
}
|
|
if field.Type() == ctxType {
|
|
continue
|
|
}
|
|
if entry, ok := f.boxes.Load(field.Type()); ok {
|
|
e := entry.(boxEntry)
|
|
if e.err != nil {
|
|
return nil, e.err
|
|
}
|
|
field.Set(e.val)
|
|
} else if field.Kind() == reflect.Ptr && field.Type().Elem().Kind() == reflect.Struct {
|
|
if err := f.autowired(field); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
}
|
|
g := guardInstance.Addr().Interface()
|
|
return &g, nil
|
|
}
|