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