mirror of
https://github.com/zap-proto/pb2zap.git
synced 2026-10-30 15:04:14 +00:00
Syntactic go/ast codemod (runs on non-compiling trees). v1 rewrites the one
transform that is unambiguous from syntax — message construction:
&pb.Msg{F:v} / pb.Msg{F:v} -> wire.NewMsg(wire.MsgInput{F:v})
fixes imports (add wire, drop pb when fully unused), and reports the pb
selectors it leaves (field reads/writes a human owns — immutable ZAP has no
field-write; reads need accessors, which need type info).
Rule{PB,Wire,Name}; Rewrite(file,src,rules)->(out,changed,pbLeft); CLI
pb2zap -map rules.txt [-w] ./... Pure transform + thin CLI, x/tools only.
Tested: construction rewrite + import drop; import-kept-when-still-used.
NOTE: v1 is the safe scaffold. Construction alone changes a value's type
(*pb.Msg -> []byte), so a full tree migration needs the COORDINATED type-
aware transform (construct + client-call + field-read together) = v2.
82 lines
2.1 KiB
Go
82 lines
2.1 KiB
Go
package pb2zap
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func TestRewrite_Construction(t *testing.T) {
|
|
src := `package x
|
|
|
|
import (
|
|
"context"
|
|
|
|
mount_pb "example.com/pb/mount_pb"
|
|
)
|
|
|
|
func do(ctx context.Context) {
|
|
a := &mount_pb.ConfigureRequest{CollectionCapacity: 5}
|
|
b := mount_pb.ConfigureResponse{}
|
|
_, _ = a, b
|
|
}
|
|
`
|
|
rules := []Rule{{PB: "example.com/pb/mount_pb", Wire: "example.com/wire/mount", Name: "mountwire"}}
|
|
out, changed, pbLeft, err := Rewrite("x.go", []byte(src), rules)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if !changed {
|
|
t.Fatal("expected changed")
|
|
}
|
|
s := string(out)
|
|
for _, want := range []string{
|
|
"mountwire.NewConfigureRequest(mountwire.ConfigureRequestInput{CollectionCapacity: 5})",
|
|
"mountwire.NewConfigureResponse(mountwire.ConfigureResponseInput{})",
|
|
`mountwire "example.com/wire/mount"`,
|
|
} {
|
|
if !strings.Contains(s, want) {
|
|
t.Errorf("missing %q in:\n%s", want, s)
|
|
}
|
|
}
|
|
if strings.Contains(s, "mount_pb") {
|
|
t.Errorf("mount_pb import should be gone:\n%s", s)
|
|
}
|
|
if pbLeft != 0 {
|
|
t.Errorf("pbLeft = %d, want 0", pbLeft)
|
|
}
|
|
}
|
|
|
|
// TestRewrite_KeepsImportWhenStillUsed proves pb2zap rewrites construction but
|
|
// keeps the pb import (and reports pbLeft) when other pb uses remain — it never
|
|
// guesses field reads.
|
|
func TestRewrite_KeepsImportWhenStillUsed(t *testing.T) {
|
|
src := `package x
|
|
|
|
import filer_pb "example.com/pb/filer_pb"
|
|
|
|
func do(e *filer_pb.Entry) []byte {
|
|
r := &filer_pb.LookupRequest{Name: e.Name}
|
|
return r
|
|
}
|
|
`
|
|
rules := []Rule{{PB: "example.com/pb/filer_pb", Wire: "example.com/wire/filer", Name: "filerwire"}}
|
|
out, changed, pbLeft, err := Rewrite("x.go", []byte(src), rules)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if !changed {
|
|
t.Fatal("expected changed")
|
|
}
|
|
s := string(out)
|
|
if !strings.Contains(s, "filerwire.NewLookupRequest(filerwire.LookupRequestInput{Name: e.Name})") {
|
|
t.Errorf("construction not rewritten:\n%s", s)
|
|
}
|
|
// *filer_pb.Entry param still references filer_pb -> import stays, pbLeft>0.
|
|
if !strings.Contains(s, "filer_pb.Entry") {
|
|
t.Errorf("expected filer_pb.Entry type ref to remain (human owns it):\n%s", s)
|
|
}
|
|
if pbLeft == 0 {
|
|
t.Errorf("pbLeft = 0, want > 0 (the *filer_pb.Entry param)")
|
|
}
|
|
}
|