An implementation of the BARE message format for Go.
Status
This mostly works, but you may run into some edge cases with union types.
An example is provided in the examples
directory. Here is a basic
introduction:
$ cat schema.bare
type Address {
address: [4]string
city: string
state: string
country: string
}
$ go run git.sr.ht/~sircmpwn/go-bare/cmd/gen -p models schema.bare models/gen.go
Then you can write something like the following:
import "models"
/* ... */
bytes := []byte{ /* ... */ }
var addr Address
err := addr.Decode(bytes)
You can also add custom types and skip generating them by passing the -s TypeName
flag to gen, then providing your own implementation. For example, to
rig up time.Time with a custom "Time" BARE type, add this to your BARE schema:
type Time string
Then pass -s Time
to gen, and provide your own implementation of Time in the
same package. See examples/time.go
for an example of such an implementation.
For many use-cases, it may be more convenient to write your types manually and
use Marshal and Unmarshal directly. If you choose this approach, you may also
use git.sr.ht/~sircmpwn/go-bare/schema.SchemaFor
to generate a BARE schema
language document describing your structs.
package main
import (
"fmt"
"git.sr.ht/~sircmpwn/go-bare"
)
// type Coordinates {
// x: int
// y: int
// z: int
// q: optional<int>
// }
type Coordinates struct {
X uint
Y uint
Z uint
Q *uint
}
func main() {
var coords Coordinates
payload := []byte{0x01, 0x02, 0x03, 0x01, 0x04}
err := bare.Unmarshal(payload, &coords)
if err != nil {
panic(err)
}
fmt.Printf("coords: %d, %d, %d (%d)\n",
coords.X, coords.Y, coords.Z, *coords.Q) /* coords: 1, 2, 3 (4) */
}
To use union types, you need to define an interface to represent the union of
possible values, and this interface needs to implement bare.Union
:
type Person interface {
Union
}
Then, for each possible union type, implement the interface:
type Employee struct { /* ... */ }
func (e Employee) IsUnion() {}
type Customer struct { /* ... */ }
func (c Customer) IsUnion() {}
The IsUnion function is necessary to make the type compatible with the Union interface. Then, to marshal and unmarshal using this union type, you need to tell go-bare about your union:
func init() {
// The first argument is a pointer of the union interface, and the
// subsequent arguments are values of each possible subtype, in ascending
// order of union tag:
bare.RegisterUnion((*Person)(nil)).
Member(*new(Employee), 0).
Member(*new(Customer), 1)
}
This is all done for you if you use code generation.
Send patches and questions to ~sircmpwn/public-inbox@lists.sr.ht