R6(c): share the nested FB builders between notify and gateway transcode

Extract the FlatBuffers builders for the wire tables shared by the backend push
encoder and the gateway edge transcoder — GameView, MoveRecord, StateView,
AccountRef, Invitation and their nested rows — into a new scrabble/pkg/wire
package. Both callers keep their local builder signatures (no call sites move)
but now map their own source types (the backend's notify.* payloads and the
decoded engine.MoveRecord; the gateway's backendclient.* REST DTOs) to neutral
wire.* structs and delegate the construction to package wire, the single
definition of the nested-table layout.

Behaviour-preserving: the verified-identical field sets mean the wire bytes
decode the same, and the notify + transcode round-trip tests pass unchanged. The
fiddly Start/Add/End + reverse-prepend vector boilerplate now lives once; the two
encode files shrink while pkg/wire carries the shared logic.
This commit is contained in:
Ilia Denisov
2026-06-10 17:21:18 +02:00
parent 1079878654
commit b47c47e969
4 changed files with 459 additions and 305 deletions
+22 -41
View File
@@ -5,6 +5,7 @@ import (
"scrabble/gateway/internal/backendclient"
fb "scrabble/pkg/fbs/scrabblefb"
"scrabble/pkg/wire"
)
// Social encoders: friends, blocks, invitations, statistics and GCG. They follow
@@ -12,12 +13,7 @@ import (
// buildAccountRef builds an AccountRef table and returns its offset.
func buildAccountRef(b *flatbuffers.Builder, r backendclient.AccountRefResp) flatbuffers.UOffsetT {
id := b.CreateString(r.AccountID)
name := b.CreateString(r.DisplayName)
fb.AccountRefStart(b)
fb.AccountRefAddAccountId(b, id)
fb.AccountRefAddDisplayName(b, name)
return fb.AccountRefEnd(b)
return wire.BuildAccountRef(b, wire.AccountRef{AccountID: r.AccountID, DisplayName: r.DisplayName})
}
// buildAccountRefVector builds a [AccountRef] vector using the table-specific
@@ -110,43 +106,28 @@ func encodeStats(r backendclient.StatsResp) []byte {
// buildInvitation builds an Invitation table and returns its offset.
func buildInvitation(b *flatbuffers.Builder, inv backendclient.InvitationResp) flatbuffers.UOffsetT {
inviteeOffs := make([]flatbuffers.UOffsetT, len(inv.Invitees))
invitees := make([]wire.InvitationInvitee, len(inv.Invitees))
for i, iv := range inv.Invitees {
aid := b.CreateString(iv.AccountID)
name := b.CreateString(iv.DisplayName)
resp := b.CreateString(iv.Response)
fb.InvitationInviteeStart(b)
fb.InvitationInviteeAddAccountId(b, aid)
fb.InvitationInviteeAddDisplayName(b, name)
fb.InvitationInviteeAddSeat(b, int32(iv.Seat))
fb.InvitationInviteeAddResponse(b, resp)
inviteeOffs[i] = fb.InvitationInviteeEnd(b)
invitees[i] = wire.InvitationInvitee{
AccountID: iv.AccountID,
DisplayName: iv.DisplayName,
Seat: iv.Seat,
Response: iv.Response,
}
}
fb.InvitationStartInviteesVector(b, len(inviteeOffs))
for i := len(inviteeOffs) - 1; i >= 0; i-- {
b.PrependUOffsetT(inviteeOffs[i])
}
invitees := b.EndVector(len(inviteeOffs))
inviter := buildAccountRef(b, inv.Inviter)
id := b.CreateString(inv.ID)
variant := b.CreateString(inv.Variant)
dropout := b.CreateString(inv.DropoutTiles)
status := b.CreateString(inv.Status)
gameID := b.CreateString(inv.GameID)
fb.InvitationStart(b)
fb.InvitationAddId(b, id)
fb.InvitationAddInviter(b, inviter)
fb.InvitationAddInvitees(b, invitees)
fb.InvitationAddVariant(b, variant)
fb.InvitationAddTurnTimeoutSecs(b, int32(inv.TurnTimeoutSecs))
fb.InvitationAddHintsAllowed(b, inv.HintsAllowed)
fb.InvitationAddHintsPerPlayer(b, int32(inv.HintsPerPlayer))
fb.InvitationAddDropoutTiles(b, dropout)
fb.InvitationAddStatus(b, status)
fb.InvitationAddGameId(b, gameID)
fb.InvitationAddExpiresAtUnix(b, inv.ExpiresAtUnix)
return fb.InvitationEnd(b)
return wire.BuildInvitation(b, wire.Invitation{
ID: inv.ID,
Inviter: wire.AccountRef{AccountID: inv.Inviter.AccountID, DisplayName: inv.Inviter.DisplayName},
Invitees: invitees,
Variant: inv.Variant,
TurnTimeoutSecs: inv.TurnTimeoutSecs,
HintsAllowed: inv.HintsAllowed,
HintsPerPlayer: inv.HintsPerPlayer,
DropoutTiles: inv.DropoutTiles,
Status: inv.Status,
GameID: inv.GameID,
ExpiresAtUnix: inv.ExpiresAtUnix,
})
}
// encodeInvitation builds an Invitation payload.