/* 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"