87 lines
2.6 KiB
Go
87 lines
2.6 KiB
Go
package domain
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
contract "git.cin.su/m/Assistant/contracts/situation"
|
|
)
|
|
|
|
// FocusStack is a rebuildable, situation-owned projection. Push suspends the
|
|
// previous active frame; Pop resumes its parent and never deletes a frame.
|
|
type FocusStack struct{ frames []contract.FocusFrame }
|
|
|
|
func NewFocusStack(frames []contract.FocusFrame) (FocusStack, error) {
|
|
stack := FocusStack{frames: append([]contract.FocusFrame(nil), frames...)}
|
|
for _, frame := range stack.frames {
|
|
if err := frame.Validate(); err != nil {
|
|
return FocusStack{}, err
|
|
}
|
|
}
|
|
return stack, nil
|
|
}
|
|
|
|
func (s FocusStack) Frames() []contract.FocusFrame {
|
|
return append([]contract.FocusFrame(nil), s.frames...)
|
|
}
|
|
|
|
func (s FocusStack) Current() (contract.FocusFrame, error) {
|
|
for i := len(s.frames) - 1; i >= 0; i-- {
|
|
if s.frames[i].State == contract.FocusActive {
|
|
return s.frames[i], nil
|
|
}
|
|
}
|
|
return contract.FocusFrame{}, ErrNoActiveFocus
|
|
}
|
|
|
|
func (s FocusStack) Push(frame contract.FocusFrame) (FocusStack, error) {
|
|
if err := frame.Validate(); err != nil {
|
|
return FocusStack{}, err
|
|
}
|
|
if frame.State != contract.FocusActive {
|
|
return FocusStack{}, fmt.Errorf("pushed frame must be active")
|
|
}
|
|
frames := s.Frames()
|
|
if len(frames) > 0 {
|
|
current, err := s.Current()
|
|
if err == nil {
|
|
for i := range frames {
|
|
if frames[i].ID == current.ID {
|
|
frames[i].State = contract.FocusSuspended
|
|
break
|
|
}
|
|
}
|
|
frame.ParentFrameRef = ¤t.ID
|
|
}
|
|
}
|
|
frames = append(frames, frame)
|
|
return FocusStack{frames: frames}, nil
|
|
}
|
|
|
|
func (s FocusStack) Pop() (FocusStack, contract.FocusFrame, error) {
|
|
current, err := s.Current()
|
|
if err != nil {
|
|
return FocusStack{}, contract.FocusFrame{}, err
|
|
}
|
|
frames := s.Frames()
|
|
for i := range frames {
|
|
if frames[i].ID == current.ID {
|
|
frames[i].State = contract.FocusSuspended
|
|
break
|
|
}
|
|
}
|
|
if current.ParentFrameRef != nil {
|
|
for i := range frames {
|
|
if frames[i].ID == *current.ParentFrameRef {
|
|
frames[i].State = contract.FocusActive
|
|
break
|
|
}
|
|
}
|
|
}
|
|
return FocusStack{frames: frames}, current, nil
|
|
}
|
|
|
|
// ProjectFocusProjection retains the V1 API as a read projection only.
|
|
func ProjectFocusProjection(frame contract.FocusFrame, project contract.ProjectDescriptor) contract.ActiveProjectFocus {
|
|
return contract.ActiveProjectFocus{ActorID: frame.ActorID, PrincipalID: frame.PrincipalID, ProjectID: contract.ProjectID(project.ID), ProjectDisplayName: project.DisplayName, Provenance: contract.ProjectResolutionProvenance(frame.Provenance), ConfidenceMillis: frame.ConfidenceMillis, SourceUtteranceID: frame.SourceUtteranceRef, UpdatedAtUnixMillis: int64(frame.RecordedAt)}
|
|
}
|