diff --git a/v1/context/context.go b/v1/context/context.go index 52a1595..8b040ab 100644 --- a/v1/context/context.go +++ b/v1/context/context.go @@ -2,7 +2,7 @@ package context import ( "context" - "github.com/abelith/gomax/v1/state" + "github.com/abelith/gomax/v1/fsm/state" maxbot "github.com/max-messenger/max-bot-api-client-go/v2" "github.com/max-messenger/max-bot-api-client-go/v2/model" "time" diff --git a/v1/core/dispatcher.go b/v1/core/dispatcher.go index 7a76d15..ddda2e2 100644 --- a/v1/core/dispatcher.go +++ b/v1/core/dispatcher.go @@ -4,9 +4,9 @@ import ( "context" "errors" mcontext "github.com/abelith/gomax/v1/context" + "github.com/abelith/gomax/v1/fsm/memory" + "github.com/abelith/gomax/v1/fsm/state" "github.com/abelith/gomax/v1/isolation" - "github.com/abelith/gomax/v1/state" - "github.com/abelith/gomax/v1/state/memory" "github.com/google/uuid" maxbot "github.com/max-messenger/max-bot-api-client-go/v2" "github.com/max-messenger/max-bot-api-client-go/v2/model" diff --git a/v1/filters/filters.go b/v1/filters/filters.go index 138b15c..1258c41 100644 --- a/v1/filters/filters.go +++ b/v1/filters/filters.go @@ -2,7 +2,7 @@ package filters import ( mcontext "github.com/abelith/gomax/v1/context" - "github.com/abelith/gomax/v1/state" + "github.com/abelith/gomax/v1/fsm/state" "github.com/max-messenger/max-bot-api-client-go/v2/model" "regexp" "strings" diff --git a/v1/state/memory/memory.go b/v1/fsm/memory/memory.go similarity index 98% rename from v1/state/memory/memory.go rename to v1/fsm/memory/memory.go index 2a27171..efdadd8 100644 --- a/v1/state/memory/memory.go +++ b/v1/fsm/memory/memory.go @@ -2,8 +2,8 @@ package memory import ( "context" + "github.com/abelith/gomax/v1/fsm/state" "github.com/abelith/gomax/v1/isolation" - "github.com/abelith/gomax/v1/state" "github.com/google/uuid" "sync" "time" diff --git a/v1/state/state.go b/v1/fsm/state/state.go similarity index 77% rename from v1/state/state.go rename to v1/fsm/state/state.go index dee0d07..e6348f6 100644 --- a/v1/state/state.go +++ b/v1/fsm/state/state.go @@ -4,6 +4,7 @@ import ( "encoding/json" "fmt" "github.com/abelith/gomax/v1/isolation" + "strings" ) var ( @@ -11,7 +12,15 @@ var ( ErrKeyNotFound = fmt.Errorf("state: key not found") ) -type State string +type State struct { + group string + name string +} + +type Group struct { + name string + states map[string]*State +} type Codec interface { Marshal(v any) ([]byte, error) @@ -30,7 +39,7 @@ func (JSONCodec) Unmarshal(d []byte, v any) error { type Snapshot struct { Scope isolation.Scope `json:"id"` - State State `json:"state"` + State string `json:"state"` Data map[string][]byte `json:"data"` } @@ -44,6 +53,49 @@ type Context struct { type Option func(*Context) +func NewStateGroup(name string) *Group { + return &Group{ + name: name, + states: make(map[string]*State), + } +} + +func (g *Group) State(name string) State { + state := g.states[name] + + if state != nil { + return *state + } + + if state = g.states[name]; state != nil { + return *state + } + + state = &State{ + group: g.name, + name: name, + } + + g.states[name] = state + return *state +} + +func (s *State) String() string { + return fmt.Sprintf("%s:%s", s.group, s.name) +} + +func fromString(s string) State { + pieces := strings.Split(s, ":") + if len(pieces) < 2 { + return State{} + } + + return State{ + group: pieces[0], + name: pieces[1], + } +} + func WithCodec(c Codec) Option { return func(sc *Context) { sc.codec = c } } func New(scope isolation.Scope, opts ...Option) *Context { @@ -60,7 +112,7 @@ func New(scope isolation.Scope, opts ...Option) *Context { func Restore(s Snapshot, opts ...Option) *Context { sc := New(s.Scope, opts...) - sc.state = s.State + sc.state = fromString(s.State) for k, v := range s.Data { sc.data[k] = v } @@ -72,7 +124,7 @@ func (sc *Context) Snapshot() Snapshot { for k, v := range sc.data { data[k] = v } - return Snapshot{Scope: sc.scope, State: sc.state, Data: data} + return Snapshot{Scope: sc.scope, State: sc.state.String(), Data: data} } func (sc *Context) Set(state State) { @@ -82,13 +134,13 @@ func (sc *Context) Set(state State) { func (sc *Context) State() State { return sc.state } func (sc *Context) Clear() { - sc.state = "" + sc.state = State{} sc.clear = true sc.data = make(map[string][]byte) } func (sc *Context) IsEmpty() bool { - return sc.state == "" && sc.clear + return sc.state == State{} && sc.clear } func (sc *Context) Store(key string, val any) error {