You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

184 lines
4.1 KiB

package state
import (
"errors"
"fmt"
"log"
"reflect"
)
type S map[string]any
type SetState struct {
States S
Setters []Setter
Broadcast bool // 广播或者指定 includes
ExcludeReceivers []string
IncludeReceivers []string
}
// State 增量状态同步
type State struct {
options Options
v any
rv reflect.Value
c chan SetState
statesTmp S
settersTmp []Setter
done chan bool
}
// InitState 初始化状态 todo serializable distribute transfer
func (s *State) InitState(ptrV any, opts ...Option) {
t := reflect.TypeOf(ptrV)
if t.Kind() != reflect.Ptr {
panic("参数必须是ptr类型")
}
s.options = defaultOptions
for _, opt := range opts {
opt(&s.options)
}
s.v = ptrV
s.rv = reflect.ValueOf(s.v)
s.c = make(chan SetState, 16)
s.statesTmp = S{}
s.done = make(chan bool)
}
func (s *State) Chan() (c <-chan SetState, done <-chan bool) {
return s.c, s.done
}
// State2Broadcast 聚合消息发送到管道进行广播
// excludeReceivers 忽略 receiver 列表
func (s *State) State2Broadcast(excludeReceivers ...string) {
s.state2Pipe(true, nil, excludeReceivers)
}
// State2Assign 聚合消息发送到管道指定receiver发送
// includes 指定 receiver 列表
func (s *State) State2Assign(includesReceivers ...string) {
s.state2Pipe(false, includesReceivers, nil)
}
func (s *State) state2Pipe(broadcast bool, includes []string, excludes []string) {
if len(s.statesTmp) == 0 && len(s.settersTmp) == 0 {
return
}
state := SetState{
States: s.statesTmp,
Setters: s.settersTmp,
Broadcast: broadcast,
IncludeReceivers: includes,
ExcludeReceivers: excludes,
}
s.statesTmp = S{}
s.settersTmp = []Setter{}
s.options.Mapping(state.States, &state.Setters)
s.c <- state
}
func (s *State) StateIdleClean() {
s.statesTmp = S{}
s.settersTmp = []Setter{}
}
// SetState panic error
func (s *State) SetState(state S) {
err := s.setState0(state)
if err != nil {
panic(err)
}
}
// SetStateEx return error
func (s *State) SetStateEx(state S) (err error) {
return s.setState0(state)
}
func (s *State) setState0(state S) (err error) {
defer func() {
if e := recover(); e != nil {
if e2, ok := e.(string); ok {
err = errors.New(e2)
return
}
if e1, ok := e.(error); ok {
err = e1
return
}
err = errors.New("recover setState")
log.Printf("error recover setState: %v \n", e)
}
}()
for k, v := range state {
field := s.rv.Elem().FieldByName(k)
if !field.CanSet() {
return fmt.Errorf("%s cannot set", k)
}
setter, ok := v.(Setter)
if !ok {
if v == nil {
field.Set(reflect.New(field.Type()).Elem())
} else {
field.Set(reflect.ValueOf(v))
}
s.statesTmp[k] = v
} else {
switch setter.T {
case 2: // setArray
fallthrough
case 3: // setSlice
ele := field.Index(setter.Index)
if !ele.CanSet() {
return fmt.Errorf("%s %d cannot set", k, setter.Index)
}
if setter.V == nil {
ele.Set(reflect.New(ele.Type()).Elem())
} else {
ele.Set(reflect.ValueOf(setter.V))
}
case 4: // setMap
field.SetMapIndex(reflect.ValueOf(setter.K2), reflect.ValueOf(setter.V))
case 5: // setStruct
field2 := field.FieldByName(setter.K2)
if !field2.CanSet() {
return fmt.Errorf("%s.%s cannot set", k, setter.K2)
}
if setter.V == nil {
field2.Set(reflect.New(field2.Type()).Elem())
} else {
field2.Set(reflect.ValueOf(setter.V))
}
case 6: // append slice
if !field.CanSet() {
return fmt.Errorf("%s.%s cannot set", k, setter.K)
}
if setter.V == nil {
field.Set(reflect.Append(field, reflect.New(field.Type().Elem()).Elem()))
} else {
field.Set(reflect.Append(field, reflect.ValueOf(setter.V)))
}
case 7: // pop slice
newV := field.Slice(0, field.Len()-setter.Index)
field.Set(newV)
case 8: // shift slice
newV := field.Slice(setter.Index, field.Len())
field.Set(newV)
}
setter.K = k
s.settersTmp = append(s.settersTmp, setter)
}
}
return
}
func (s *State) Close() {
s.done <- true
close(s.c)
return
}