Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ require (
google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5
google.golang.org/grpc v1.75.0
google.golang.org/protobuf v1.36.8
gopkg.in/yaml.v2 v2.4.0
gopkg.in/yaml.v3 v3.0.1
sigs.k8s.io/yaml v1.4.0
)
Expand Down Expand Up @@ -152,5 +153,4 @@ require (
golang.org/x/text v0.28.0 // indirect
golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20250707201910-8d1bb00bc6a7 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
)
20 changes: 20 additions & 0 deletions pkg/loop/cmd/genwiring/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Tiny code generator that turns your domain model into:

- .proto service + messages
- single Go file (rpc.go) with:
- typed gRPC Client
- typed gRPC Server (thin shim over your implementation)
- pb ↔ domain converters for every user message
- oneof (interface) adapters
- safe handling of bytes, repeated fields, and fixed-size arrays
- an optional rpc_test.go with a single bufconn server and subtests (happy-path roundtrip)
Usage example:
Suppose your interface is in pkg/path and its called MyInterface:
1. Generate proto files + go wrappers
//go:generate bash -c "set -euo pipefail; mkdir -p ./gen/pb ./gen/wrap && go run ./genwiring --pkg pkg/path --interface MyInterface --config config.yaml --service MyService --proto-pkg loop.test --proto-go-package my/proto/package --proto-out ./path/to/my.proto --go-out ./path/to/my/go/wrappers --go-pkg my/go/pkg"
2. Use protoc to generate go proto types
//go:generate bash -c "set -euo pipefail; protoc -I ./gen/pb --go_out=paths=source_relative:./gen/pb --go-grpc_out=paths=source_relative:./gen/pb ./gen/pb/service.proto"
The output will be:
- A strongly typed Client that does domain <-> pb conversions rpc.go, rpc_test.go
- A server wrapper that converts pb and calls your impl.
A full usage example with config in pkg/loop/internal/generator/testdata
53 changes: 53 additions & 0 deletions pkg/loop/cmd/genwiring/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package main

import (
"flag"
"fmt"
"log"
"os"

"github.com/smartcontractkit/chainlink-common/pkg/loop/internal/generator"
)

func main() {
var (
pkgPath = flag.String("pkg", "", "Go package path containing the interface (module-qualified)")
ifaceName = flag.String("interface", "", "Interface name (e.g. ExampleService)")
serviceName = flag.String("service", "Example", "Proto service name (e.g. Example)")
protoOut = flag.String("proto-out", "", ".proto output path")
goOutDir = flag.String("go-out", "", "Directory for generated Go wrappers")
goPkg = flag.String("go-pkg", "", "Go package import path for generated wrappers")
protoPkg = flag.String("proto-pkg", "loop.solana", "Proto package name")
goPackageOpt = flag.String("proto-go-package", "github.com/smartcontractkit/chainlink-common/pkg/loop/solana", "option go_package value for the .proto")
configPath = flag.String("config", "", "YAML config for externals/aliases/enums")
)
flag.Parse()

if *pkgPath == "" || *ifaceName == "" || *protoOut == "" || *goOutDir == "" || *goPkg == "" {
log.Fatalf("missing required flags: --pkg, --interface, --proto-out, --go-out, --go-pkg")
}

cfg, err := generator.LoadConfig(*configPath)
if err != nil {
log.Fatalf("load config: %v", err)
}

svc, err := generator.ParseInterface(*pkgPath, *ifaceName, cfg)
if err != nil {
log.Fatalf("parse: %v", err)
}
svc.ServiceName = *serviceName
svc.ProtoPkg = *protoPkg
svc.OptionGoPackage = *goPackageOpt
svc.WrapGoPackage = *goPkg

if err := os.MkdirAll(*goOutDir, 0o755); err != nil {
log.Fatalf("mkdir go-out: %v", err)
}

if err := generator.RenderAll(*protoOut, *goOutDir, svc); err != nil {
log.Fatalf("render: %v", err)
}

fmt.Printf("Generated:\n %s\n %s/server.go\n %s/client.go\n", *protoOut, *goOutDir, *goOutDir)
}
Loading
Loading