Files
zeekay bfaa9812dd v1/codegen: emit singular NESTED object fields (zero-copy, typed)
Completes the variable-length codegen (strings/bytes/lists/nested) — the
last shape needed before protoc-gen-zap can map every proto message field
mechanically.

Runtime (v1/nested.go):
- NestedAt[S,N]: read an out-of-line 4-byte object pointer as a typed
  View[N]; null/OOB -> zero View (proto3 unset).
- WriteNested[S,N]: build a flat nested object into the parent tail and
  patch the pointer; the singular peer of WriteList. Composes recursively
  (nested may carry scalars, strings, bytes, lists, further nesting).

Codegen (schema.go, emit.go):
- Field.Nested *NestedMsg (4-byte object pointer, distinct from the 8-byte
  list/tail pointer); IsNested().
- Constructor takes *Value (nil => null), nil-guarded WriteNested build.
- emitNestedSupport: value struct + NestedAt read accessor.
- emitElemWrites: shared scalar/string/bytes element-write body, so list
  elements now also support string/bytes sub-fields (repeated-of-message).
- ListElem/NestedMsg carry Wire (element WireName) for Offset<Wire>_<F>.

Proven end-to-end: runtime roundtrip (nested w/ own string tail + null
case), generator-output tests (go/parser-valid), and a generated-code wire
roundtrip (testpkg/nestwire). Full v1+codegen suite green, no regression.
2026-06-23 16:28:51 -07:00

61 lines
1.7 KiB
Go

// Copyright (C) 2026, Lux Industries Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package nestwire
import (
"testing"
zapv1 "github.com/luxfi/zap/v1"
)
// TestRoundTrip_Nested proves the GENERATED singular-nested code works on
// the wire: build an Outer with a nested Inner (which itself has a string
// tail), wrap the bytes back, and read the nested scalar + string zero-copy
// through the generated NestedAt accessor.
func TestRoundTrip_Nested(t *testing.T) {
t.Parallel()
_, buf := NewOuter(0xBEEF, &Inner{ID: 7, Label: "hello-nested"})
got, err := WrapOuter(buf)
if err != nil {
t.Fatalf("WrapOuter: %v", err)
}
// Parent scalar.
if seq := zapv1.Read(got, OuterSchemaFields.Seq); seq != 0xBEEF {
t.Errorf("Seq = %#x, want 0xBEEF", seq)
}
// Nested object: present, with its own scalar + string tail.
inner := OuterInner(got)
if inner.IsZero() {
t.Fatal("nested Inner is zero, want present")
}
if id := zapv1.Read(inner, InnerSchemaFields.ID); id != 7 {
t.Errorf("Inner.ID = %d, want 7", id)
}
if lbl := InnerLabel(inner); lbl != "hello-nested" {
t.Errorf("Inner.Label = %q, want %q", lbl, "hello-nested")
}
}
// TestRoundTrip_NestedNull proves an unset nested message (nil) encodes as
// a null pointer and reads back as the zero View.
func TestRoundTrip_NestedNull(t *testing.T) {
t.Parallel()
_, buf := NewOuter(1, nil)
got, err := WrapOuter(buf)
if err != nil {
t.Fatalf("WrapOuter: %v", err)
}
if seq := zapv1.Read(got, OuterSchemaFields.Seq); seq != 1 {
t.Errorf("Seq = %d, want 1", seq)
}
if inner := OuterInner(got); !inner.IsZero() {
t.Error("unset nested Inner should be the zero View, got present")
}
}