forked from BigfootDev/flatbuffers
Add --go-module-name flag to support generating Go module compatible code (#7651)
* Add --go-module-name flag to support generating code for go modules * Rename echo example folder * Grammar * Update readme for go-echo example * Update readme for go-echo example * Re-enable go modules after test is done
This commit is contained in:
27
examples/go-echo/README.md
Normal file
27
examples/go-echo/README.md
Normal file
@@ -0,0 +1,27 @@
|
||||
# Go Echo Example
|
||||
|
||||
A simple example demonstrating how to send flatbuffers over the network in Go.
|
||||
|
||||
## Generate flatbuffer code
|
||||
|
||||
```
|
||||
flatc -g --gen-object-api --go-module-name echo hero.fbs net.fbs
|
||||
```
|
||||
|
||||
## Running example
|
||||
|
||||
1. Run go mod tidy to get dependencies
|
||||
```
|
||||
go mod tidy
|
||||
```
|
||||
|
||||
2. Start a server
|
||||
```
|
||||
go run server/server.go
|
||||
```
|
||||
|
||||
3. Run the client in another terminal
|
||||
```
|
||||
go run client/client.go
|
||||
```
|
||||
|
||||
62
examples/go-echo/client/client.go
Normal file
62
examples/go-echo/client/client.go
Normal file
@@ -0,0 +1,62 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
|
||||
"echo/hero"
|
||||
"echo/net"
|
||||
|
||||
flatbuffers "github.com/google/flatbuffers/go"
|
||||
)
|
||||
|
||||
func RequestBody() *bytes.Reader {
|
||||
b := flatbuffers.NewBuilder(0)
|
||||
r := net.RequestT{Player: &hero.WarriorT{Name: "Krull", Hp: 100}}
|
||||
b.Finish(r.Pack(b))
|
||||
|
||||
// Encode builder head in last 4 bytes of request body
|
||||
buf := make([]byte, 4)
|
||||
flatbuffers.WriteUOffsetT(buf, b.Head())
|
||||
buf = append(b.Bytes, buf...)
|
||||
|
||||
return bytes.NewReader(buf)
|
||||
}
|
||||
|
||||
func ReadResponse(r *http.Response) {
|
||||
body, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
fmt.Printf("Unable to read request body: %v\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Last 4 bytes is offset.
|
||||
off := flatbuffers.GetUOffsetT(body[len(body)-4:])
|
||||
buf := body[:len(body) - 4]
|
||||
|
||||
res := net.GetRootAsResponse(buf, off)
|
||||
player := res.Player(nil)
|
||||
|
||||
fmt.Printf("Got response (name: %v, hp: %v)\n", string(player.Name()), player.Hp())
|
||||
}
|
||||
|
||||
func main() {
|
||||
|
||||
body := RequestBody()
|
||||
req, err := http.NewRequest("POST", "http://localhost:8080/echo", body)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
client := http.DefaultClient
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
ReadResponse(resp)
|
||||
}
|
||||
5
examples/go-echo/go.mod
Normal file
5
examples/go-echo/go.mod
Normal file
@@ -0,0 +1,5 @@
|
||||
module echo
|
||||
|
||||
go 1.19
|
||||
|
||||
require github.com/google/flatbuffers v22.10.26+incompatible
|
||||
6
examples/go-echo/hero.fbs
Normal file
6
examples/go-echo/hero.fbs
Normal file
@@ -0,0 +1,6 @@
|
||||
namespace hero;
|
||||
|
||||
table Warrior {
|
||||
name: string;
|
||||
hp: uint32;
|
||||
}
|
||||
93
examples/go-echo/hero/Warrior.go
Normal file
93
examples/go-echo/hero/Warrior.go
Normal file
@@ -0,0 +1,93 @@
|
||||
// Code generated by the FlatBuffers compiler. DO NOT EDIT.
|
||||
|
||||
package hero
|
||||
|
||||
import (
|
||||
flatbuffers "github.com/google/flatbuffers/go"
|
||||
)
|
||||
|
||||
type WarriorT struct {
|
||||
Name string `json:"name"`
|
||||
Hp uint32 `json:"hp"`
|
||||
}
|
||||
|
||||
func (t *WarriorT) Pack(builder *flatbuffers.Builder) flatbuffers.UOffsetT {
|
||||
if t == nil { return 0 }
|
||||
nameOffset := builder.CreateString(t.Name)
|
||||
WarriorStart(builder)
|
||||
WarriorAddName(builder, nameOffset)
|
||||
WarriorAddHp(builder, t.Hp)
|
||||
return WarriorEnd(builder)
|
||||
}
|
||||
|
||||
func (rcv *Warrior) UnPackTo(t *WarriorT) {
|
||||
t.Name = string(rcv.Name())
|
||||
t.Hp = rcv.Hp()
|
||||
}
|
||||
|
||||
func (rcv *Warrior) UnPack() *WarriorT {
|
||||
if rcv == nil { return nil }
|
||||
t := &WarriorT{}
|
||||
rcv.UnPackTo(t)
|
||||
return t
|
||||
}
|
||||
|
||||
type Warrior struct {
|
||||
_tab flatbuffers.Table
|
||||
}
|
||||
|
||||
func GetRootAsWarrior(buf []byte, offset flatbuffers.UOffsetT) *Warrior {
|
||||
n := flatbuffers.GetUOffsetT(buf[offset:])
|
||||
x := &Warrior{}
|
||||
x.Init(buf, n+offset)
|
||||
return x
|
||||
}
|
||||
|
||||
func GetSizePrefixedRootAsWarrior(buf []byte, offset flatbuffers.UOffsetT) *Warrior {
|
||||
n := flatbuffers.GetUOffsetT(buf[offset+flatbuffers.SizeUint32:])
|
||||
x := &Warrior{}
|
||||
x.Init(buf, n+offset+flatbuffers.SizeUint32)
|
||||
return x
|
||||
}
|
||||
|
||||
func (rcv *Warrior) Init(buf []byte, i flatbuffers.UOffsetT) {
|
||||
rcv._tab.Bytes = buf
|
||||
rcv._tab.Pos = i
|
||||
}
|
||||
|
||||
func (rcv *Warrior) Table() flatbuffers.Table {
|
||||
return rcv._tab
|
||||
}
|
||||
|
||||
func (rcv *Warrior) Name() []byte {
|
||||
o := flatbuffers.UOffsetT(rcv._tab.Offset(4))
|
||||
if o != 0 {
|
||||
return rcv._tab.ByteVector(o + rcv._tab.Pos)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (rcv *Warrior) Hp() uint32 {
|
||||
o := flatbuffers.UOffsetT(rcv._tab.Offset(6))
|
||||
if o != 0 {
|
||||
return rcv._tab.GetUint32(o + rcv._tab.Pos)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (rcv *Warrior) MutateHp(n uint32) bool {
|
||||
return rcv._tab.MutateUint32Slot(6, n)
|
||||
}
|
||||
|
||||
func WarriorStart(builder *flatbuffers.Builder) {
|
||||
builder.StartObject(2)
|
||||
}
|
||||
func WarriorAddName(builder *flatbuffers.Builder, name flatbuffers.UOffsetT) {
|
||||
builder.PrependUOffsetTSlot(0, flatbuffers.UOffsetT(name), 0)
|
||||
}
|
||||
func WarriorAddHp(builder *flatbuffers.Builder, hp uint32) {
|
||||
builder.PrependUint32Slot(1, hp, 0)
|
||||
}
|
||||
func WarriorEnd(builder *flatbuffers.Builder) flatbuffers.UOffsetT {
|
||||
return builder.EndObject()
|
||||
}
|
||||
11
examples/go-echo/net.fbs
Normal file
11
examples/go-echo/net.fbs
Normal file
@@ -0,0 +1,11 @@
|
||||
include "hero.fbs";
|
||||
|
||||
namespace net;
|
||||
|
||||
table Request {
|
||||
player: hero.Warrior;
|
||||
}
|
||||
|
||||
table Response {
|
||||
player: hero.Warrior;
|
||||
}
|
||||
82
examples/go-echo/net/Request.go
Normal file
82
examples/go-echo/net/Request.go
Normal file
@@ -0,0 +1,82 @@
|
||||
// Code generated by the FlatBuffers compiler. DO NOT EDIT.
|
||||
|
||||
package net
|
||||
|
||||
import (
|
||||
flatbuffers "github.com/google/flatbuffers/go"
|
||||
|
||||
hero "echo/hero"
|
||||
)
|
||||
|
||||
type RequestT struct {
|
||||
Player *hero.WarriorT `json:"player"`
|
||||
}
|
||||
|
||||
func (t *RequestT) Pack(builder *flatbuffers.Builder) flatbuffers.UOffsetT {
|
||||
if t == nil { return 0 }
|
||||
playerOffset := t.Player.Pack(builder)
|
||||
RequestStart(builder)
|
||||
RequestAddPlayer(builder, playerOffset)
|
||||
return RequestEnd(builder)
|
||||
}
|
||||
|
||||
func (rcv *Request) UnPackTo(t *RequestT) {
|
||||
t.Player = rcv.Player(nil).UnPack()
|
||||
}
|
||||
|
||||
func (rcv *Request) UnPack() *RequestT {
|
||||
if rcv == nil { return nil }
|
||||
t := &RequestT{}
|
||||
rcv.UnPackTo(t)
|
||||
return t
|
||||
}
|
||||
|
||||
type Request struct {
|
||||
_tab flatbuffers.Table
|
||||
}
|
||||
|
||||
func GetRootAsRequest(buf []byte, offset flatbuffers.UOffsetT) *Request {
|
||||
n := flatbuffers.GetUOffsetT(buf[offset:])
|
||||
x := &Request{}
|
||||
x.Init(buf, n+offset)
|
||||
return x
|
||||
}
|
||||
|
||||
func GetSizePrefixedRootAsRequest(buf []byte, offset flatbuffers.UOffsetT) *Request {
|
||||
n := flatbuffers.GetUOffsetT(buf[offset+flatbuffers.SizeUint32:])
|
||||
x := &Request{}
|
||||
x.Init(buf, n+offset+flatbuffers.SizeUint32)
|
||||
return x
|
||||
}
|
||||
|
||||
func (rcv *Request) Init(buf []byte, i flatbuffers.UOffsetT) {
|
||||
rcv._tab.Bytes = buf
|
||||
rcv._tab.Pos = i
|
||||
}
|
||||
|
||||
func (rcv *Request) Table() flatbuffers.Table {
|
||||
return rcv._tab
|
||||
}
|
||||
|
||||
func (rcv *Request) Player(obj *hero.Warrior) *hero.Warrior {
|
||||
o := flatbuffers.UOffsetT(rcv._tab.Offset(4))
|
||||
if o != 0 {
|
||||
x := rcv._tab.Indirect(o + rcv._tab.Pos)
|
||||
if obj == nil {
|
||||
obj = new(hero.Warrior)
|
||||
}
|
||||
obj.Init(rcv._tab.Bytes, x)
|
||||
return obj
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func RequestStart(builder *flatbuffers.Builder) {
|
||||
builder.StartObject(1)
|
||||
}
|
||||
func RequestAddPlayer(builder *flatbuffers.Builder, player flatbuffers.UOffsetT) {
|
||||
builder.PrependUOffsetTSlot(0, flatbuffers.UOffsetT(player), 0)
|
||||
}
|
||||
func RequestEnd(builder *flatbuffers.Builder) flatbuffers.UOffsetT {
|
||||
return builder.EndObject()
|
||||
}
|
||||
82
examples/go-echo/net/Response.go
Normal file
82
examples/go-echo/net/Response.go
Normal file
@@ -0,0 +1,82 @@
|
||||
// Code generated by the FlatBuffers compiler. DO NOT EDIT.
|
||||
|
||||
package net
|
||||
|
||||
import (
|
||||
flatbuffers "github.com/google/flatbuffers/go"
|
||||
|
||||
hero "echo/hero"
|
||||
)
|
||||
|
||||
type ResponseT struct {
|
||||
Player *hero.WarriorT `json:"player"`
|
||||
}
|
||||
|
||||
func (t *ResponseT) Pack(builder *flatbuffers.Builder) flatbuffers.UOffsetT {
|
||||
if t == nil { return 0 }
|
||||
playerOffset := t.Player.Pack(builder)
|
||||
ResponseStart(builder)
|
||||
ResponseAddPlayer(builder, playerOffset)
|
||||
return ResponseEnd(builder)
|
||||
}
|
||||
|
||||
func (rcv *Response) UnPackTo(t *ResponseT) {
|
||||
t.Player = rcv.Player(nil).UnPack()
|
||||
}
|
||||
|
||||
func (rcv *Response) UnPack() *ResponseT {
|
||||
if rcv == nil { return nil }
|
||||
t := &ResponseT{}
|
||||
rcv.UnPackTo(t)
|
||||
return t
|
||||
}
|
||||
|
||||
type Response struct {
|
||||
_tab flatbuffers.Table
|
||||
}
|
||||
|
||||
func GetRootAsResponse(buf []byte, offset flatbuffers.UOffsetT) *Response {
|
||||
n := flatbuffers.GetUOffsetT(buf[offset:])
|
||||
x := &Response{}
|
||||
x.Init(buf, n+offset)
|
||||
return x
|
||||
}
|
||||
|
||||
func GetSizePrefixedRootAsResponse(buf []byte, offset flatbuffers.UOffsetT) *Response {
|
||||
n := flatbuffers.GetUOffsetT(buf[offset+flatbuffers.SizeUint32:])
|
||||
x := &Response{}
|
||||
x.Init(buf, n+offset+flatbuffers.SizeUint32)
|
||||
return x
|
||||
}
|
||||
|
||||
func (rcv *Response) Init(buf []byte, i flatbuffers.UOffsetT) {
|
||||
rcv._tab.Bytes = buf
|
||||
rcv._tab.Pos = i
|
||||
}
|
||||
|
||||
func (rcv *Response) Table() flatbuffers.Table {
|
||||
return rcv._tab
|
||||
}
|
||||
|
||||
func (rcv *Response) Player(obj *hero.Warrior) *hero.Warrior {
|
||||
o := flatbuffers.UOffsetT(rcv._tab.Offset(4))
|
||||
if o != 0 {
|
||||
x := rcv._tab.Indirect(o + rcv._tab.Pos)
|
||||
if obj == nil {
|
||||
obj = new(hero.Warrior)
|
||||
}
|
||||
obj.Init(rcv._tab.Bytes, x)
|
||||
return obj
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func ResponseStart(builder *flatbuffers.Builder) {
|
||||
builder.StartObject(1)
|
||||
}
|
||||
func ResponseAddPlayer(builder *flatbuffers.Builder, player flatbuffers.UOffsetT) {
|
||||
builder.PrependUOffsetTSlot(0, flatbuffers.UOffsetT(player), 0)
|
||||
}
|
||||
func ResponseEnd(builder *flatbuffers.Builder) flatbuffers.UOffsetT {
|
||||
return builder.EndObject()
|
||||
}
|
||||
35
examples/go-echo/server/server.go
Normal file
35
examples/go-echo/server/server.go
Normal file
@@ -0,0 +1,35 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"echo/net"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
|
||||
flatbuffers "github.com/google/flatbuffers/go"
|
||||
)
|
||||
|
||||
func echo(w http.ResponseWriter, r *http.Request) {
|
||||
body, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
fmt.Printf("Unable to read request body: %v\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Last 4 bytes is offset. See client.go.
|
||||
off := flatbuffers.GetUOffsetT(body[len(body)-4:])
|
||||
buf := body[:len(body) - 4]
|
||||
|
||||
req := net.GetRootAsRequest(buf, off)
|
||||
player := req.Player(nil)
|
||||
|
||||
fmt.Printf("Got request (name: %v, hp: %v)\n", string(player.Name()), player.Hp())
|
||||
w.Write(body)
|
||||
}
|
||||
|
||||
func main() {
|
||||
http.HandleFunc("/echo", echo)
|
||||
|
||||
fmt.Println("Listening on port :8080")
|
||||
http.ListenAndServe(":8080", nil)
|
||||
}
|
||||
@@ -623,6 +623,7 @@ struct IDLOptions {
|
||||
bool binary_schema_gen_embed;
|
||||
std::string go_import;
|
||||
std::string go_namespace;
|
||||
std::string go_module_name;
|
||||
bool protobuf_ascii_alike;
|
||||
bool size_prefixed;
|
||||
std::string root_type;
|
||||
@@ -915,7 +916,7 @@ class Parser : public ParserState {
|
||||
|
||||
// Returns the number of characters were consumed when parsing a JSON string.
|
||||
std::ptrdiff_t BytesConsumed() const;
|
||||
|
||||
|
||||
// Set the root type. May override the one set in the schema.
|
||||
bool SetRootType(const char *name);
|
||||
|
||||
|
||||
@@ -152,6 +152,8 @@ const static FlatCOption options[] = {
|
||||
{ "", "go-import", "IMPORT",
|
||||
"Generate the overriding import for flatbuffers in Golang (default is "
|
||||
"\"github.com/google/flatbuffers/go\")." },
|
||||
{ "", "go-module-name", "",
|
||||
"Prefix local import paths of generated go code with the module name" },
|
||||
{ "", "raw-binary", "",
|
||||
"Allow binaries without file_identifier to be read. This may crash flatc "
|
||||
"given a mismatched schema." },
|
||||
@@ -448,6 +450,9 @@ int FlatCompiler::Compile(int argc, const char **argv) {
|
||||
} else if (arg == "--go-import") {
|
||||
if (++argi >= argc) Error("missing golang import" + arg, true);
|
||||
opts.go_import = argv[argi];
|
||||
} else if (arg == "--go-module-name") {
|
||||
if (++argi >= argc) Error("missing golang module name" + arg, true);
|
||||
opts.go_module_name = argv[argi];
|
||||
} else if (arg == "--defaults-json") {
|
||||
opts.output_default_scalars_in_json = true;
|
||||
} else if (arg == "--unknown-json") {
|
||||
|
||||
@@ -1532,7 +1532,12 @@ class GoGenerator : public BaseGenerator {
|
||||
|
||||
// Create the full path for the imported namespace (format: A/B/C).
|
||||
std::string NamespaceImportPath(const Namespace *ns) const {
|
||||
return namer_.Directories(*ns, SkipDir::OutputPathAndTrailingPathSeparator);
|
||||
std::string path =
|
||||
namer_.Directories(*ns, SkipDir::OutputPathAndTrailingPathSeparator);
|
||||
if (!parser_.opts.go_module_name.empty()) {
|
||||
path = parser_.opts.go_module_name + "/" + path;
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
// Ensure that a type is prefixed with its go package import name if it is
|
||||
|
||||
@@ -72,3 +72,6 @@ if [[ ${NOT_FMT_FILES} != "" ]]; then
|
||||
# enable this when enums are properly formated
|
||||
# exit 1
|
||||
fi
|
||||
|
||||
# Re-enable go modules when done tests
|
||||
go env -w GO111MODULE=on
|
||||
|
||||
Reference in New Issue
Block a user