Skip to content

Commit

Permalink
message: support for pointer types other than Message
Browse files Browse the repository at this point in the history
Adds support for pointer types which are not Message.

This is especially helpful for booleans, as `*bool` can now be used to marshal
conditional boolean values where there is a difference between the value
being `false` and the boolean being unset.

Signed-off-by: Matt Ellison <matt@arroyo.io>
  • Loading branch information
mmellison authored and enr0n committed Jun 11, 2020
1 parent bbcad7b commit 42761e7
Show file tree
Hide file tree
Showing 3 changed files with 117 additions and 13 deletions.
18 changes: 5 additions & 13 deletions vici/message.go
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,9 @@ func UnmarshalMessage(m *Message, v interface{}) error {
// - map (the map must be valid as per MarshalMessage)
// - struct (the struct must be valid as per MarshalMessage)
//
// Pointer types of the above are allowed and can be used to differentiate between
// an unset value and a zero value. If a pointer is nil, it is not added to the message.
//
// If the key already exists the value is overwritten, but the ordering
// of the message is not changed.
func (m *Message) Set(key string, value interface{}) error {
Expand Down Expand Up @@ -834,13 +837,7 @@ func (m *Message) marshalField(name string, rv reflect.Value) error {
if _, ok := rv.Interface().(*Message); ok {
return m.addItem(name, rv.Interface())
}

msg := NewMessage()
if err := msg.marshal(rv.Interface()); err != nil {
return err
}

return m.addItem(name, msg)
return m.marshalField(name, reflect.Indirect(rv))

case reflect.Struct, reflect.Map:
msg := NewMessage()
Expand Down Expand Up @@ -1029,16 +1026,11 @@ func (m *Message) unmarshalField(field reflect.Value, rv reflect.Value) error {
return nil
}

msg, ok := rv.Interface().(*Message)
if !ok {
return fmt.Errorf("%v: %v", errUnmarshalNonMessage, rv.Type())
}

if field.IsNil() {
field.Set(reflect.New(field.Type().Elem()))
}

return msg.unmarshal(field.Interface())
return m.unmarshalField(field.Elem(), rv)

case reflect.Struct:
msg, ok := rv.Interface().(*Message)
Expand Down
56 changes: 56 additions & 0 deletions vici/message_marshal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,62 @@ func TestMarshalBoolFalse(t *testing.T) {
}
}

func TestMarshalBoolTruePtr(t *testing.T) {
boolValue := true
boolMessage := struct {
Field *bool `vici:"field"`
}{
Field: &boolValue,
}

m, err := MarshalMessage(boolMessage)
if err != nil {
t.Fatalf("Error marshalling pointer to bool value: %v", err)
}

value := m.Get("field")
if !reflect.DeepEqual(value, "yes") {
t.Fatalf("Marshalled boolean pointer value is invalid.\nExpected: yes\nReceived: %+v", value)
}
}

func TestMarshalBoolFalsePtr(t *testing.T) {
boolValue := false
boolMessage := struct {
Field *bool `vici:"field"`
}{
Field: &boolValue,
}

m, err := MarshalMessage(boolMessage)
if err != nil {
t.Fatalf("Error marshalling pointer to bool value: %v", err)
}

value := m.Get("field")
if !reflect.DeepEqual(value, "no") {
t.Fatalf("Marshalled boolean pointer value is invalid.\nExpected: no\nReceived: %+v", value)
}
}

func TestMarshalBoolNilPtr(t *testing.T) {
boolMessage := struct {
Field *bool `vici:"field"`
}{
Field: nil,
}

m, err := MarshalMessage(boolMessage)
if err != nil {
t.Fatalf("Error marshalling pointer to bool value: %v", err)
}

value := m.Get("field")
if value != nil {
t.Fatalf("Marshalled nil boolean pointer value is present.\nReceived: %+v", value)
}
}

func TestMarshalInt(t *testing.T) {
intMessage := struct {
Field int `vici:"field"`
Expand Down
56 changes: 56 additions & 0 deletions vici/message_unmarshal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,62 @@ func TestUnmarshalBoolInvalid(t *testing.T) {
}
}

func TestUnmarshalBoolTruePtr(t *testing.T) {
boolMessage := struct {
Field *bool `vici:"field"`
}{
Field: nil,
}

m := &Message{
[]string{"field"},
map[string]interface{}{
"field": "yes",
},
}

err := UnmarshalMessage(m, &boolMessage)
if err != nil {
t.Fatalf("Error unmarshalling bool value to pointer: %v", err)
}

if boolMessage.Field == nil {
t.Fatalf("Unmarshalled boolean pointer is nil.")
}

if *boolMessage.Field != true {
t.Fatalf("Unmarshalled boolean value is invalid.\nExpected: true\nReceived: %+v", *boolMessage.Field)
}
}

func TestUnmarshalBoolFalsePtr(t *testing.T) {
boolMessage := struct {
Field *bool `vici:"field"`
}{
Field: nil,
}

m := &Message{
[]string{"field"},
map[string]interface{}{
"field": "no",
},
}

err := UnmarshalMessage(m, &boolMessage)
if err != nil {
t.Fatalf("Error unmarshalling bool value to pointer: %v", err)
}

if boolMessage.Field == nil {
t.Fatalf("Unmarshalled boolean pointer is nil.")
}

if *boolMessage.Field != false {
t.Fatalf("Unmarshalled boolean value is invalid.\nExpected: false\nReceived: %+v", *boolMessage.Field)
}
}

func TestUnmarshalInt(t *testing.T) {
intMessage := struct {
Field int `vici:"field"`
Expand Down

0 comments on commit 42761e7

Please sign in to comment.