detached cac78eb

refactor(ports): separate command wire codecs

commit
cac78eb7bc1908e7d37ae166917dabb89ea5fc48
author
brice · · 2026-07-24 05:40 UTC
parent
ca15ec4
signature
GPG VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · arch-gpg Verified for the committer email
2 files · +154 -151
M internal/ports/wire.go +0 -151
@@ -190,27 +190,6 @@ type Kill struct {
190190 All bool
191191 }
192192
193-// CommandRequest asks the daemon to run one control command. Version must
194-// stay first so a future payload layout can still be rejected cleanly.
195-type CommandRequest struct {
196- Version uint16
197- Self bool
198- Slug string
199- Args []string
200- TargetSession string
201- TargetTab string
202- TargetPane string
203- JSON bool
204-}
205-
206-// CommandResult reports a control command's outcome.
207-type CommandResult struct {
208- OK bool
209- Code uint16
210- Text string
211- Output string
212-}
213-
214193 // SessionInfo describes one session in a Sessions listing.
215194 type SessionInfo struct {
216195 SessionID string
@@ -368,15 +347,6 @@ func PeekHelloVersion(b []byte) (uint16, bool) {
368347 return binary.BigEndian.Uint16(b), true
369348 }
370349
371-// PeekCommandVersion returns the leading protocol version from a
372-// CommandRequest payload.
373-func PeekCommandVersion(b []byte) (uint16, bool) {
374- if len(b) < 2 {
375- return 0, false
376- }
377- return binary.BigEndian.Uint16(b), true
378-}
379-
380350 // MarshalHello encodes h into a Hello message payload.
381351 func MarshalHello(h Hello) []byte {
382352 w := payloadWriter{}
@@ -886,127 +856,6 @@ func UnmarshalKill(b []byte) (Kill, error) {
886856 return Kill{Name: name, All: all != 0}, nil
887857 }
888858
889-// MarshalCommandRequest encodes m into a CommandRequest payload.
890-func MarshalCommandRequest(m CommandRequest) []byte {
891- w := payloadWriter{}
892- w.putUint16(m.Version)
893- if m.Self {
894- w.putUint8(1)
895- } else {
896- w.putUint8(0)
897- }
898- w.putString(m.Slug)
899- w.putUint16(uint16(len(m.Args)))
900- for _, arg := range m.Args {
901- w.putLongString(arg)
902- }
903- w.putString(m.TargetSession)
904- w.putString(m.TargetTab)
905- w.putString(m.TargetPane)
906- if m.JSON {
907- w.putUint8(1)
908- } else {
909- w.putUint8(0)
910- }
911- return w.b
912-}
913-
914-// UnmarshalCommandRequest decodes a strict CommandRequest payload.
915-func UnmarshalCommandRequest(b []byte) (CommandRequest, error) {
916- r := payloadReader{b: b}
917- var m CommandRequest
918- var err error
919-
920- if m.Version, err = r.getUint16(); err != nil {
921- return CommandRequest{}, err
922- }
923- selfFlag, err := r.getUint8()
924- if err != nil {
925- return CommandRequest{}, err
926- }
927- m.Self = selfFlag != 0
928- if m.Slug, err = r.getString(); err != nil {
929- return CommandRequest{}, err
930- }
931- argCount, err := r.getUint16()
932- if err != nil {
933- return CommandRequest{}, err
934- }
935- // Every argument requires at least a uint32 length prefix. Reject an
936- // impossible count before allocating from untrusted input.
937- if uint64(argCount) > uint64(len(r.b)/4) {
938- return CommandRequest{}, errShortPayload
939- }
940- if argCount != 0 {
941- m.Args = make([]string, 0, int(argCount))
942- for range int(argCount) {
943- arg, err := r.getLongString()
944- if err != nil {
945- return CommandRequest{}, err
946- }
947- m.Args = append(m.Args, arg)
948- }
949- }
950- if m.TargetSession, err = r.getString(); err != nil {
951- return CommandRequest{}, err
952- }
953- if m.TargetTab, err = r.getString(); err != nil {
954- return CommandRequest{}, err
955- }
956- if m.TargetPane, err = r.getString(); err != nil {
957- return CommandRequest{}, err
958- }
959- jsonFlag, err := r.getUint8()
960- if err != nil {
961- return CommandRequest{}, err
962- }
963- m.JSON = jsonFlag != 0
964- if err := r.done(); err != nil {
965- return CommandRequest{}, err
966- }
967- return m, nil
968-}
969-
970-// MarshalCommandResult encodes m into a CommandResult payload.
971-func MarshalCommandResult(m CommandResult) []byte {
972- w := payloadWriter{}
973- if m.OK {
974- w.putUint8(1)
975- } else {
976- w.putUint8(0)
977- }
978- w.putUint16(m.Code)
979- w.putString(m.Text)
980- w.putLongString(m.Output)
981- return w.b
982-}
983-
984-// UnmarshalCommandResult decodes a strict CommandResult payload.
985-func UnmarshalCommandResult(b []byte) (CommandResult, error) {
986- r := payloadReader{b: b}
987- var m CommandResult
988- var err error
989-
990- okFlag, err := r.getUint8()
991- if err != nil {
992- return CommandResult{}, err
993- }
994- m.OK = okFlag != 0
995- if m.Code, err = r.getUint16(); err != nil {
996- return CommandResult{}, err
997- }
998- if m.Text, err = r.getString(); err != nil {
999- return CommandResult{}, err
1000- }
1001- if m.Output, err = r.getLongString(); err != nil {
1002- return CommandResult{}, err
1003- }
1004- if err := r.done(); err != nil {
1005- return CommandResult{}, err
1006- }
1007- return m, nil
1008-}
1009-
1010859 // MarshalSessions encodes m into a Sessions message payload: a uint16 count
1011860 // followed by that many session records.
1012861 func MarshalSessions(m Sessions) []byte {
@@ -190,27 +190,6 @@ type Kill struct {
190 All bool190 All bool
191 }191 }
192 192
193-// CommandRequest asks the daemon to run one control command. Version must
194-// stay first so a future payload layout can still be rejected cleanly.
195-type CommandRequest struct {
196- Version uint16
197- Self bool
198- Slug string
199- Args []string
200- TargetSession string
201- TargetTab string
202- TargetPane string
203- JSON bool
204-}
205-
206-// CommandResult reports a control command's outcome.
207-type CommandResult struct {
208- OK bool
209- Code uint16
210- Text string
211- Output string
212-}
213-
214 // SessionInfo describes one session in a Sessions listing.193 // SessionInfo describes one session in a Sessions listing.
215 type SessionInfo struct {194 type SessionInfo struct {
216 SessionID string195 SessionID string
@@ -368,15 +347,6 @@ func PeekHelloVersion(b []byte) (uint16, bool) {
368 return binary.BigEndian.Uint16(b), true347 return binary.BigEndian.Uint16(b), true
369 }348 }
370 349
371-// PeekCommandVersion returns the leading protocol version from a
372-// CommandRequest payload.
373-func PeekCommandVersion(b []byte) (uint16, bool) {
374- if len(b) < 2 {
375- return 0, false
376- }
377- return binary.BigEndian.Uint16(b), true
378-}
379-
380 // MarshalHello encodes h into a Hello message payload.350 // MarshalHello encodes h into a Hello message payload.
381 func MarshalHello(h Hello) []byte {351 func MarshalHello(h Hello) []byte {
382 w := payloadWriter{}352 w := payloadWriter{}
@@ -886,127 +856,6 @@ func UnmarshalKill(b []byte) (Kill, error) {
886 return Kill{Name: name, All: all != 0}, nil856 return Kill{Name: name, All: all != 0}, nil
887 }857 }
888 858
889-// MarshalCommandRequest encodes m into a CommandRequest payload.
890-func MarshalCommandRequest(m CommandRequest) []byte {
891- w := payloadWriter{}
892- w.putUint16(m.Version)
893- if m.Self {
894- w.putUint8(1)
895- } else {
896- w.putUint8(0)
897- }
898- w.putString(m.Slug)
899- w.putUint16(uint16(len(m.Args)))
900- for _, arg := range m.Args {
901- w.putLongString(arg)
902- }
903- w.putString(m.TargetSession)
904- w.putString(m.TargetTab)
905- w.putString(m.TargetPane)
906- if m.JSON {
907- w.putUint8(1)
908- } else {
909- w.putUint8(0)
910- }
911- return w.b
912-}
913-
914-// UnmarshalCommandRequest decodes a strict CommandRequest payload.
915-func UnmarshalCommandRequest(b []byte) (CommandRequest, error) {
916- r := payloadReader{b: b}
917- var m CommandRequest
918- var err error
919-
920- if m.Version, err = r.getUint16(); err != nil {
921- return CommandRequest{}, err
922- }
923- selfFlag, err := r.getUint8()
924- if err != nil {
925- return CommandRequest{}, err
926- }
927- m.Self = selfFlag != 0
928- if m.Slug, err = r.getString(); err != nil {
929- return CommandRequest{}, err
930- }
931- argCount, err := r.getUint16()
932- if err != nil {
933- return CommandRequest{}, err
934- }
935- // Every argument requires at least a uint32 length prefix. Reject an
936- // impossible count before allocating from untrusted input.
937- if uint64(argCount) > uint64(len(r.b)/4) {
938- return CommandRequest{}, errShortPayload
939- }
940- if argCount != 0 {
941- m.Args = make([]string, 0, int(argCount))
942- for range int(argCount) {
943- arg, err := r.getLongString()
944- if err != nil {
945- return CommandRequest{}, err
946- }
947- m.Args = append(m.Args, arg)
948- }
949- }
950- if m.TargetSession, err = r.getString(); err != nil {
951- return CommandRequest{}, err
952- }
953- if m.TargetTab, err = r.getString(); err != nil {
954- return CommandRequest{}, err
955- }
956- if m.TargetPane, err = r.getString(); err != nil {
957- return CommandRequest{}, err
958- }
959- jsonFlag, err := r.getUint8()
960- if err != nil {
961- return CommandRequest{}, err
962- }
963- m.JSON = jsonFlag != 0
964- if err := r.done(); err != nil {
965- return CommandRequest{}, err
966- }
967- return m, nil
968-}
969-
970-// MarshalCommandResult encodes m into a CommandResult payload.
971-func MarshalCommandResult(m CommandResult) []byte {
972- w := payloadWriter{}
973- if m.OK {
974- w.putUint8(1)
975- } else {
976- w.putUint8(0)
977- }
978- w.putUint16(m.Code)
979- w.putString(m.Text)
980- w.putLongString(m.Output)
981- return w.b
982-}
983-
984-// UnmarshalCommandResult decodes a strict CommandResult payload.
985-func UnmarshalCommandResult(b []byte) (CommandResult, error) {
986- r := payloadReader{b: b}
987- var m CommandResult
988- var err error
989-
990- okFlag, err := r.getUint8()
991- if err != nil {
992- return CommandResult{}, err
993- }
994- m.OK = okFlag != 0
995- if m.Code, err = r.getUint16(); err != nil {
996- return CommandResult{}, err
997- }
998- if m.Text, err = r.getString(); err != nil {
999- return CommandResult{}, err
1000- }
1001- if m.Output, err = r.getLongString(); err != nil {
1002- return CommandResult{}, err
1003- }
1004- if err := r.done(); err != nil {
1005- return CommandResult{}, err
1006- }
1007- return m, nil
1008-}
1009-
1010 // MarshalSessions encodes m into a Sessions message payload: a uint16 count859 // MarshalSessions encodes m into a Sessions message payload: a uint16 count
1011 // followed by that many session records.860 // followed by that many session records.
1012 func MarshalSessions(m Sessions) []byte {861 func MarshalSessions(m Sessions) []byte {
A internal/ports/wire_command.go +154 -0
@@ -0,0 +1,154 @@
1+package ports
2+
3+import "encoding/binary"
4+
5+// CommandRequest asks the daemon to run one control command. Version must
6+// stay first so a future payload layout can still be rejected cleanly.
7+type CommandRequest struct {
8+ Version uint16
9+ Self bool
10+ Slug string
11+ Args []string
12+ TargetSession string
13+ TargetTab string
14+ TargetPane string
15+ JSON bool
16+}
17+
18+// CommandResult reports a control command's outcome.
19+type CommandResult struct {
20+ OK bool
21+ Code uint16
22+ Text string
23+ Output string
24+}
25+
26+// PeekCommandVersion returns the leading protocol version from a
27+// CommandRequest payload.
28+func PeekCommandVersion(b []byte) (uint16, bool) {
29+ if len(b) < 2 {
30+ return 0, false
31+ }
32+ return binary.BigEndian.Uint16(b), true
33+}
34+
35+// MarshalCommandRequest encodes m into a CommandRequest payload.
36+func MarshalCommandRequest(m CommandRequest) []byte {
37+ w := payloadWriter{}
38+ w.putUint16(m.Version)
39+ if m.Self {
40+ w.putUint8(1)
41+ } else {
42+ w.putUint8(0)
43+ }
44+ w.putString(m.Slug)
45+ w.putUint16(uint16(len(m.Args)))
46+ for _, arg := range m.Args {
47+ w.putLongString(arg)
48+ }
49+ w.putString(m.TargetSession)
50+ w.putString(m.TargetTab)
51+ w.putString(m.TargetPane)
52+ if m.JSON {
53+ w.putUint8(1)
54+ } else {
55+ w.putUint8(0)
56+ }
57+ return w.b
58+}
59+
60+// UnmarshalCommandRequest decodes a strict CommandRequest payload.
61+func UnmarshalCommandRequest(b []byte) (CommandRequest, error) {
62+ r := payloadReader{b: b}
63+ var m CommandRequest
64+ var err error
65+
66+ if m.Version, err = r.getUint16(); err != nil {
67+ return CommandRequest{}, err
68+ }
69+ selfFlag, err := r.getUint8()
70+ if err != nil {
71+ return CommandRequest{}, err
72+ }
73+ m.Self = selfFlag != 0
74+ if m.Slug, err = r.getString(); err != nil {
75+ return CommandRequest{}, err
76+ }
77+ argCount, err := r.getUint16()
78+ if err != nil {
79+ return CommandRequest{}, err
80+ }
81+ // Every argument requires at least a uint32 length prefix. Reject an
82+ // impossible count before allocating from untrusted input.
83+ if uint64(argCount) > uint64(len(r.b)/4) {
84+ return CommandRequest{}, errShortPayload
85+ }
86+ if argCount != 0 {
87+ m.Args = make([]string, 0, int(argCount))
88+ for range int(argCount) {
89+ arg, err := r.getLongString()
90+ if err != nil {
91+ return CommandRequest{}, err
92+ }
93+ m.Args = append(m.Args, arg)
94+ }
95+ }
96+ if m.TargetSession, err = r.getString(); err != nil {
97+ return CommandRequest{}, err
98+ }
99+ if m.TargetTab, err = r.getString(); err != nil {
100+ return CommandRequest{}, err
101+ }
102+ if m.TargetPane, err = r.getString(); err != nil {
103+ return CommandRequest{}, err
104+ }
105+ jsonFlag, err := r.getUint8()
106+ if err != nil {
107+ return CommandRequest{}, err
108+ }
109+ m.JSON = jsonFlag != 0
110+ if err := r.done(); err != nil {
111+ return CommandRequest{}, err
112+ }
113+ return m, nil
114+}
115+
116+// MarshalCommandResult encodes m into a CommandResult payload.
117+func MarshalCommandResult(m CommandResult) []byte {
118+ w := payloadWriter{}
119+ if m.OK {
120+ w.putUint8(1)
121+ } else {
122+ w.putUint8(0)
123+ }
124+ w.putUint16(m.Code)
125+ w.putString(m.Text)
126+ w.putLongString(m.Output)
127+ return w.b
128+}
129+
130+// UnmarshalCommandResult decodes a strict CommandResult payload.
131+func UnmarshalCommandResult(b []byte) (CommandResult, error) {
132+ r := payloadReader{b: b}
133+ var m CommandResult
134+ var err error
135+
136+ okFlag, err := r.getUint8()
137+ if err != nil {
138+ return CommandResult{}, err
139+ }
140+ m.OK = okFlag != 0
141+ if m.Code, err = r.getUint16(); err != nil {
142+ return CommandResult{}, err
143+ }
144+ if m.Text, err = r.getString(); err != nil {
145+ return CommandResult{}, err
146+ }
147+ if m.Output, err = r.getLongString(); err != nil {
148+ return CommandResult{}, err
149+ }
150+ if err := r.done(); err != nil {
151+ return CommandResult{}, err
152+ }
153+ return m, nil
154+}
@@ -0,0 +1,154 @@
1+package ports
2+
3+import "encoding/binary"
4+
5+// CommandRequest asks the daemon to run one control command. Version must
6+// stay first so a future payload layout can still be rejected cleanly.
7+type CommandRequest struct {
8+ Version uint16
9+ Self bool
10+ Slug string
11+ Args []string
12+ TargetSession string
13+ TargetTab string
14+ TargetPane string
15+ JSON bool
16+}
17+
18+// CommandResult reports a control command's outcome.
19+type CommandResult struct {
20+ OK bool
21+ Code uint16
22+ Text string
23+ Output string
24+}
25+
26+// PeekCommandVersion returns the leading protocol version from a
27+// CommandRequest payload.
28+func PeekCommandVersion(b []byte) (uint16, bool) {
29+ if len(b) < 2 {
30+ return 0, false
31+ }
32+ return binary.BigEndian.Uint16(b), true
33+}
34+
35+// MarshalCommandRequest encodes m into a CommandRequest payload.
36+func MarshalCommandRequest(m CommandRequest) []byte {
37+ w := payloadWriter{}
38+ w.putUint16(m.Version)
39+ if m.Self {
40+ w.putUint8(1)
41+ } else {
42+ w.putUint8(0)
43+ }
44+ w.putString(m.Slug)
45+ w.putUint16(uint16(len(m.Args)))
46+ for _, arg := range m.Args {
47+ w.putLongString(arg)
48+ }
49+ w.putString(m.TargetSession)
50+ w.putString(m.TargetTab)
51+ w.putString(m.TargetPane)
52+ if m.JSON {
53+ w.putUint8(1)
54+ } else {
55+ w.putUint8(0)
56+ }
57+ return w.b
58+}
59+
60+// UnmarshalCommandRequest decodes a strict CommandRequest payload.
61+func UnmarshalCommandRequest(b []byte) (CommandRequest, error) {
62+ r := payloadReader{b: b}
63+ var m CommandRequest
64+ var err error
65+
66+ if m.Version, err = r.getUint16(); err != nil {
67+ return CommandRequest{}, err
68+ }
69+ selfFlag, err := r.getUint8()
70+ if err != nil {
71+ return CommandRequest{}, err
72+ }
73+ m.Self = selfFlag != 0
74+ if m.Slug, err = r.getString(); err != nil {
75+ return CommandRequest{}, err
76+ }
77+ argCount, err := r.getUint16()
78+ if err != nil {
79+ return CommandRequest{}, err
80+ }
81+ // Every argument requires at least a uint32 length prefix. Reject an
82+ // impossible count before allocating from untrusted input.
83+ if uint64(argCount) > uint64(len(r.b)/4) {
84+ return CommandRequest{}, errShortPayload
85+ }
86+ if argCount != 0 {
87+ m.Args = make([]string, 0, int(argCount))
88+ for range int(argCount) {
89+ arg, err := r.getLongString()
90+ if err != nil {
91+ return CommandRequest{}, err
92+ }
93+ m.Args = append(m.Args, arg)
94+ }
95+ }
96+ if m.TargetSession, err = r.getString(); err != nil {
97+ return CommandRequest{}, err
98+ }
99+ if m.TargetTab, err = r.getString(); err != nil {
100+ return CommandRequest{}, err
101+ }
102+ if m.TargetPane, err = r.getString(); err != nil {
103+ return CommandRequest{}, err
104+ }
105+ jsonFlag, err := r.getUint8()
106+ if err != nil {
107+ return CommandRequest{}, err
108+ }
109+ m.JSON = jsonFlag != 0
110+ if err := r.done(); err != nil {
111+ return CommandRequest{}, err
112+ }
113+ return m, nil
114+}
115+
116+// MarshalCommandResult encodes m into a CommandResult payload.
117+func MarshalCommandResult(m CommandResult) []byte {
118+ w := payloadWriter{}
119+ if m.OK {
120+ w.putUint8(1)
121+ } else {
122+ w.putUint8(0)
123+ }
124+ w.putUint16(m.Code)
125+ w.putString(m.Text)
126+ w.putLongString(m.Output)
127+ return w.b
128+}
129+
130+// UnmarshalCommandResult decodes a strict CommandResult payload.
131+func UnmarshalCommandResult(b []byte) (CommandResult, error) {
132+ r := payloadReader{b: b}
133+ var m CommandResult
134+ var err error
135+
136+ okFlag, err := r.getUint8()
137+ if err != nil {
138+ return CommandResult{}, err
139+ }
140+ m.OK = okFlag != 0
141+ if m.Code, err = r.getUint16(); err != nil {
142+ return CommandResult{}, err
143+ }
144+ if m.Text, err = r.getString(); err != nil {
145+ return CommandResult{}, err
146+ }
147+ if m.Output, err = r.getLongString(); err != nil {
148+ return CommandResult{}, err
149+ }
150+ if err := r.done(); err != nil {
151+ return CommandResult{}, err
152+ }
153+ return m, nil
154+}

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 12

Tags 11

↑↓ moveenter openesc close