github.com/mattyw/juju@v0.0.0-20140610034352-732aecd63861/rpc/rpcreflect/value.go (about)

     1  // Copyright 2012, 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package rpcreflect
     5  
     6  import (
     7  	"fmt"
     8  	"reflect"
     9  )
    10  
    11  // CallNotImplementedError is an error, returned an attempt to call to
    12  // an unknown API method is made.
    13  type CallNotImplementedError struct {
    14  	Method     string
    15  	RootMethod string
    16  }
    17  
    18  func (e *CallNotImplementedError) Error() string {
    19  	if e.Method == "" {
    20  		return fmt.Sprintf("unknown object type %q", e.RootMethod)
    21  	}
    22  	return fmt.Sprintf("no such request - method %s.%s is not implemented", e.RootMethod, e.Method)
    23  }
    24  
    25  // MethodCaller knows how to call a particular RPC method.
    26  type MethodCaller struct {
    27  	// ParamsType holds the required type of the parameter to the object method.
    28  	ParamsType reflect.Type
    29  	// ResultType holds the result type of the result of caling the object method.
    30  	ResultType reflect.Type
    31  
    32  	rootValue  reflect.Value
    33  	rootMethod RootMethod
    34  	objMethod  ObjMethod
    35  }
    36  
    37  // MethodCaller holds the value of the root of an RPC server that
    38  // can call methods directly on a Go value.
    39  type Value struct {
    40  	rootValue reflect.Value
    41  	rootType  *Type
    42  }
    43  
    44  // ValueOf returns a value that can be used to call RPC-style
    45  // methods on the given root value. It returns the zero
    46  // Value if rootValue.IsValid is false.
    47  func ValueOf(rootValue reflect.Value) Value {
    48  	if !rootValue.IsValid() {
    49  		return Value{}
    50  	}
    51  	return Value{
    52  		rootValue: rootValue,
    53  		rootType:  TypeOf(rootValue.Type()),
    54  	}
    55  }
    56  
    57  // IsValid returns whether the Value has been initialized with ValueOf.
    58  func (v Value) IsValid() bool {
    59  	return v.rootType != nil
    60  }
    61  
    62  // GoValue returns the value that was passed to ValueOf to create v.
    63  func (v Value) GoValue() reflect.Value {
    64  	return v.rootValue
    65  }
    66  
    67  // MethodCaller returns an object that can be used to make calls on
    68  // the given root value to the given root method and object method.
    69  // It returns an error if either the root method or the object
    70  // method were not found.
    71  // It panics if called on the zero Value.
    72  func (v Value) MethodCaller(rootMethodName, objMethodName string) (MethodCaller, error) {
    73  	if !v.IsValid() {
    74  		panic("MethodCaller called on invalid Value")
    75  	}
    76  	caller := MethodCaller{
    77  		rootValue: v.rootValue,
    78  	}
    79  	var err error
    80  	caller.rootMethod, err = v.rootType.Method(rootMethodName)
    81  	if err != nil {
    82  		return MethodCaller{}, &CallNotImplementedError{
    83  			RootMethod: rootMethodName,
    84  		}
    85  	}
    86  	caller.objMethod, err = caller.rootMethod.ObjType.Method(objMethodName)
    87  	if err != nil {
    88  		return MethodCaller{}, &CallNotImplementedError{
    89  			RootMethod: rootMethodName,
    90  			Method:     objMethodName,
    91  		}
    92  	}
    93  	caller.ParamsType = caller.objMethod.Params
    94  	caller.ResultType = caller.objMethod.Result
    95  	return caller, nil
    96  }
    97  
    98  func (caller MethodCaller) Call(objId string, arg reflect.Value) (reflect.Value, error) {
    99  	obj, err := caller.rootMethod.Call(caller.rootValue, objId)
   100  	if err != nil {
   101  		return reflect.Value{}, err
   102  	}
   103  	return caller.objMethod.Call(obj, arg)
   104  }