1package daemon
2
3import "github.com/bnema/vev/internal/domain/layout"
4
5// movePaneTopology is the private pane topology Implementation consumed by the
6// shared moveTransaction Module.
7type movePaneTopology struct {
8 req movePaneRequest
9 admission movePaneAdmission
10}
11
12// movePaneAdmission is an immutable snapshot of the exact live topology and
13// generations a move may later publish. Common Session and Attachment authority
14// is snapshotted by moveTransaction.
15type movePaneAdmission struct {
16 source *session
17 destination *session
18 sourceTab *tab
19 destinationTab *tab
20 movedPane *pane
21 sourceGeneration uint64
22 destinationGeneration uint64
23 finalSourceTab bool
24}
25
26func (p *movePaneTopology) transactionRequest() moveTransactionRequest {
27 req := p.req
28 return moveTransactionRequest{
29 follow: req.Follow,
30 beforeFollow: req.BeforeFollow,
31 afterFollow: req.AfterFollow,
32 operation: "pane",
33 attachment: req.Attachment,
34 attachmentCapability: req.AttachmentCapability,
35 source: req.Source,
36 destination: req.Destination,
37 logAttrs: []any{
38 "source_session_id", req.Source.ID,
39 "source_tab_id", req.SourceTabID,
40 "source_pane_id", req.SourcePaneID,
41 "destination_session_id", req.Destination.ID,
42 "destination_tab_id", req.DestinationTabID,
43 },
44 }
45}
46
47func (p *movePaneTopology) validRequest() bool {
48 return p != nil && p.req.SourceTabID != "" && p.req.SourcePaneID != "" && p.req.DestinationTabID != ""
49}
50
51// admitLocked runs with daemon and ordered Session locks held. It acquires only
52// the private ordered Tab locks and performs no publication.
53func (p *movePaneTopology) admitLocked(_ *Daemon, source, destination *session) error {
54 req := p.req
55 sourceTab := findMoveTabLocked(source, req.SourceTabID)
56 destinationTab := findMoveTabLocked(destination, req.DestinationTabID)
57 if sourceTab == nil || destinationTab == nil {
58 return errMoveStaleTarget
59 }
60 if sourceTab == destinationTab {
61 return errMovePaneInvalid
62 }
63 unlockTabs := lockMoveTabs(sourceTab, destinationTab)
64 defer unlockTabs()
65
66 var movedPane *pane
67 for _, candidate := range sourceTab.panes {
68 if candidate != nil && candidate.stableID == string(req.SourcePaneID) {
69 movedPane = candidate
70 break
71 }
72 }
73 if movedPane == nil {
74 return errMoveStaleTarget
75 }
76
77 p.admission = movePaneAdmission{
78 source: source, destination: destination, sourceTab: sourceTab, destinationTab: destinationTab,
79 movedPane: movedPane,
80 sourceGeneration: sourceTab.layoutGeneration, destinationGeneration: destinationTab.layoutGeneration,
81 finalSourceTab: len(source.tabs) == 1 && source.tabs[0] == sourceTab &&
82 sourceTab.tree != nil && sourceTab.tree.Root != nil && len(layout.LeafIDs(sourceTab.tree.Root)) == 1,
83 }
84 return nil
85}
86
87func (p *movePaneTopology) afterAdmission(d *Daemon) {
88 if d.afterMovePaneSourceSnapshot != nil {
89 d.afterMovePaneSourceSnapshot()
90 }
91}
92
93func (p *movePaneTopology) willRetireSource() bool {
94 return p != nil && p.admission.finalSourceTab && p.admission.source != p.admission.destination
95}
96
97func (p *movePaneTopology) resizeFences(source, destination *session) *moveResizeFences {
98 admission := p.admission
99 return newMovePaneResizeFences(source, destination, admission.sourceTab, admission.destinationTab, admission.movedPane)
100}
101
102func (p *movePaneTopology) publishLocked(d *Daemon, source, destination *session) (moveTopologyPublication, error) {
103 admission := p.admission
104 commit := movePaneCommit{
105 req: p.req,
106 source: source,
107 destination: destination,
108 sourceTab: admission.sourceTab,
109 destinationTab: admission.destinationTab,
110 movedPane: admission.movedPane,
111 sourceGeneration: admission.sourceGeneration,
112 destinationGeneration: admission.destinationGeneration,
113 }
114 return commit.publishTopologyLocked(d)
115}