pogs

package
v2.18.2+incompatible Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 13, 2021 License: MIT Imports: 8 Imported by: 0

Documentation

Overview

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.

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func Extract

func Extract(val interface{}, typeID uint64, s capnp.Struct) error

Extract copies s into val, a pointer to a Go struct.

Example
// books.capnp:
// struct Book {
//   title @0 :Text;
//   pageCount @1 :Int32;
// }

type Book struct {
	Title     string
	PageCount int32
}

// Read the message from bytes.
msg, err := capnp.Unmarshal(bookData)
if err != nil {
	panic(err)
}
root, err := msg.RootPtr()
if err != nil {
	panic(err)
}

// Extract the book from the root struct.
b := new(Book)
if err := pogs.Extract(b, books.Book_TypeID, root.Struct()); err != nil {
	panic(err)
}
fmt.Printf("%q has %d pages\n", b.Title, b.PageCount)
Output:

"War and Peace" has 1440 pages

func Insert

func Insert(typeID uint64, s capnp.Struct, val interface{}) error

Insert copies val, a pointer to a Go struct, into s.

Example
// books.capnp:
// struct Book {
//   title @0 :Text;
//   pageCount @1 :Int32;
// }

type Book struct {
	Title     string
	PageCount int32
}

// Allocate a new Cap'n Proto Book struct.
_, seg, err := capnp.NewMessage(capnp.SingleSegment(nil))
if err != nil {
	panic(err)
}
root, err := books.NewRootBook(seg)
if err != nil {
	panic(err)
}

// Insert the book struct into the Cap'n Proto struct.
b := &Book{
	Title:     "War and Peace",
	PageCount: 1440,
}
if err := pogs.Insert(books.Book_TypeID, root.Struct, b); err != nil {
	panic(err)
}
fmt.Println(root)
Output:

(title = "War and Peace", pageCount = 1440)

Types

This section is empty.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL