summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYorhel <git@yorhel.nl>2012-04-09 09:56:03 +0200
committerYorhel <git@yorhel.nl>2012-04-09 09:56:03 +0200
commit7bdb9d948bf07f56fedd6305ae146de59f51610b (patch)
tree972f9e3c4dacd6c9d8c9a68dd0afd3d3f71d178d
parent89cb48caf91a8abe6d483e85e94bc18a64926855 (diff)
rpc: Allow capturing remaining elements using variadic methods
-rw-r--r--rpc.go29
-rw-r--r--rpc_test.go38
2 files changed, 58 insertions, 9 deletions
diff --git a/rpc.go b/rpc.go
index 5501df7..049f689 100644
--- a/rpc.go
+++ b/rpc.go
@@ -13,10 +13,10 @@ import "reflect"
//
// The method type must conform to the following rules:
// - It should be exported (name must start with an uppercase letter)
-// - It should not have any out parameters (return values)
+// - It should not have any return values
// - It may have any number of arguments, as long as their types are allowed.
-// - The method may not be variadic
// - The first argument may be of type *tanja.Message
+// - If the method is variadic, its last argument must be of type ...Element
// - Any other arguments must be of a type accepted by El()
//
// Methods that do not conform to these rules can not be registered and f()
@@ -35,7 +35,13 @@ import "reflect"
// exist in the received tuple or if the element can not be converted into the
// type that the method accepts, the method will not be called.
//
+// If the method is variadic, any remaining elements in the tuple (after those
+// captured by any previous arguments) will be passed through to the last
+// argument.
+//
// Returns the number of patters registered.
+//
+// TODO: Examples are required to really understand the use of this method.
func (s *Session) RegRPC(obj interface{}, f func(string) Tuple) int {
objv := reflect.ValueOf(obj)
objt := objv.Type()
@@ -58,7 +64,11 @@ func (s *Session) RegRPC(obj interface{}, f func(string) Tuple) int {
willReply = true
j++
}
- for ; j < mt.NumIn(); j++ {
+ end := mt.NumIn()
+ if mt.IsVariadic() {
+ end--
+ }
+ for ; j < end; j++ {
pat = append(pat, El(nil))
}
// Generate callback and register
@@ -122,7 +132,11 @@ func regRPCGenCallback(mt reflect.Type, mv reflect.Value, offset int, willReply
arg++
}
off := offset
- for arg < mt.NumIn() {
+ end := mt.NumIn()
+ if mt.IsVariadic() {
+ end--
+ }
+ for arg < end {
args[arg] = regRPCValue(mt.In(arg), msg.Tup[off])
if !args[arg].IsValid() {
msg.Close()
@@ -131,6 +145,11 @@ func regRPCGenCallback(mt reflect.Type, mv reflect.Value, offset int, willReply
off++
arg++
}
- mv.Call(args)
+ if mt.IsVariadic() {
+ args[arg] = reflect.ValueOf(msg.Tup[off:])
+ mv.CallSlice(args)
+ } else {
+ mv.Call(args)
+ }
}
}
diff --git a/rpc_test.go b/rpc_test.go
index 1a06b9e..b26751b 100644
--- a/rpc_test.go
+++ b/rpc_test.go
@@ -12,6 +12,7 @@ type sT struct {
thiscalled int
replycalled int
closecalled int
+ varcalled int
}
var testmap map[string]Element = map[string]Element{
@@ -43,6 +44,22 @@ func (t *sT) ExportThis(a float32, b uint8, c string, d bool, e map[string]Eleme
t.thiscalled++
}
+func (t *sT) ExportVariadic(a int, e ...Element) {
+ t.varcalled++
+ err := true
+ switch t.varcalled {
+ case 1:
+ err = a != 10 || len(e) != 1 || e[0].String() != "extra"
+ case 2:
+ err = a != 2 || len(e) != 0
+ case 3:
+ err = a != 3 || !reflect.DeepEqual(e, []Element{El(testmap)})
+ }
+ if err {
+ t.tst.Errorf("Unexpected variadic argument(%d): %d, %#v", t.varcalled, a, e)
+ }
+}
+
func (t *sT) ExportReply(m *Message, s string) {
if s != "10" {
t.tst.Errorf("ExportReply string argument is '%s', expected '%s'", s, "argument")
@@ -77,8 +94,8 @@ func TestRPC(tst *testing.T) {
}
return nil
})
- if n != 3 {
- tst.Fatalf("Number of exported methods is %d, expected %d", n, 3)
+ if n != 4 {
+ tst.Fatalf("Number of exported methods is %d, expected %d", n, 4)
}
go func() {
@@ -89,7 +106,7 @@ func TestRPC(tst *testing.T) {
ses.Send(false, "prefix", 2, "close", 1)
ses.Send(false, "prefix", 1, "Close", 1)
- // Try replying
+ // Try replying (tuple should be received by This, Close, Reply and Variadic)
r := ses.Send(true, "prefix", 1, nil, 10, "extra")
if t := <-r.Chan(); !reflect.DeepEqual(t, Tup(2)) || <-r.Chan() != nil {
tst.Error("Received invalid reply")
@@ -103,9 +120,19 @@ func TestRPC(tst *testing.T) {
}
r.Close()
+ // Variadic test
+ ses.Send(false, "prefix", 1, "variadic", 2)
+ ses.Send(false, "prefix", 1, "variadic", 3, testmap)
+
// Testing various types
ses.Send(false, "prefix", 1, "this", 10.5, "42", "str", "some_true_value", testmap, testslice)
+ // Shouldn't match
+ ses.Send(false, "prefix", 1, "this", []Element{}, "42", "str", "some_true_value", testmap, testslice)
+ ses.Send(false, "prefix", 1, "this", 10.5, nil, "str", "some_true_value", testmap, testslice)
+ ses.Send(false, "prefix", 1, "this", 10.5, "42", "str", "some_true_value", testmap, 0)
+ ses.Send(false, "prefix", 1, "this", 10.5, "42", "str", "some_true_value", 1, testslice)
+
// Actual close
ses.Send(false, "prefix", 1, "close", "really!")
// Shouldn't be received
@@ -120,6 +147,9 @@ func TestRPC(tst *testing.T) {
tst.Errorf("Close is called %d times, expected %d", obj.closecalled, 3)
}
if obj.replycalled != 1 {
- tst.Errorf("Close is called %d times, expected %d", obj.replycalled, 1)
+ tst.Errorf("Reply is called %d times, expected %d", obj.replycalled, 1)
+ }
+ if obj.varcalled != 3 {
+ tst.Errorf("Variadic is called %d times, expected %d", obj.varcalled, 3)
}
}