You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@qpid.apache.org by ac...@apache.org on 2017/05/31 21:22:14 UTC

[1/2] qpid-proton git commit: PROTON-1450: go binding amqp.Key and message map types.

Repository: qpid-proton
Updated Branches:
  refs/heads/master 5f8738f57 -> 4f724ace0


PROTON-1450: go binding amqp.Key and message map types.

Add amqp.Key type for message maps with keys that can be ulong or symbol.

Add type-safe methods to deal with amqp.Message maps, old methods remain for
compatibility but are deprecated.


Project: http://git-wip-us.apache.org/repos/asf/qpid-proton/repo
Commit: http://git-wip-us.apache.org/repos/asf/qpid-proton/commit/110f851a
Tree: http://git-wip-us.apache.org/repos/asf/qpid-proton/tree/110f851a
Diff: http://git-wip-us.apache.org/repos/asf/qpid-proton/diff/110f851a

Branch: refs/heads/master
Commit: 110f851add21f4f79de08a0af55d93226593c26c
Parents: 5f8738f
Author: Alan Conway <ac...@redhat.com>
Authored: Thu Apr 27 17:16:19 2017 -0400
Committer: Alan Conway <ac...@redhat.com>
Committed: Wed May 31 17:17:50 2017 -0400

----------------------------------------------------------------------
 .../go/src/qpid.apache.org/amqp/marshal.go      |   2 +
 .../go/src/qpid.apache.org/amqp/marshal_test.go |  90 ++++++++++++++++
 .../go/src/qpid.apache.org/amqp/message.go      | 106 ++++++++++++++-----
 .../go/src/qpid.apache.org/amqp/message_test.go |  57 ++++++++--
 .../go/src/qpid.apache.org/amqp/types.go        |  18 ++++
 .../go/src/qpid.apache.org/amqp/types_test.go   |  34 ++++++
 .../go/src/qpid.apache.org/amqp/unmarshal.go    |   9 ++
 7 files changed, 285 insertions(+), 31 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/110f851a/proton-c/bindings/go/src/qpid.apache.org/amqp/marshal.go
----------------------------------------------------------------------
diff --git a/proton-c/bindings/go/src/qpid.apache.org/amqp/marshal.go b/proton-c/bindings/go/src/qpid.apache.org/amqp/marshal.go
index b6adf90..a0a732e 100644
--- a/proton-c/bindings/go/src/qpid.apache.org/amqp/marshal.go
+++ b/proton-c/bindings/go/src/qpid.apache.org/amqp/marshal.go
@@ -204,6 +204,8 @@ func marshal(v interface{}, data *C.pn_data_t) {
 			marshal(val, data)
 		}
 		C.pn_data_exit(data)
+	case Key:
+		marshal(v.Get(), data)
 	default:
 		switch reflect.TypeOf(v).Kind() {
 		case reflect.Map:

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/110f851a/proton-c/bindings/go/src/qpid.apache.org/amqp/marshal_test.go
----------------------------------------------------------------------
diff --git a/proton-c/bindings/go/src/qpid.apache.org/amqp/marshal_test.go b/proton-c/bindings/go/src/qpid.apache.org/amqp/marshal_test.go
new file mode 100644
index 0000000..2eda33c
--- /dev/null
+++ b/proton-c/bindings/go/src/qpid.apache.org/amqp/marshal_test.go
@@ -0,0 +1,90 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+package amqp
+
+import (
+	"testing"
+)
+
+func TestSymbolKey(t *testing.T) {
+	bytes, err := Marshal(SymbolKey("foo"), nil)
+	if err != nil {
+		t.Fatal(err)
+	}
+	var k Key
+	if _, err := Unmarshal(bytes, &k); err != nil {
+		t.Error(err)
+	}
+	if err := checkEqual("foo", string(k.Get().(Symbol))); err != nil {
+		t.Error(err)
+	}
+	var sym Symbol
+	if _, err := Unmarshal(bytes, &sym); err != nil {
+		t.Error(err)
+	}
+	if err := checkEqual("foo", sym.String()); err != nil {
+		t.Error(err)
+	}
+
+}
+
+func TestStringKey(t *testing.T) {
+	bytes, err := Marshal(StringKey("foo"), nil)
+	if err != nil {
+		t.Fatal(err)
+	}
+	var k Key
+	if _, err := Unmarshal(bytes, &k); err != nil {
+		t.Error(err)
+	}
+	if err := checkEqual("foo", string(k.Get().(Symbol))); err != nil {
+		t.Error(err)
+	}
+	var s string
+	if _, err := Unmarshal(bytes, &s); err != nil {
+		t.Error(err)
+	}
+	if err := checkEqual("foo", s); err != nil {
+		t.Error(err)
+	}
+
+}
+
+func TestIntKey(t *testing.T) {
+	bytes, err := Marshal(IntKey(12345), nil)
+	if err != nil {
+		t.Fatal(err)
+	}
+	var k Key
+	if _, err := Unmarshal(bytes, &k); err != nil {
+		t.Error(err)
+	}
+	if 12345 != k.Get().(uint64) {
+		t.Errorf("(%T)%v != (%T)%v", 12345, k.Get().(uint64))
+	}
+	var n uint64
+	if _, err := Unmarshal(bytes, &n); err != nil {
+		t.Error(err)
+	}
+	if 12345 != n {
+		t.Errorf("%v != %v", 12345, k.Get().(uint64))
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/110f851a/proton-c/bindings/go/src/qpid.apache.org/amqp/message.go
----------------------------------------------------------------------
diff --git a/proton-c/bindings/go/src/qpid.apache.org/amqp/message.go b/proton-c/bindings/go/src/qpid.apache.org/amqp/message.go
index 753682e..9f4d7d1 100644
--- a/proton-c/bindings/go/src/qpid.apache.org/amqp/message.go
+++ b/proton-c/bindings/go/src/qpid.apache.org/amqp/message.go
@@ -117,17 +117,20 @@ type Message interface {
 	ReplyToGroupId() string
 	SetReplyToGroupId(string)
 
-	// Instructions - AMQP delivery instructions.
-	Instructions() map[string]interface{}
-	SetInstructions(v map[string]interface{})
+	// Property map set by the application to be carried with the message.
+	// Values must be simple types (not maps, lists or sequences)
+	ApplicationProperties() map[Key]interface{}
+	SetApplicationProperties(map[Key]interface{})
 
-	// Annotations - AMQP annotations.
-	Annotations() map[string]interface{}
-	SetAnnotations(v map[string]interface{})
+	// Per-delivery annotations to provide delivery instructions.
+	// May be added or removed by intermediaries during delivery.
+	DeliveryAnnotations() map[Key]interface{}
+	SetDeliveryAnnotations(map[Key]interface{})
 
-	// Properties - Application properties.
-	Properties() map[string]interface{}
-	SetProperties(v map[string]interface{})
+	// Message annotations added as part of the bare message at creation, usually
+	// by an AMQP library. See ApplicationProperties() for adding application data.
+	MessageAnnotations() map[Key]interface{}
+	SetMessageAnnotations(map[Key]interface{})
 
 	// Inferred indicates how the message content
 	// is encoded into AMQP sections. If inferred is true then binary and
@@ -160,6 +163,18 @@ type Message interface {
 
 	// Copy the contents of another message to this one.
 	Copy(m Message) error
+
+	// Deprecated: use DeliveryAnnotations() for a more type-safe interface
+	Instructions() map[string]interface{}
+	SetInstructions(v map[string]interface{})
+
+	// Deprecated: use MessageAnnotations() for a more type-safe interface
+	Annotations() map[string]interface{}
+	SetAnnotations(v map[string]interface{})
+
+	// Deprecated: use ApplicationProperties() for a more type-safe interface
+	Properties() map[string]interface{}
+	SetProperties(v map[string]interface{})
 }
 
 type message struct{ pn *C.pn_message_t }
@@ -203,13 +218,6 @@ func rewindGet(data *C.pn_data_t) (v interface{}) {
 	return v
 }
 
-func rewindMap(data *C.pn_data_t) (v map[string]interface{}) {
-	C.pn_data_rewind(data)
-	C.pn_data_next(data)
-	unmarshal(&v, data)
-	return v
-}
-
 func (m *message) Inferred() bool  { return bool(C.pn_message_is_inferred(m.pn)) }
 func (m *message) Durable() bool   { return bool(C.pn_message_is_durable(m.pn)) }
 func (m *message) Priority() uint8 { return uint8(C.pn_message_get_priority(m.pn)) }
@@ -237,14 +245,21 @@ func (m *message) GroupId() string        { return C.GoString(C.pn_message_get_g
 func (m *message) GroupSequence() int32   { return int32(C.pn_message_get_group_sequence(m.pn)) }
 func (m *message) ReplyToGroupId() string { return C.GoString(C.pn_message_get_reply_to_group_id(m.pn)) }
 
-func (m *message) Instructions() map[string]interface{} {
-	return rewindMap(C.pn_message_instructions(m.pn))
+func getAnnotations(data *C.pn_data_t) (v map[Key]interface{}) {
+	C.pn_data_rewind(data)
+	C.pn_data_next(data)
+	unmarshal(&v, data)
+	return v
 }
-func (m *message) Annotations() map[string]interface{} {
-	return rewindMap(C.pn_message_annotations(m.pn))
+
+func (m *message) DeliveryAnnotations() map[Key]interface{} {
+	return getAnnotations(C.pn_message_instructions(m.pn))
 }
-func (m *message) Properties() map[string]interface{} {
-	return rewindMap(C.pn_message_properties(m.pn))
+func (m *message) MessageAnnotations() map[Key]interface{} {
+	return getAnnotations(C.pn_message_annotations(m.pn))
+}
+func (m *message) ApplicationProperties() map[Key]interface{} {
+	return getAnnotations(C.pn_message_properties(m.pn))
 }
 
 // ==== message set methods
@@ -299,11 +314,15 @@ func (m *message) SetReplyToGroupId(s string) {
 	C.msg_set_str(m.pn, C.CString(s), C.set_fn(C.pn_message_set_reply_to_group_id))
 }
 
-func (m *message) SetInstructions(v map[string]interface{}) {
+func (m *message) SetDeliveryAnnotations(v map[Key]interface{}) {
 	setData(v, C.pn_message_instructions(m.pn))
 }
-func (m *message) SetAnnotations(v map[string]interface{}) { setData(v, C.pn_message_annotations(m.pn)) }
-func (m *message) SetProperties(v map[string]interface{})  { setData(v, C.pn_message_properties(m.pn)) }
+func (m *message) SetMessageAnnotations(v map[Key]interface{}) {
+	setData(v, C.pn_message_annotations(m.pn))
+}
+func (m *message) SetApplicationProperties(v map[Key]interface{}) {
+	setData(v, C.pn_message_properties(m.pn))
+}
 
 // Marshal/Unmarshal body
 func (m *message) Marshal(v interface{})   { clearMarshal(v, C.pn_message_body(m.pn)) }
@@ -346,3 +365,40 @@ func (m *message) Encode(buffer []byte) ([]byte, error) {
 // TODO aconway 2015-09-14: Multi-section messages.
 
 // TODO aconway 2016-09-09: Message.String() use inspect.
+
+// ==== Deprecated functions
+func oldGetAnnotations(data *C.pn_data_t) (v map[string]interface{}) {
+	C.pn_data_rewind(data)
+	C.pn_data_next(data)
+	unmarshal(&v, data)
+	return v
+}
+
+func (m *message) Instructions() map[string]interface{} {
+	return oldGetAnnotations(C.pn_message_instructions(m.pn))
+}
+func (m *message) Annotations() map[string]interface{} {
+	return oldGetAnnotations(C.pn_message_annotations(m.pn))
+}
+func (m *message) Properties() map[string]interface{} {
+	return oldGetAnnotations(C.pn_message_properties(m.pn))
+}
+
+// Convert old string-keyed annotations to a Key map
+func fixAnnotations(old map[string]interface{}) (annotations map[Key]interface{}) {
+	annotations = make(map[Key]interface{})
+	for k, v := range old {
+		annotations[StringKey(k)] = v
+	}
+	return
+}
+
+func (m *message) SetInstructions(v map[string]interface{}) {
+	setData(fixAnnotations(v), C.pn_message_instructions(m.pn))
+}
+func (m *message) SetAnnotations(v map[string]interface{}) {
+	setData(fixAnnotations(v), C.pn_message_annotations(m.pn))
+}
+func (m *message) SetProperties(v map[string]interface{}) {
+	setData(fixAnnotations(v), C.pn_message_properties(m.pn))
+}

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/110f851a/proton-c/bindings/go/src/qpid.apache.org/amqp/message_test.go
----------------------------------------------------------------------
diff --git a/proton-c/bindings/go/src/qpid.apache.org/amqp/message_test.go b/proton-c/bindings/go/src/qpid.apache.org/amqp/message_test.go
index 7a6e5a8..3081cc5 100644
--- a/proton-c/bindings/go/src/qpid.apache.org/amqp/message_test.go
+++ b/proton-c/bindings/go/src/qpid.apache.org/amqp/message_test.go
@@ -55,6 +55,11 @@ func TestDefaultMessage(t *testing.T) {
 		{m.ReplyToGroupId(), ""},
 		{m.MessageId(), nil},
 		{m.CorrelationId(), nil},
+		{m.DeliveryAnnotations(), map[Key]interface{}{}},
+		{m.MessageAnnotations(), map[Key]interface{}{}},
+		{m.ApplicationProperties(), map[Key]interface{}{}},
+
+		// Deprecated
 		{m.Instructions(), map[string]interface{}{}},
 		{m.Annotations(), map[string]interface{}{}},
 		{m.Properties(), map[string]interface{}{}},
@@ -86,9 +91,9 @@ func TestMessageRoundTrip(t *testing.T) {
 	m.SetReplyToGroupId("replytogroup")
 	m.SetMessageId("id")
 	m.SetCorrelationId("correlation")
-	m.SetInstructions(map[string]interface{}{"instructions": "foo"})
-	m.SetAnnotations(map[string]interface{}{"annotations": "foo"})
-	m.SetProperties(map[string]interface{}{"int": int32(32), "bool": true, "string": "foo"})
+	m.SetDeliveryAnnotations(map[Key]interface{}{SymbolKey("instructions"): "foo"})
+	m.SetMessageAnnotations(map[Key]interface{}{SymbolKey("annotations"): "bar"})
+	m.SetApplicationProperties(map[Key]interface{}{SymbolKey("int"): int32(32), SymbolKey("bool"): true, IntKey(42): "42"})
 	m.Marshal("hello")
 
 	for _, data := range [][]interface{}{
@@ -107,10 +112,50 @@ func TestMessageRoundTrip(t *testing.T) {
 		{m.ReplyToGroupId(), "replytogroup"},
 		{m.MessageId(), "id"},
 		{m.CorrelationId(), "correlation"},
-		{m.Instructions(), map[string]interface{}{"instructions": "foo"}},
-		{m.Annotations(), map[string]interface{}{"annotations": "foo"}},
-		{m.Properties(), map[string]interface{}{"int": int32(32), "bool": true, "string": "foo"}},
+
+		{m.DeliveryAnnotations(), map[Key]interface{}{SymbolKey("instructions"): "foo"}},
+		{m.MessageAnnotations(), map[Key]interface{}{SymbolKey("annotations"): "bar"}},
+		{m.ApplicationProperties(), map[Key]interface{}{SymbolKey("int"): int32(32), SymbolKey("bool"): true, IntKey(42): "42"}},
 		{m.Body(), "hello"},
+
+		// Deprecated
+		{m.Instructions(), map[string]interface{}{"instructions": "foo"}},
+		{m.Annotations(), map[string]interface{}{"annotations": "bar"}},
+	} {
+		if err := checkEqual(data[0], data[1]); err != nil {
+			t.Error(err)
+		}
+	}
+	if err := roundTrip(m); err != nil {
+		t.Error(err)
+	}
+
+	func() { // Expect a panic
+		defer func() {
+			if x, ok := recover().(*UnmarshalError); !ok {
+				t.Errorf("Expected UnmarshalError, got %#v", x)
+			}
+		}()
+		m.Properties()
+		t.Error("Expected panic")
+	}()
+}
+
+func TestDeprecated(t *testing.T) {
+	m := NewMessage()
+
+	m.SetInstructions(map[string]interface{}{"instructions": "foo"})
+	m.SetAnnotations(map[string]interface{}{"annotations": "bar"})
+	m.SetProperties(map[string]interface{}{"int": int32(32), "bool": true})
+
+	for _, data := range [][]interface{}{
+		{m.DeliveryAnnotations(), map[Key]interface{}{SymbolKey("instructions"): "foo"}},
+		{m.MessageAnnotations(), map[Key]interface{}{SymbolKey("annotations"): "bar"}},
+		{m.ApplicationProperties(), map[Key]interface{}{SymbolKey("int"): int32(32), SymbolKey("bool"): true}},
+
+		{m.Instructions(), map[string]interface{}{"instructions": "foo"}},
+		{m.Annotations(), map[string]interface{}{"annotations": "bar"}},
+		{m.Properties(), map[string]interface{}{"int": int32(32), "bool": true}},
 	} {
 		if err := checkEqual(data[0], data[1]); err != nil {
 			t.Error(err)

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/110f851a/proton-c/bindings/go/src/qpid.apache.org/amqp/types.go
----------------------------------------------------------------------
diff --git a/proton-c/bindings/go/src/qpid.apache.org/amqp/types.go b/proton-c/bindings/go/src/qpid.apache.org/amqp/types.go
index 2852c23..76f223f 100644
--- a/proton-c/bindings/go/src/qpid.apache.org/amqp/types.go
+++ b/proton-c/bindings/go/src/qpid.apache.org/amqp/types.go
@@ -105,6 +105,7 @@ type List []interface{}
 // Symbol is a string that is encoded as an AMQP symbol
 type Symbol string
 
+func (s Symbol) String() string   { return string(s) }
 func (s Symbol) GoString() string { return fmt.Sprintf("s\"%s\"", s) }
 
 // Binary is a string that is encoded as an AMQP binary.
@@ -112,6 +113,7 @@ func (s Symbol) GoString() string { return fmt.Sprintf("s\"%s\"", s) }
 // a map key, AMQP frequently uses binary types as map keys. It can convert to and from []byte
 type Binary string
 
+func (b Binary) String() string   { return string(b) }
 func (b Binary) GoString() string { return fmt.Sprintf("b\"%s\"", b) }
 
 // GoString for Map prints values with their types, useful for debugging.
@@ -192,3 +194,19 @@ func cPtr(b []byte) *C.char {
 func cLen(b []byte) C.size_t {
 	return C.size_t(len(b))
 }
+
+// Key is used as a map key for some AMQP "restricted" maps which are
+// allowed to have keys that are either symbol or ulong but no other type.
+//
+type Key struct {
+	value interface{}
+}
+
+func SymbolKey(v Symbol) Key { return Key{v} }
+func IntKey(v uint64) Key    { return Key{v} }
+func StringKey(v string) Key { return Key{Symbol(v)} }
+
+// Returns the value which must be Symbol, uint64 or nil
+func (k Key) Get() interface{} { return k.value }
+
+func (k Key) String() string { return fmt.Sprintf("%v", k.Get()) }

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/110f851a/proton-c/bindings/go/src/qpid.apache.org/amqp/types_test.go
----------------------------------------------------------------------
diff --git a/proton-c/bindings/go/src/qpid.apache.org/amqp/types_test.go b/proton-c/bindings/go/src/qpid.apache.org/amqp/types_test.go
new file mode 100644
index 0000000..b9c0596
--- /dev/null
+++ b/proton-c/bindings/go/src/qpid.apache.org/amqp/types_test.go
@@ -0,0 +1,34 @@
+/*
+Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements.  See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership.  The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License.  You may obtain a copy of the License at
+
+  http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied.  See the License for the
+specific language governing permissions and limitations
+under the License.
+*/
+
+package amqp
+
+import (
+	"fmt"
+)
+
+func ExampleKey() {
+	var k Key = SymbolKey(Symbol("foo"))
+	fmt.Println(k.Get().(Symbol))
+	k = IntKey(42)
+	fmt.Println(k.Get().(uint64))
+	// Output:
+	// foo
+	// 42
+}

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/110f851a/proton-c/bindings/go/src/qpid.apache.org/amqp/unmarshal.go
----------------------------------------------------------------------
diff --git a/proton-c/bindings/go/src/qpid.apache.org/amqp/unmarshal.go b/proton-c/bindings/go/src/qpid.apache.org/amqp/unmarshal.go
index d56cbd2..96b8e05 100644
--- a/proton-c/bindings/go/src/qpid.apache.org/amqp/unmarshal.go
+++ b/proton-c/bindings/go/src/qpid.apache.org/amqp/unmarshal.go
@@ -178,6 +178,8 @@ Types are converted as follows:
  |                           +------------------------+---------------------------------------------+
  |                           |list                    |List                                         |
  +---------------------------+------------------------+---------------------------------------------+
+ |Key                        |symbol, ulong                                                         |
+ +---------------------------+----------------------------------------------------------------------+
 
 The following Go types cannot be unmarshaled: uintptr, function, interface, channel.
 
@@ -431,6 +433,13 @@ func unmarshal(v interface{}, data *C.pn_data_t) {
 	case *interface{}:
 		getInterface(data, v)
 
+	case *Key:
+		if pnType == C.PN_ULONG || pnType == C.PN_SYMBOL || pnType == C.PN_STRING {
+			unmarshal(&v.value, data)
+		} else {
+			panic(newUnmarshalError(pnType, v))
+		}
+
 	default:
 		if reflect.TypeOf(v).Kind() != reflect.Ptr {
 			panic(newUnmarshalError(pnType, v))


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@qpid.apache.org
For additional commands, e-mail: commits-help@qpid.apache.org


[2/2] qpid-proton git commit: PROTON-1456: Support for AMQP described types

Posted by ac...@apache.org.
PROTON-1456: Support for AMQP described types

Support AMQP described types, required for compliant AMQP 1.0 filters.

Added some extra encoding/decoding tests in types_test.go


Project: http://git-wip-us.apache.org/repos/asf/qpid-proton/repo
Commit: http://git-wip-us.apache.org/repos/asf/qpid-proton/commit/4f724ace
Tree: http://git-wip-us.apache.org/repos/asf/qpid-proton/tree/4f724ace
Diff: http://git-wip-us.apache.org/repos/asf/qpid-proton/diff/4f724ace

Branch: refs/heads/master
Commit: 4f724ace01f942c69e650c4e7c513c327d76cfbc
Parents: 110f851
Author: Alan Conway <ac...@redhat.com>
Authored: Tue May 30 16:02:51 2017 -0400
Committer: Alan Conway <ac...@redhat.com>
Committed: Wed May 31 17:18:29 2017 -0400

----------------------------------------------------------------------
 proton-c/bindings/go/CMakeLists.txt             |   1 +
 .../go/src/qpid.apache.org/amqp/interop_test.go |   8 -
 .../go/src/qpid.apache.org/amqp/marshal.go      |  19 +-
 .../go/src/qpid.apache.org/amqp/types.go        |  11 +-
 .../go/src/qpid.apache.org/amqp/types_test.go   | 163 ++++++++++++++++
 .../go/src/qpid.apache.org/amqp/unmarshal.go    | 185 ++++++++++++-------
 6 files changed, 306 insertions(+), 81 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/4f724ace/proton-c/bindings/go/CMakeLists.txt
----------------------------------------------------------------------
diff --git a/proton-c/bindings/go/CMakeLists.txt b/proton-c/bindings/go/CMakeLists.txt
index d0ffe9b..7f814b9 100644
--- a/proton-c/bindings/go/CMakeLists.txt
+++ b/proton-c/bindings/go/CMakeLists.txt
@@ -23,6 +23,7 @@ message(STATUS "Found Go: ${GO_EXE} (${go_ver})")
 
 set(GO_BUILD_FLAGS "" CACHE STRING "Flags for 'go build'")
 set(GO_TEST_FLAGS "-v" CACHE STRING "Flags for 'go test'")
+set(GO_VET_FLAGS "-v" CACHE STRING "Flags for 'go test'")
 
 # Flags that differ for golang go and gcc go.
 if (go_ver MATCHES "gccgo")

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/4f724ace/proton-c/bindings/go/src/qpid.apache.org/amqp/interop_test.go
----------------------------------------------------------------------
diff --git a/proton-c/bindings/go/src/qpid.apache.org/amqp/interop_test.go b/proton-c/bindings/go/src/qpid.apache.org/amqp/interop_test.go
index b3e27bc..a5fb92e 100644
--- a/proton-c/bindings/go/src/qpid.apache.org/amqp/interop_test.go
+++ b/proton-c/bindings/go/src/qpid.apache.org/amqp/interop_test.go
@@ -24,7 +24,6 @@ package amqp
 
 import (
 	"bytes"
-	"fmt"
 	"io"
 	"io/ioutil"
 	"os"
@@ -33,13 +32,6 @@ import (
 	"testing"
 )
 
-func checkEqual(want interface{}, got interface{}) error {
-	if !reflect.DeepEqual(want, got) {
-		return fmt.Errorf("%#v != %#v", want, got)
-	}
-	return nil
-}
-
 func getReader(t *testing.T, name string) (r io.Reader) {
 	dir := os.Getenv("PN_INTEROP_DIR")
 	if dir == "" {

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/4f724ace/proton-c/bindings/go/src/qpid.apache.org/amqp/marshal.go
----------------------------------------------------------------------
diff --git a/proton-c/bindings/go/src/qpid.apache.org/amqp/marshal.go b/proton-c/bindings/go/src/qpid.apache.org/amqp/marshal.go
index a0a732e..04ecf04 100644
--- a/proton-c/bindings/go/src/qpid.apache.org/amqp/marshal.go
+++ b/proton-c/bindings/go/src/qpid.apache.org/amqp/marshal.go
@@ -87,17 +87,16 @@ Go types are encoded as follows
  +-------------------------------------+--------------------------------------------+
  |List                                 |list, may have mixed types  values          |
  +-------------------------------------+--------------------------------------------+
+ |Described                            |described type                              |
+ +-------------------------------------+--------------------------------------------+
 
-The following Go types cannot be marshaled: uintptr, function, interface, channel
-
-TODO
-
-Go types: array, slice, struct, complex64/128.
+The following Go types cannot be marshaled: uintptr, function, channel, array (use slice), struct
 
-AMQP types: decimal32/64/128, char, timestamp, uuid, array, multi-section message bodies.
+TODO: Not yet implemented:
 
-Described types.
+Go types: struct, complex64/128.
 
+AMQP types: decimal32/64/128, char, timestamp, uuid, array.
 */
 func Marshal(v interface{}, buffer []byte) (outbuf []byte, err error) {
 	defer func() {
@@ -204,6 +203,12 @@ func marshal(v interface{}, data *C.pn_data_t) {
 			marshal(val, data)
 		}
 		C.pn_data_exit(data)
+	case Described:
+		C.pn_data_put_described(data)
+		C.pn_data_enter(data)
+		marshal(v.Descriptor, data)
+		marshal(v.Value, data)
+		C.pn_data_exit(data)
 	case Key:
 		marshal(v.Get(), data)
 	default:

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/4f724ace/proton-c/bindings/go/src/qpid.apache.org/amqp/types.go
----------------------------------------------------------------------
diff --git a/proton-c/bindings/go/src/qpid.apache.org/amqp/types.go b/proton-c/bindings/go/src/qpid.apache.org/amqp/types.go
index 76f223f..6e2ebc8 100644
--- a/proton-c/bindings/go/src/qpid.apache.org/amqp/types.go
+++ b/proton-c/bindings/go/src/qpid.apache.org/amqp/types.go
@@ -83,7 +83,7 @@ func (t C.pn_type_t) String() string {
 	case C.PN_MAP:
 		return "map"
 	default:
-		return "no-data"
+		return fmt.Sprintf("<bad-type %v>", int(t))
 	}
 }
 
@@ -210,3 +210,12 @@ func StringKey(v string) Key { return Key{Symbol(v)} }
 func (k Key) Get() interface{} { return k.value }
 
 func (k Key) String() string { return fmt.Sprintf("%v", k.Get()) }
+
+// Described represents an AMQP described type, which is really
+// just a pair of AMQP values - the first is treated as a "descriptor",
+// and is normally a string or ulong providing information about the type.
+// The second is the "value" and can be any AMQP value.
+type Described struct {
+	Descriptor interface{}
+	Value      interface{}
+}

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/4f724ace/proton-c/bindings/go/src/qpid.apache.org/amqp/types_test.go
----------------------------------------------------------------------
diff --git a/proton-c/bindings/go/src/qpid.apache.org/amqp/types_test.go b/proton-c/bindings/go/src/qpid.apache.org/amqp/types_test.go
index b9c0596..096d27f 100644
--- a/proton-c/bindings/go/src/qpid.apache.org/amqp/types_test.go
+++ b/proton-c/bindings/go/src/qpid.apache.org/amqp/types_test.go
@@ -21,8 +21,28 @@ package amqp
 
 import (
 	"fmt"
+	"reflect"
+	"testing"
 )
 
+func checkEqual(want interface{}, got interface{}) error {
+	if !reflect.DeepEqual(want, got) {
+		return fmt.Errorf("%#v != %#v", want, got)
+	}
+	return nil
+}
+
+func checkUnmarshal(marshalled []byte, v interface{}) error {
+	got, err := Unmarshal(marshalled, v)
+	if err != nil {
+		return err
+	}
+	if got != len(marshalled) {
+		return fmt.Errorf("Wanted to Unmarshal %v bytes, got %v", len(marshalled), got)
+	}
+	return nil
+}
+
 func ExampleKey() {
 	var k Key = SymbolKey(Symbol("foo"))
 	fmt.Println(k.Get().(Symbol))
@@ -32,3 +52,146 @@ func ExampleKey() {
 	// foo
 	// 42
 }
+
+// Values that are unchanged by a marshal/unmarshal round-trip from interface{}
+// to interface{}
+var rtValues = []interface{}{
+	true,
+	int8(-8), int16(-16), int32(-32), int64(-64),
+	uint8(8), uint16(16), uint32(32), uint64(64),
+	float32(0.32), float64(0.64),
+	"string", Binary("Binary"), Symbol("symbol"),
+	nil,
+	Map{"V": "X"},
+	List{"V", int32(1)},
+	Described{"D", "V"},
+}
+
+// Go values that unmarshal as an equivalent value but a different type
+// if unmarshalled to interface{}.
+var oddValues = []interface{}{
+	int(-99), uint(99), // [u]int32|64
+	[]byte("byte"),            // amqp.Binary
+	map[string]int{"str": 99}, // amqp.Map
+	[]string{"a", "b"},        // amqp.List
+}
+
+var allValues = append(rtValues, oddValues...)
+
+// %v formatted representation of allValues
+var vstrings = []string{
+	// for rtValues
+	"true",
+	"-8", "-16", "-32", "-64",
+	"8", "16", "32", "64",
+	"0.32", "0.64",
+	"string", "Binary", "symbol",
+	"<nil>",
+	"map[V:X]",
+	"[V 1]",
+	"{D V}",
+	// for oddValues
+	"-99", "99",
+	"[98 121 116 101]", /*"byte"*/
+	"map[str:99]",
+	"[a b]",
+}
+
+// Round-trip encoding test
+func TestTypesRoundTrip(t *testing.T) {
+	for _, x := range rtValues {
+		marshalled, err := Marshal(x, nil)
+		if err != nil {
+			t.Error(err)
+		}
+		var v interface{}
+		if err := checkUnmarshal(marshalled, &v); err != nil {
+			t.Error(err)
+		}
+		if err := checkEqual(v, x); err != nil {
+			t.Error(t, err)
+		}
+	}
+}
+
+// Round trip from T to T where T is the type of the value.
+func TestTypesRoundTripAll(t *testing.T) {
+	for _, x := range allValues {
+		marshalled, err := Marshal(x, nil)
+		if err != nil {
+			t.Error(err)
+		}
+		if x == nil { // We can't create an instance of nil to unmarshal to.
+			continue
+		}
+		vp := reflect.New(reflect.TypeOf(x)) // v points to a Zero of the same type as x
+		if err := checkUnmarshal(marshalled, vp.Interface()); err != nil {
+			t.Error(err)
+		}
+		v := vp.Elem().Interface()
+		if err := checkEqual(v, x); err != nil {
+			t.Error(err)
+		}
+	}
+}
+
+func TestTypesPrint(t *testing.T) {
+	// Default %v representations of rtValues and oddValues
+	for i, x := range allValues {
+		if s := fmt.Sprintf("%v", x); vstrings[i] != s {
+			t.Errorf("printing %T: want %v, got %v", x, vstrings[i], s)
+		}
+	}
+}
+
+func TestDescribed(t *testing.T) {
+	want := Described{"D", "V"}
+	marshalled, _ := Marshal(want, nil)
+
+	// Unmarshal to Described type
+	var d Described
+	if err := checkUnmarshal(marshalled, &d); err != nil {
+		t.Error(err)
+	}
+	if err := checkEqual(want, d); err != nil {
+		t.Error(err)
+	}
+
+	// Unmarshal to interface{}
+	var i interface{}
+	if err := checkUnmarshal(marshalled, &i); err != nil {
+		t.Error(err)
+	}
+	if _, ok := i.(Described); !ok {
+		t.Errorf("Expected Described, got %T(%v)", i, i)
+	}
+	if err := checkEqual(want, i); err != nil {
+		t.Error(err)
+	}
+
+	// Unmarshal value only (drop descriptor) to the value type
+	var s string
+	if err := checkUnmarshal(marshalled, &s); err != nil {
+		t.Error(err)
+	}
+	if err := checkEqual(want.Value, s); err != nil {
+		t.Error(err)
+	}
+
+	// Nested described types
+	want = Described{Described{int64(123), true}, "foo"}
+	marshalled, _ = Marshal(want, nil)
+	if err := checkUnmarshal(marshalled, &d); err != nil {
+		t.Error(err)
+	}
+	if err := checkEqual(want, d); err != nil {
+		t.Error(err)
+	}
+	// Nested to interface
+	if err := checkUnmarshal(marshalled, &i); err != nil {
+		t.Error(err)
+	}
+	if err := checkEqual(want, i); err != nil {
+		t.Error(err)
+	}
+}

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/4f724ace/proton-c/bindings/go/src/qpid.apache.org/amqp/unmarshal.go
----------------------------------------------------------------------
diff --git a/proton-c/bindings/go/src/qpid.apache.org/amqp/unmarshal.go b/proton-c/bindings/go/src/qpid.apache.org/amqp/unmarshal.go
index 96b8e05..b49727e 100644
--- a/proton-c/bindings/go/src/qpid.apache.org/amqp/unmarshal.go
+++ b/proton-c/bindings/go/src/qpid.apache.org/amqp/unmarshal.go
@@ -27,6 +27,7 @@ import (
 	"fmt"
 	"io"
 	"reflect"
+	"strings"
 	"unsafe"
 )
 
@@ -44,16 +45,23 @@ type UnmarshalError struct {
 
 func (e UnmarshalError) Error() string { return e.s }
 
-func newUnmarshalError(pnType C.pn_type_t, v interface{}) *UnmarshalError {
+func newUnmarshalErrorMsg(pnType C.pn_type_t, v interface{}, msg string) *UnmarshalError {
+	if len(msg) > 0 && !strings.HasPrefix(msg, ":") {
+		msg = ": " + msg
+	}
 	e := &UnmarshalError{AMQPType: C.pn_type_t(pnType).String(), GoType: reflect.TypeOf(v)}
 	if e.GoType.Kind() != reflect.Ptr {
-		e.s = fmt.Sprintf("cannot unmarshal to type %s, not a pointer", e.GoType)
+		e.s = fmt.Sprintf("cannot unmarshal to type %s, not a pointer%s", e.GoType, msg)
 	} else {
-		e.s = fmt.Sprintf("cannot unmarshal AMQP %s to %s", e.AMQPType, e.GoType)
+		e.s = fmt.Sprintf("cannot unmarshal AMQP %s to %s%s", e.AMQPType, e.GoType, msg)
 	}
 	return e
 }
 
+func newUnmarshalError(pnType C.pn_type_t, v interface{}) *UnmarshalError {
+	return newUnmarshalErrorMsg(pnType, v, "")
+}
+
 func newUnmarshalErrorData(data *C.pn_data_t, v interface{}) *UnmarshalError {
 	err := PnError(C.pn_data_error(data))
 	if err == nil {
@@ -130,68 +138,73 @@ func (d *Decoder) Decode(v interface{}) (err error) {
 }
 
 /*
-Unmarshal decodes AMQP-encoded bytes and stores the result in the value pointed to by v.
+Unmarshal decodes AMQP-encoded bytes and stores the result in the Go value pointed to by v.
 Types are converted as follows:
 
- +---------------------------+----------------------------------------------------------------------+
- |To Go types                |From AMQP types                                                       |
- +===========================+======================================================================+
- |bool                       |bool                                                                  |
- +---------------------------+----------------------------------------------------------------------+
- |int, int8, int16,          |Equivalent or smaller signed integer type: byte, short, int, long.    |
- |int32, int64               |                                                                      |
- +---------------------------+----------------------------------------------------------------------+
- |uint, uint8, uint16,       |Equivalent or smaller unsigned integer type: ubyte, ushort, uint,     |
- |uint32, uint64 types       |ulong                                                                 |
- +---------------------------+----------------------------------------------------------------------+
- |float32, float64           |Equivalent or smaller float or double.                                |
- +---------------------------+----------------------------------------------------------------------+
- |string, []byte             |string, symbol or binary.                                             |
- +---------------------------+----------------------------------------------------------------------+
- |Symbol                     |symbol                                                                |
- +---------------------------+----------------------------------------------------------------------+
- |map[K]T                    |map, provided all keys and values can unmarshal to types K, T         |
- +---------------------------+----------------------------------------------------------------------+
- |Map                        |map, any AMQP map                                                     |
- +---------------------------+----------------------------------------------------------------------+
- |interface{}                |Any AMQP value can be unmarshaled to an interface{} as follows:       |
- |                           +------------------------+---------------------------------------------+
- |                           |AMQP Type               |Go Type in interface{}                       |
- |                           +========================+=============================================+
- |                           |bool                    |bool                                         |
- |                           +------------------------+---------------------------------------------+
- |                           |byte,short,int,long     |int8,int16,int32,int64                       |
- |                           +------------------------+---------------------------------------------+
- |                           |ubyte,ushort,uint,ulong |uint8,uint16,uint32,uint64                   |
- |                           +------------------------+---------------------------------------------+
- |                           |float, double           |float32, float64                             |
- |                           +------------------------+---------------------------------------------+
- |                           |string                  |string                                       |
- |                           +------------------------+---------------------------------------------+
- |                           |symbol                  |Symbol                                       |
- |                           +------------------------+---------------------------------------------+
- |                           |binary                  |Binary                                       |
- |                           +------------------------+---------------------------------------------+
- |                           |nulll                   |nil                                          |
- |                           +------------------------+---------------------------------------------+
- |                           |map                     |Map                                          |
- |                           +------------------------+---------------------------------------------+
- |                           |list                    |List                                         |
- +---------------------------+------------------------+---------------------------------------------+
- |Key                        |symbol, ulong                                                         |
- +---------------------------+----------------------------------------------------------------------+
-
-The following Go types cannot be unmarshaled: uintptr, function, interface, channel.
-
-TODO
-
-Go types: array, struct.
-
-AMQP types: decimal32/64/128, char (round trip), timestamp, uuid, array, multi-section message bodies.
-
-AMQP maps with mixed/unhashable key types need an alternate representation.
-
-Described types.
+ +------------------------+-------------------------------------------------+
+ |To Go types             |From AMQP types                                  |
+ +========================+=================================================+
+ |bool                    |bool                                             |
+ +------------------------+-------------------------------------------------+
+ |int, int8, int16, int32,|Equivalent or smaller signed integer type: byte, |
+ |int64                   |short, int, long.                                |
+ +------------------------+-------------------------------------------------+
+ |uint, uint8, uint16,    |Equivalent or smaller unsigned integer type:     |
+ |uint32, uint64          |ubyte, ushort, uint, ulong                       |
+ +------------------------+-------------------------------------------------+
+ |float32, float64        |Equivalent or smaller float or double.           |
+ +------------------------+-------------------------------------------------+
+ |string, []byte          |string, symbol or binary.                        |
+ +------------------------+-------------------------------------------------+
+ |Symbol                  |symbol                                           |
+ +------------------------+-------------------------------------------------+
+ |map[K]T                 |map, provided all keys and values can unmarshal  |
+ |                        |to types K,T                                     |
+ +------------------------+-------------------------------------------------+
+ |Map                     |map, any AMQP map                                |
+ +------------------------+-------------------------------------------------+
+ |Described               |described type                                   |
+ +------------------------+-------------------------------------------------+
+
+An AMQP described type can unmarshal into the corresponding plain type, discarding the descriptor.
+For example an AMQP described string can unmarshal into a plain go string.
+Unmarshal into the Described type preserves the descriptor.
+
+Any AMQP type can unmarshal to an interface{}, the Go type used to unmarshal is chosen from the AMQP type as follows
+
+ +------------------------+-------------------------------------------------+
+ |AMQP Type               |Go Type in interface{}                           |
+ +========================+=================================================+
+ |bool                    |bool                                             |
+ +------------------------+-------------------------------------------------+
+ |byte,short,int,long     |int8,int16,int32,int64                           |
+ +------------------------+-------------------------------------------------+
+ |ubyte,ushort,uint,ulong |uint8,uint16,uint32,uint64                       |
+ +------------------------+-------------------------------------------------+
+ |float, double           |float32, float64                                 |
+ +------------------------+-------------------------------------------------+
+ |string                  |string                                           |
+ +------------------------+-------------------------------------------------+
+ |symbol                  |Symbol                                           |
+ +------------------------+-------------------------------------------------+
+ |binary                  |Binary                                           |
+ +------------------------+-------------------------------------------------+
+ |null                    |nil                                              |
+ +------------------------+-------------------------------------------------+
+ |map                     |Map                                              |
+ +------------------------+-------------------------------------------------+
+ |list                    |List                                             |
+ +------------------------+-------------------------------------------------+
+ |described type          |Described                                        |
+ +--------------------------------------------------------------------------+
+
+The following Go types cannot be unmarshaled: uintptr, function, interface, channel, array (use slice), struct
+
+TODO: Not yet implemented:
+
+AMQP types: decimal32/64/128, char (round trip), timestamp, uuid.
+
+AMQP maps with mixed key types, or key types that are not legal Go map keys.
 */
 func Unmarshal(bytes []byte, v interface{}) (n int, err error) {
 	defer recoverUnmarshal(&err)
@@ -227,6 +240,15 @@ func (d *Decoder) more() error {
 // Unmarshal from data into value pointed at by v.
 func unmarshal(v interface{}, data *C.pn_data_t) {
 	pnType := C.pn_data_type(data)
+
+	// Check for PN_DESCRIBED first, as described types can unmarshal into any of the Go types.
+	// Interfaces are handled in the switch below, even for described types.
+	if _, isInterface := v.(*interface{}); !isInterface && bool(C.pn_data_is_described(data)) {
+		getDescribed(data, v)
+		return
+	}
+
+	// Unmarshal based on the target type
 	switch v := v.(type) {
 	case *bool:
 		switch pnType {
@@ -440,7 +462,7 @@ func unmarshal(v interface{}, data *C.pn_data_t) {
 			panic(newUnmarshalError(pnType, v))
 		}
 
-	default:
+	default: // This is not one of the fixed well-known types, reflect for map and slice types
 		if reflect.TypeOf(v).Kind() != reflect.Ptr {
 			panic(newUnmarshalError(pnType, v))
 		}
@@ -508,8 +530,18 @@ func getInterface(data *C.pn_data_t, v *interface{}) {
 		l := make(List, 0)
 		unmarshal(&l, data)
 		*v = l
-	default: // No data (-1 or NULL)
+	case C.PN_DESCRIBED:
+		d := Described{}
+		unmarshal(&d, data)
+		*v = d
+	case C.PN_NULL:
 		*v = nil
+	case C.PN_INVALID:
+		// Allow decoding from an empty data object to an interface, treat it like NULL.
+		// This happens when optional values or properties are omitted from a message.
+		*v = nil
+	default: // Don't know how to handle this
+		panic(newUnmarshalError(pnType, v))
 	}
 }
 
@@ -558,6 +590,29 @@ func getList(data *C.pn_data_t, v interface{}) {
 	reflect.ValueOf(v).Elem().Set(listValue)
 }
 
+func getDescribed(data *C.pn_data_t, v interface{}) {
+	d, _ := v.(*Described)
+	pnType := C.pn_data_type(data)
+	if bool(C.pn_data_enter(data)) {
+		defer C.pn_data_exit(data)
+		if bool(C.pn_data_next(data)) {
+			if d != nil {
+				unmarshal(&d.Descriptor, data)
+			}
+			if bool(C.pn_data_next(data)) {
+				if d != nil {
+					unmarshal(&d.Value, data)
+				} else {
+					unmarshal(v, data)
+				}
+				return
+			}
+		}
+	}
+	// The pn_data cursor didn't move as expected
+	panic(newUnmarshalErrorMsg(pnType, v, "bad described value encoding"))
+}
+
 // decode from bytes.
 // Return bytes decoded or 0 if we could not decode a complete object.
 //


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@qpid.apache.org
For additional commands, e-mail: commits-help@qpid.apache.org