yakumo.izuru 154d055f9f Prefer immortal.run over runit and rc.d, use vendored modules
for convenience.

Signed-off-by: Izuru Yakumo <yakumo.izuru@chaotic.ninja>

git-svn-id: https://svn.yakumo.dev/yakumo.izuru/suika/trunk@822 f0ae65fe-ee39-954e-97ec-027ff2717ef4
2023-08-20 14:36:11 +00:00
..

go-bare godocs.io builds.sr.ht status

An implementation of the BARE message format for Go.

Status

This mostly works, but you may run into some edge cases with union types.

Code generation

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.

Marshal usage

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) */
}

Unions

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.

Contributing, getting help

Send patches and questions to ~sircmpwn/public-inbox@lists.sr.ht