165 lines
4.8 KiB
Go
165 lines
4.8 KiB
Go
/*
|
|
Package pogs provides functions to convert Cap'n Proto messages to and
|
|
from Go structs. pogs operates similarly to encoding/json: define a
|
|
struct that is optionally marked up with tags, then Insert and Extract
|
|
will copy the fields to and from the corresponding Cap'n Proto struct.
|
|
|
|
Inserting
|
|
|
|
To copy data into a Cap'n Proto struct, we use the Insert function.
|
|
Consider the following schema:
|
|
|
|
struct Message {
|
|
name @0 :Text;
|
|
body @1 :Text;
|
|
time @2 :Int64;
|
|
}
|
|
|
|
and the Go struct:
|
|
|
|
type Message struct {
|
|
Name string
|
|
Body string
|
|
Time int64
|
|
}
|
|
|
|
We can copy the Go struct into a Cap'n Proto struct like this:
|
|
|
|
_, arena, _ := capnp.NewMessage(capnp.SingleSegment(nil))
|
|
root, _ := myschema.NewRootMessage(arena)
|
|
m := &Message{"Alice", "Hello", 1294706395881547000}
|
|
err := pogs.Insert(myschema.Message_TypeID, root.Struct, m)
|
|
|
|
Note that if any field names in our Go struct don't match to a field in
|
|
the Cap'n Proto struct, Insert returns an error. We'll see how to fix
|
|
that in a moment.
|
|
|
|
Extracting
|
|
|
|
Copying data back out from a Cap'n Proto struct is quite similar: we
|
|
pass a pointer to our Go struct to Extract.
|
|
|
|
m := new(Message)
|
|
err := pogs.Extract(m, myschema.Message_TypeID, root.Struct)
|
|
|
|
Types
|
|
|
|
The mapping between Cap'n Proto types and underlying Go types is as
|
|
follows:
|
|
|
|
Bool -> bool
|
|
Int8, Int16, Int32, Int64 -> int8, int16, int32, int64
|
|
UInt8, UInt16, UInt32, UInt64 -> uint8, uint16, uint32, uint64
|
|
Float32, Float64 -> float32, float64
|
|
Text -> either []byte or string
|
|
Data -> []byte
|
|
List -> slice
|
|
enum -> uint16
|
|
struct -> a struct or pointer to struct
|
|
interface -> a capnp.Client or struct with
|
|
exactly one field, named
|
|
"Client", of type capnp.Client
|
|
|
|
Note that the unsized int and uint type can't be used: int and float
|
|
types must match in size. For Data and Text fields using []byte, the
|
|
filled-in byte slice will point to original segment.
|
|
|
|
Renaming and Omitting Fields
|
|
|
|
By default, the Go field name is the same as the Cap'n Proto schema
|
|
field name with the first letter capitalized. If we want to change this
|
|
mapping, we use the capnp field tag.
|
|
|
|
type MessageRenamed struct {
|
|
Subject string `capnp:"name"`
|
|
Body string
|
|
SentMillis int64 `capnp:"time"`
|
|
}
|
|
|
|
Using a "-" will cause the field to be ignored by the Insert and
|
|
Extract functions.
|
|
|
|
type ExtraFieldsMessage struct {
|
|
ID uint64 `capnp:"-"`
|
|
Name string
|
|
Body string
|
|
Time int64
|
|
}
|
|
|
|
Unions
|
|
|
|
Since Go does not have support for variant types, Go structs that want
|
|
to use fields inside a Cap'n Proto union must have an explicit
|
|
discriminant field called Which. The Extract function will populate the
|
|
Which field and the Insert function will read the Which field to
|
|
determine which field to set. Given this schema:
|
|
|
|
struct Shape {
|
|
area @0 :Float64;
|
|
|
|
union {
|
|
circle @1 :Float64;
|
|
square @2 :Float64;
|
|
}
|
|
}
|
|
|
|
the Go struct should look like this:
|
|
|
|
type Shape struct {
|
|
Area float64
|
|
|
|
Which myschema.Shape_Which // or any other uint16 type
|
|
Circle float64
|
|
Square float64
|
|
}
|
|
|
|
Attempting to use fields in a union without a uint16 Which field will
|
|
result in an error. There is one exception: we can declare our Which
|
|
field to be fixed to one particular union value by using a field tag.
|
|
|
|
type Square struct {
|
|
Which struct{} `capnp:",which=square"`
|
|
Area float64
|
|
Width float64 `capnp:"square"`
|
|
}
|
|
|
|
This can be useful if we want to use a different Go type depending on
|
|
which field in the union is set.
|
|
|
|
shape, err := myschema.ReadRootShape(msg)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
switch shape.Which() {
|
|
case myschema.Shape_Which_square:
|
|
sq := new(Square)
|
|
err = pogs.Extract(sq, myschema.Square_TypeID, shape.Struct)
|
|
return sq, err
|
|
case myschema.Shape_Which_circle:
|
|
// ...
|
|
}
|
|
|
|
Embedding
|
|
|
|
Anonymous struct fields are usually extracted or inserted as if their
|
|
inner exported fields were fields in the outer struct, subject to the
|
|
rules in the next paragraph. An anonymous struct field with a name
|
|
given in its capnp tag is treated as having that name, rather than being
|
|
anonymous. An anonymous struct field with a capnp tag of "-" will be
|
|
ignored.
|
|
|
|
The visibility rules for struct fields are amended for pogs in the same
|
|
way they are amended in encoding/json: if there are multiple fields at
|
|
the same level, and that level is the least nested, the following extra
|
|
rules apply:
|
|
|
|
1) Of those fields, if any are capnp-tagged, only tagged fields are
|
|
considered, even if there are multiple untagged fields that would
|
|
otherwise conflict.
|
|
2) If there is exactly one field (tagged or not according to the first
|
|
rule), that is selected.
|
|
3) Otherwise, there are multiple fields, and all are ignored; no error
|
|
occurs.
|
|
*/
|
|
package pogs // import "zombiezen.com/go/capnproto2/pogs"
|