mirror of
https://github.com/luxfi/zap.git
synced 2026-10-30 15:04:16 +00:00
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.
61 lines
1.7 KiB
Go
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")
|
|
}
|
|
}
|