Skip to content

Instantly share code, notes, and snippets.

@rauljordan
Created February 11, 2021 16:43
Show Gist options
  • Save rauljordan/7c38274a256b8e78cdfbee80d35fdd19 to your computer and use it in GitHub Desktop.
Save rauljordan/7c38274a256b8e78cdfbee80d35fdd19 to your computer and use it in GitHub Desktop.
Implements proto message
package main
import (
"reflect"
)
type ProtoMessage interface {
Hi()
}
// ImplementsProtoMessage checks if an input argument implements
// a proto.Message interface. This supports slices of proto messages,
// pointers to proto messages, structs, and maps.
func ImplementsProtoMessage(x interface{}) bool {
return implementsProtoMessageReflect(reflect.ValueOf(x))
}
func implementsProtoMessageReflect(val reflect.Value) bool {
switch val.Kind() {
case reflect.Slice, reflect.Map:
return implementsProtoMessageReflect(reflect.Zero(val.Type().Elem()))
case reflect.Ptr, reflect.Struct:
modelType := reflect.TypeOf((*ProtoMessage)(nil)).Elem()
return val.Type().Implements(modelType)
default:
return false
}
}
package main
import "testing"
type A struct{}
func (a *A) Hi() {}
type B struct{}
func (b B) Hi() {}
type C struct{}
func TestImplementsProtoMessage(t *testing.T) {
tests := []struct {
name string
input interface{}
want bool
}{
{
name: "Pointer to struct which implements proto.Message returns true",
input: &A{},
want: true,
},
{
name: "Pointer to struct which does not implement proto.Message returns false",
input: &C{},
want: false,
},
{
name: "Struct which implements proto.Message returns true",
input: B{},
want: true,
},
{
name: "Struct which does not implement proto.Message returns false",
input: C{},
want: false,
},
{
name: "Slice of slices of struct values which implement proto.Message returns true",
input: [][]B{{{}}},
want: true,
},
{
name: "Slice of struct values which implement proto.Message returns true",
input: []B{{}},
want: true,
},
{
name: "Empty slice of struct values which implement proto.Message returns true",
input: []B{},
want: true,
},
{
name: "Slice of struct values which do not implement proto.Message returns false",
input: []C{{}},
want: false,
},
{
name: "Empty slice of struct values which do not implement proto.Message returns false",
input: []C{},
want: false,
},
{
name: "Map of value with struct type implementing proto.Message returns true",
input: make(map[int]B),
want: true,
},
{
name: "Map of value with struct type not implementing proto.Message returns false",
input: make(map[int]C),
want: false,
},
{
name: "Map of value with pointer type implementing proto.Message returns true",
input: make(map[int]*A),
want: true,
},
{
name: "Map of value with slice of pointer type implementing proto.Message returns true",
input: make(map[int][]*A),
want: true,
},
{
name: "Map of value with slice of value type implementing proto.Message returns true",
input: make(map[int][]B),
want: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := ImplementsProtoMessage(tt.input); got != tt.want {
t.Errorf("ImplementsProtoMessage() = %v, want %v", got, tt.want)
}
})
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment