preview.go RawHistoryFolder

1package brokerwire
2
3// Preview wire converters (broker preview slice 1).
4//
5// StartPreview/CancelPreview travel client-to-server; PreviewPublication
6// travels server-to-client with exactly one of its preview/error result
7// members set. Conversion mirrors the port preview types losslessly and
8// validates every narrowing numeric cast before it happens, following the
9// shared converter patterns in convert.go: wire uint32 taxonomy fields
10// never truncate into a smaller semantic enum, identity lengths are exact,
11// and semantic validation failures map to ErrInvalidMessage.
12//
13// The preview viewport (RemotePreviewRequest/RemotePreview) reuses the
14// terminal wire shapes owned by the session conversation; the broker
15// conversation carries them as nested messages under its own envelope
16// tags. Repeated authority is scope/generation plus the preview target
17// identity response; the route names the observed daemon only, and the
18// broker allocates the observation stream itself.
19//
20// The field-by-field RemotePreviewRequest/RemotePreview conversion logic
21// lives in internal/adapters/protoconv, the canonical implementation shared
22// with sessionwire. The wrappers below adapt protoconv's shared
23// ErrOutOfRange to this package's errConvertRange, keeping range failures
24// distinct from a semantic protocol validation failure (which maps to
25// ErrInvalidMessage, or ErrTooLarge for an oversized preview) exactly as
26// before extraction.
27
28import (
29	"errors"
30
31	"github.com/bnema/vev/internal/adapters/protoconv"
32	"github.com/bnema/vev/internal/domain"
33	"github.com/bnema/vev/internal/ports"
34	"github.com/bnema/vev/internal/protocol"
35	"github.com/bnema/vev/internal/protocol/wire"
36)
37
38func remotePreviewRequestToWire(message protocol.RemotePreviewRequest) (*wire.RemotePreviewRequest, error) {
39	out, err := protoconv.RemotePreviewRequestToWire(message)
40	if err != nil {
41		return nil, ErrInvalidMessage
42	}
43	return out, nil
44}
45
46func remotePreviewRequestFromWire(message *wire.RemotePreviewRequest) (protocol.RemotePreviewRequest, error) {
47	if message == nil {
48		return protocol.RemotePreviewRequest{}, ErrInvalidMessage
49	}
50	request, err := protoconv.RemotePreviewRequestFromWire(message)
51	if err != nil {
52		if errors.Is(err, protoconv.ErrOutOfRange) {
53			return protocol.RemotePreviewRequest{}, errConvertRange
54		}
55		return protocol.RemotePreviewRequest{}, ErrInvalidMessage
56	}
57	return request, nil
58}
59
60func remotePreviewToWire(message protocol.RemotePreview) (*wire.RemotePreview, error) {
61	out, err := protoconv.RemotePreviewToWire(message)
62	if err != nil {
63		if errors.Is(err, protocol.ErrRemotePreviewTooLarge) {
64			return nil, ErrTooLarge
65		}
66		if errors.Is(err, protoconv.ErrOutOfRange) {
67			return nil, errConvertRange
68		}
69		return nil, ErrInvalidMessage
70	}
71	return out, nil
72}
73
74func remotePreviewFromWire(message *wire.RemotePreview) (protocol.RemotePreview, error) {
75	if message == nil {
76		return protocol.RemotePreview{}, ErrInvalidMessage
77	}
78	preview, err := protoconv.RemotePreviewFromWire(message)
79	if err != nil {
80		if errors.Is(err, protocol.ErrRemotePreviewTooLarge) {
81			return protocol.RemotePreview{}, ErrTooLarge
82		}
83		if errors.Is(err, protoconv.ErrOutOfRange) {
84			return protocol.RemotePreview{}, errConvertRange
85		}
86		return protocol.RemotePreview{}, ErrInvalidMessage
87	}
88	return preview, nil
89}
90
91// previewRequest mirrors one StartPreview as the ports request the preview
92// contract gates, so encode and decode refuse exactly what
93// ports.BrokerPreviewRequest.Validate refuses.
94func previewRequest(m StartPreview) ports.BrokerPreviewRequest {
95	return ports.BrokerPreviewRequest{Epoch: m.Epoch, Connection: m.Connection, Generation: m.Generation, Route: m.Route, Preview: m.Preview}
96}
97
98func previewRouteToWire(route ports.BrokerPreviewRoute) *wire.PreviewRoute {
99	out := &wire.PreviewRoute{Local: route.Local, Endpoint: route.Endpoint, Policy: policyToWire(route.Policy)}
100	if !route.Local {
101		out.Registration = registrationToWire(route.Registration)
102	}
103	return out
104}
105
106func previewRouteFromWire(message *wire.PreviewRoute) (ports.BrokerPreviewRoute, error) {
107	if message == nil {
108		return ports.BrokerPreviewRoute{}, ErrInvalidMessage
109	}
110	policy, err := policyFromWire(message.GetPolicy())
111	if err != nil {
112		return ports.BrokerPreviewRoute{}, ErrInvalidMessage
113	}
114	route := ports.BrokerPreviewRoute{Local: message.GetLocal(), Endpoint: message.GetEndpoint(), Policy: policy}
115	if route.Local {
116		if message.GetRegistration() != nil {
117			return ports.BrokerPreviewRoute{}, ErrInvalidMessage
118		}
119	} else if route.Registration, err = registrationFromWire(message.GetRegistration()); err != nil {
120		return ports.BrokerPreviewRoute{}, ErrInvalidMessage
121	}
122	if route.Validate() != nil {
123		return ports.BrokerPreviewRoute{}, ErrInvalidMessage
124	}
125	return route, nil
126}
127
128func startPreviewToWire(m StartPreview) (*wire.StartPreview, error) {
129	preview, err := remotePreviewRequestToWire(m.Preview)
130	if err != nil {
131		return nil, err
132	}
133	if err := previewRequest(m).Validate(); err != nil {
134		return nil, ErrInvalidMessage
135	}
136	return &wire.StartPreview{
137		Scope:      scopeToWire(m.Epoch, m.Connection),
138		Generation: uint64(m.Generation),
139		Route:      previewRouteToWire(m.Route),
140		Preview:    preview,
141	}, nil
142}
143
144func startPreviewFromWire(message *wire.StartPreview) (StartPreview, error) {
145	if message == nil {
146		return StartPreview{}, ErrInvalidMessage
147	}
148	epoch, connection, err := scopeFromWire(message.GetScope())
149	if err != nil {
150		return StartPreview{}, ErrInvalidMessage
151	}
152	route, err := previewRouteFromWire(message.GetRoute())
153	if err != nil {
154		return StartPreview{}, err
155	}
156	preview, err := remotePreviewRequestFromWire(message.GetPreview())
157	if err != nil {
158		return StartPreview{}, err
159	}
160	candidate := StartPreview{Epoch: epoch, Connection: connection, Generation: ports.BrokerPreviewGeneration(message.GetGeneration()), Route: route, Preview: preview}
161	if err := previewRequest(candidate).Validate(); err != nil {
162		return StartPreview{}, ErrInvalidMessage
163	}
164	return candidate, nil
165}
166
167func cancelPreviewToWire(m CancelPreview) (*wire.CancelPreview, error) {
168	if m.Epoch == 0 {
169		return nil, ErrInvalidMessage
170	}
171	if err := m.Connection.Validate(); err != nil {
172		return nil, ErrInvalidMessage
173	}
174	if err := m.Generation.Validate(); err != nil {
175		return nil, ErrInvalidMessage
176	}
177	return &wire.CancelPreview{
178		Scope:      scopeToWire(m.Epoch, m.Connection),
179		Generation: uint64(m.Generation),
180	}, nil
181}
182
183func cancelPreviewFromWire(message *wire.CancelPreview) (CancelPreview, error) {
184	var out CancelPreview
185	if message == nil {
186		return out, ErrInvalidMessage
187	}
188	epoch, connection, err := scopeFromWire(message.GetScope())
189	if err != nil {
190		return CancelPreview{}, ErrInvalidMessage
191	}
192	generation := ports.BrokerPreviewGeneration(message.GetGeneration())
193	if err := generation.Validate(); err != nil {
194		return CancelPreview{}, ErrInvalidMessage
195	}
196	return CancelPreview{Epoch: epoch, Connection: connection, Generation: generation}, nil
197}
198
199func previewPublicationToWire(m PreviewPublication) (*wire.PreviewPublication, error) {
200	if m.Epoch == 0 {
201		return nil, ErrInvalidMessage
202	}
203	if err := m.Connection.Validate(); err != nil {
204		return nil, ErrInvalidMessage
205	}
206	if err := m.Generation.Validate(); err != nil {
207		return nil, ErrInvalidMessage
208	}
209	out := &wire.PreviewPublication{
210		Scope:      scopeToWire(m.Epoch, m.Connection),
211		Generation: uint64(m.Generation),
212	}
213	// Exactly one of preview/error travels: a failed publication carries
214	// only its typed error, a successful one only its preview.
215	if m.HasError {
216		if err := m.Error.validate(); err != nil {
217			return nil, err
218		}
219		if previewCarriesViewport(m.Preview) {
220			return nil, ErrInvalidMessage
221		}
222		out.Result = &wire.PreviewPublication_Error{Error: errorDetailToWire(m.Error)}
223		return out, nil
224	}
225	if m.Error != (ErrorDetail{}) {
226		return nil, ErrInvalidMessage
227	}
228	preview, err := remotePreviewToWire(m.Preview)
229	if err != nil {
230		return nil, err
231	}
232	out.Result = &wire.PreviewPublication_Preview{Preview: preview}
233	return out, nil
234}
235
236// previewCarriesViewport reports whether a preview value carries any
237// viewport authority. A failed publication must carry none.
238func previewCarriesViewport(preview protocol.RemotePreview) bool {
239	return preview.Version != 0 || preview.Status != 0 ||
240		preview.LifecycleID != (domain.SessionLifecycleID{}) || preview.TabID != "" ||
241		preview.Revision != 0 || preview.Width != 0 || preview.Height != 0 ||
242		len(preview.Cells) != 0
243}
244
245func previewPublicationFromWire(message *wire.PreviewPublication) (PreviewPublication, error) {
246	var out PreviewPublication
247	if message == nil {
248		return out, ErrInvalidMessage
249	}
250	epoch, connection, err := scopeFromWire(message.GetScope())
251	if err != nil {
252		return PreviewPublication{}, ErrInvalidMessage
253	}
254	generation := ports.BrokerPreviewGeneration(message.GetGeneration())
255	if err := generation.Validate(); err != nil {
256		return PreviewPublication{}, ErrInvalidMessage
257	}
258	result := PreviewPublication{Epoch: epoch, Connection: connection, Generation: generation}
259	switch payload := message.GetResult().(type) {
260	case *wire.PreviewPublication_Preview:
261		if payload.Preview == nil {
262			return PreviewPublication{}, ErrInvalidMessage
263		}
264		preview, err := remotePreviewFromWire(payload.Preview)
265		if err != nil {
266			return PreviewPublication{}, err
267		}
268		result.Preview = preview
269	case *wire.PreviewPublication_Error:
270		if payload.Error == nil {
271			return PreviewPublication{}, ErrInvalidMessage
272		}
273		detail, err := errorDetailFromWire(payload.Error)
274		if err != nil {
275			return PreviewPublication{}, ErrInvalidMessage
276		}
277		result.Error = detail
278		result.HasError = true
279	default:
280		// An absent result is malformed: exactly one member is required.
281		return PreviewPublication{}, ErrInvalidMessage
282	}
283	return result, nil
284}

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