package capnp import ( "errors" "strconv" "golang.org/x/net/context" ) // An Interface is a reference to a client in a message's capability table. type Interface struct { seg *Segment cap CapabilityID } // NewInterface creates a new interface pointer. No allocation is // performed; s is only used for Segment()'s return value. func NewInterface(s *Segment, cap CapabilityID) Interface { return Interface{ seg: s, cap: cap, } } // ToInterface converts p to an Interface. // // Deprecated: Use Ptr.Interface. func ToInterface(p Pointer) Interface { if !IsValid(p) { return Interface{} } i, ok := p.underlying().(Interface) if !ok { return Interface{} } return i } // ToPtr converts the interface to a generic pointer. func (p Interface) ToPtr() Ptr { return Ptr{ seg: p.seg, lenOrCap: uint32(p.cap), flags: interfacePtrFlag, } } // Segment returns the segment this pointer came from. func (i Interface) Segment() *Segment { return i.seg } // IsValid returns whether the interface is valid. func (i Interface) IsValid() bool { return i.seg != nil } // HasData is always true. func (i Interface) HasData() bool { return true } // Capability returns the capability ID of the interface. func (i Interface) Capability() CapabilityID { return i.cap } // value returns a raw interface pointer with the capability ID. func (i Interface) value(paddr Address) rawPointer { if i.seg == nil { return 0 } return rawInterfacePointer(i.cap) } func (i Interface) underlying() Pointer { return i } // Client returns the client stored in the message's capability table // or nil if the pointer is invalid. func (i Interface) Client() Client { if i.seg == nil { return nil } tab := i.seg.msg.CapTable if int64(i.cap) >= int64(len(tab)) { return nil } return tab[i.cap] } // ErrNullClient is returned from a call made on a null client pointer. var ErrNullClient = errors.New("capnp: call on null client") // A CapabilityID is an index into a message's capability table. type CapabilityID uint32 // A Client represents an Cap'n Proto interface type. It is safe to use // from multiple goroutines. // // Generally, only RPC protocol implementers should provide types that // implement Client: call ordering guarantees, promises, and // synchronization are tricky to get right. Prefer creating a server // that wraps another interface than trying to implement Client. type Client interface { // Call starts executing a method and returns an answer that will hold // the resulting struct. The call's parameters must be placed before // Call() returns. // // Calls are delivered to the capability in the order they are made. // This guarantee is based on the concept of a capability // acknowledging delivery of a call: this is specific to an // implementation of Client. A type that implements Client must // guarantee that if foo() then bar() is called on a client, that // acknowledging foo() happens before acknowledging bar(). Call(call *Call) Answer // Close releases any resources associated with this client. // No further calls to the client should be made after calling Close. Close() error } // The Call type holds the record for an outgoing interface call. type Call struct { // Ctx is the context of the call. Ctx context.Context // Method is the interface ID and method ID, along with the optional name, // of the method to call. Method Method // Params is a struct containing parameters for the call. // This should be set when the RPC system receives a call for an // exported interface. It is mutually exclusive with ParamsFunc // and ParamsSize. Params Struct // ParamsFunc is a function that populates an allocated struct with // the parameters for the call. ParamsSize determines the size of the // struct to allocate. This is used when application code is using a // client. These settings should be set together; they are mutually // exclusive with Params. ParamsFunc func(Struct) error ParamsSize ObjectSize // Options passes RPC-specific options for the call. Options CallOptions } // Copy clones a call, ensuring that its Params are placed. // If Call.ParamsFunc is nil, then the same Call will be returned. func (call *Call) Copy(s *Segment) (*Call, error) { if call.ParamsFunc == nil { return call, nil } p, err := call.PlaceParams(s) if err != nil { return nil, err } return &Call{ Ctx: call.Ctx, Method: call.Method, Params: p, Options: call.Options, }, nil } // PlaceParams returns the parameters struct, allocating it inside // segment s as necessary. If s is nil, a new single-segment message // is allocated. func (call *Call) PlaceParams(s *Segment) (Struct, error) { if call.ParamsFunc == nil { return call.Params, nil } if s == nil { var err error _, s, err = NewMessage(SingleSegment(nil)) if err != nil { return Struct{}, err } } p, err := NewStruct(s, call.ParamsSize) if err != nil { return Struct{}, nil } err = call.ParamsFunc(p) return p, err } // CallOptions holds RPC-specific options for an interface call. // Its usage is similar to the values in context.Context, but is only // used for a single call: its values are not intended to propagate to // other callees. An example of an option would be the // Call.sendResultsTo field in rpc.capnp. type CallOptions struct { m map[interface{}]interface{} } // NewCallOptions builds a CallOptions value from a list of individual options. func NewCallOptions(opts []CallOption) CallOptions { co := CallOptions{make(map[interface{}]interface{})} for _, o := range opts { o.f(co) } return co } // Value retrieves the value associated with the options for this key, // or nil if no value is associated with this key. func (co CallOptions) Value(key interface{}) interface{} { return co.m[key] } // With creates a copy of the CallOptions value with other options applied. func (co CallOptions) With(opts []CallOption) CallOptions { newopts := CallOptions{make(map[interface{}]interface{})} for k, v := range co.m { newopts.m[k] = v } for _, o := range opts { o.f(newopts) } return newopts } // A CallOption is a function that modifies options on an interface call. type CallOption struct { f func(CallOptions) } // SetOptionValue returns a call option that associates a value to an // option key. This can be retrieved later with CallOptions.Value. func SetOptionValue(key, value interface{}) CallOption { return CallOption{func(co CallOptions) { co.m[key] = value }} } // An Answer is the deferred result of a client call, which is usually wrapped by a Pipeline. type Answer interface { // Struct waits until the call is finished and returns the result. Struct() (Struct, error) // The following methods are the same as in Client except with // an added transform parameter -- a path to the interface to use. PipelineCall(transform []PipelineOp, call *Call) Answer PipelineClose(transform []PipelineOp) error } // A Pipeline is a generic wrapper for an answer. type Pipeline struct { answer Answer parent *Pipeline op PipelineOp } // NewPipeline returns a new pipeline based on an answer. func NewPipeline(ans Answer) *Pipeline { return &Pipeline{answer: ans} } // Answer returns the answer the pipeline is derived from. func (p *Pipeline) Answer() Answer { return p.answer } // Transform returns the operations needed to transform the root answer // into the value p represents. func (p *Pipeline) Transform() []PipelineOp { n := 0 for q := p; q.parent != nil; q = q.parent { n++ } xform := make([]PipelineOp, n) for i, q := n-1, p; q.parent != nil; i, q = i-1, q.parent { xform[i] = q.op } return xform } // Struct waits until the answer is resolved and returns the struct // this pipeline represents. func (p *Pipeline) Struct() (Struct, error) { s, err := p.answer.Struct() if err != nil { return Struct{}, err } ptr, err := TransformPtr(s.ToPtr(), p.Transform()) if err != nil { return Struct{}, err } return ptr.Struct(), nil } // Client returns the client version of p. func (p *Pipeline) Client() *PipelineClient { return (*PipelineClient)(p) } // GetPipeline returns a derived pipeline which yields the pointer field given. func (p *Pipeline) GetPipeline(off uint16) *Pipeline { return p.GetPipelineDefault(off, nil) } // GetPipelineDefault returns a derived pipeline which yields the pointer field given, // defaulting to the value given. func (p *Pipeline) GetPipelineDefault(off uint16, def []byte) *Pipeline { return &Pipeline{ answer: p.answer, parent: p, op: PipelineOp{ Field: off, DefaultValue: def, }, } } // PipelineClient implements Client by calling to the pipeline's answer. type PipelineClient Pipeline func (pc *PipelineClient) transform() []PipelineOp { return (*Pipeline)(pc).Transform() } // Call calls Answer.PipelineCall with the pipeline's transform. func (pc *PipelineClient) Call(call *Call) Answer { return pc.answer.PipelineCall(pc.transform(), call) } // Close calls Answer.PipelineClose with the pipeline's transform. func (pc *PipelineClient) Close() error { return pc.answer.PipelineClose(pc.transform()) } // A PipelineOp describes a step in transforming a pipeline. // It maps closely with the PromisedAnswer.Op struct in rpc.capnp. type PipelineOp struct { Field uint16 DefaultValue []byte } // String returns a human-readable description of op. func (op PipelineOp) String() string { s := make([]byte, 0, 32) s = append(s, "get field "...) s = strconv.AppendInt(s, int64(op.Field), 10) if op.DefaultValue == nil { return string(s) } s = append(s, " with default"...) return string(s) } // A Method identifies a method along with an optional human-readable // description of the method. type Method struct { InterfaceID uint64 MethodID uint16 // Canonical name of the interface. May be empty. InterfaceName string // Method name as it appears in the schema. May be empty. MethodName string } // String returns a formatted string containing the interface name or // the method name if present, otherwise it uses the raw IDs. // This is suitable for use in error messages and logs. func (m *Method) String() string { buf := make([]byte, 0, 128) if m.InterfaceName == "" { buf = append(buf, '@', '0', 'x') buf = strconv.AppendUint(buf, m.InterfaceID, 16) } else { buf = append(buf, m.InterfaceName...) } buf = append(buf, '.') if m.MethodName == "" { buf = append(buf, '@') buf = strconv.AppendUint(buf, uint64(m.MethodID), 10) } else { buf = append(buf, m.MethodName...) } return string(buf) } // Transform applies a sequence of pipeline operations to a pointer // and returns the result. // // Deprecated: Use TransformPtr. func Transform(p Pointer, transform []PipelineOp) (Pointer, error) { pp, err := TransformPtr(toPtr(p), transform) return pp.toPointer(), err } // TransformPtr applies a sequence of pipeline operations to a pointer // and returns the result. func TransformPtr(p Ptr, transform []PipelineOp) (Ptr, error) { n := len(transform) if n == 0 { return p, nil } s := p.Struct() for _, op := range transform[:n-1] { field, err := s.Ptr(op.Field) if err != nil { return Ptr{}, err } s, err = field.StructDefault(op.DefaultValue) if err != nil { return Ptr{}, err } } op := transform[n-1] p, err := s.Ptr(op.Field) if err != nil { return Ptr{}, err } if op.DefaultValue != nil { p, err = p.Default(op.DefaultValue) } return p, err } type immediateAnswer struct { s Struct } // ImmediateAnswer returns an Answer that accesses s. func ImmediateAnswer(s Struct) Answer { return immediateAnswer{s} } func (ans immediateAnswer) Struct() (Struct, error) { return ans.s, nil } func (ans immediateAnswer) findClient(transform []PipelineOp) Client { p, err := TransformPtr(ans.s.ToPtr(), transform) if err != nil { return ErrorClient(err) } return p.Interface().Client() } func (ans immediateAnswer) PipelineCall(transform []PipelineOp, call *Call) Answer { c := ans.findClient(transform) if c == nil { return ErrorAnswer(ErrNullClient) } return c.Call(call) } func (ans immediateAnswer) PipelineClose(transform []PipelineOp) error { c := ans.findClient(transform) if c == nil { return ErrNullClient } return c.Close() } type errorAnswer struct { e error } // ErrorAnswer returns a Answer that always returns error e. func ErrorAnswer(e error) Answer { return errorAnswer{e} } func (ans errorAnswer) Struct() (Struct, error) { return Struct{}, ans.e } func (ans errorAnswer) PipelineCall([]PipelineOp, *Call) Answer { return ans } func (ans errorAnswer) PipelineClose([]PipelineOp) error { return ans.e } // IsFixedAnswer reports whether an answer was created by // ImmediateAnswer or ErrorAnswer. func IsFixedAnswer(ans Answer) bool { switch ans.(type) { case immediateAnswer: return true case errorAnswer: return true default: return false } } type errorClient struct { e error } // ErrorClient returns a Client that always returns error e. func ErrorClient(e error) Client { return errorClient{e} } func (ec errorClient) Call(*Call) Answer { return ErrorAnswer(ec.e) } func (ec errorClient) Close() error { return nil } // IsErrorClient reports whether c was created with ErrorClient. func IsErrorClient(c Client) bool { _, ok := c.(errorClient) return ok } // MethodError is an error on an associated method. type MethodError struct { Method *Method Err error } // Error returns the method name concatenated with the error string. func (e *MethodError) Error() string { return e.Method.String() + ": " + e.Err.Error() } // ErrUnimplemented is the error returned when a method is called on // a server that does not implement the method. var ErrUnimplemented = errors.New("capnp: method not implemented") // IsUnimplemented reports whether e indicates an unimplemented method error. func IsUnimplemented(e error) bool { if me, ok := e.(*MethodError); ok { e = me.Err } return e == ErrUnimplemented }