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
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 |
|
}
|
|
|