Skip to content

Commit cd3ce71

Browse files
mxpvcrosbymichael
authored andcommitted
Add unmarshal to struct provided by client
Signed-off-by: Maksym Pavlenko <[email protected]>
1 parent 102fdb1 commit cd3ce71

2 files changed

Lines changed: 80 additions & 3 deletions

File tree

types.go

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,14 +47,14 @@ func Register(v interface{}, args ...string) {
4747
defer mu.Unlock()
4848
if et, ok := registry[t]; ok {
4949
if et != p {
50-
panic(errors.Errorf("type registred with alternate path %q != %q", et, p))
50+
panic(errors.Errorf("type registered with alternate path %q != %q", et, p))
5151
}
5252
return
5353
}
5454
registry[t] = p
5555
}
5656

57-
// TypeURL returns the type url for a registred type.
57+
// TypeURL returns the type url for a registered type.
5858
func TypeURL(v interface{}) (string, error) {
5959
mu.Lock()
6060
u, ok := registry[tryDereference(v)]
@@ -120,16 +120,43 @@ func UnmarshalAny(any *types.Any) (interface{}, error) {
120120
}
121121

122122
func UnmarshalByTypeURL(typeURL string, value []byte) (interface{}, error) {
123+
return unmarshal(typeURL, value, nil)
124+
}
125+
126+
func UnmarshalTo(any *types.Any, out interface{}) error {
127+
return UnmarshalToByTypeURL(any.TypeUrl, any.Value, out)
128+
}
129+
130+
func UnmarshalToByTypeURL(typeURL string, value []byte, out interface{}) error {
131+
_, err := unmarshal(typeURL, value, out)
132+
return err
133+
}
134+
135+
func unmarshal(typeURL string, value []byte, v interface{}) (interface{}, error) {
123136
t, err := getTypeByUrl(typeURL)
124137
if err != nil {
125138
return nil, err
126139
}
127-
v := reflect.New(t.t).Interface()
140+
141+
if v == nil {
142+
v = reflect.New(t.t).Interface()
143+
} else {
144+
// Validate interface type provided by client
145+
vURL, err := TypeURL(v)
146+
if err != nil {
147+
return nil, err
148+
}
149+
if typeURL != vURL {
150+
return nil, errors.Errorf("can't unmarshal type %q to output %q", typeURL, vURL)
151+
}
152+
}
153+
128154
if t.isProto {
129155
err = proto.Unmarshal(value, v.(proto.Message))
130156
} else {
131157
err = json.Unmarshal(value, v)
132158
}
159+
133160
return v, err
134161
}
135162

types_test.go

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,56 @@ func TestMarshalUnmarshal(t *testing.T) {
101101
}
102102
}
103103

104+
func TestMarshalUnmarshalTo(t *testing.T) {
105+
clear()
106+
Register(&test{}, "test")
107+
108+
in := &test{
109+
Name: "koye",
110+
Age: 6,
111+
}
112+
any, err := MarshalAny(in)
113+
if err != nil {
114+
t.Fatal(err)
115+
}
116+
out := &test{}
117+
err = UnmarshalTo(any, out)
118+
if err != nil {
119+
t.Fatal(err)
120+
}
121+
if out.Name != "koye" {
122+
t.Fatal("invalid name")
123+
}
124+
if out.Age != 6 {
125+
t.Fatal("invalid age")
126+
}
127+
}
128+
129+
type test2 struct {
130+
Name string
131+
}
132+
133+
func TestUnmarshalToInvalid(t *testing.T) {
134+
clear()
135+
Register(&test{}, "test1")
136+
Register(&test2{}, "test2")
137+
138+
in := &test{
139+
Name: "koye",
140+
Age: 6,
141+
}
142+
any, err := MarshalAny(in)
143+
if err != nil {
144+
t.Fatal(err)
145+
}
146+
147+
out := &test2{}
148+
err = UnmarshalTo(any, out)
149+
if err == nil || err.Error() != `can't unmarshal type "test1" to output "test2"` {
150+
t.Fatalf("unexpected result: %+v", err)
151+
}
152+
}
153+
104154
func TestIs(t *testing.T) {
105155
clear()
106156
Register(&test{}, "test")

0 commit comments

Comments
 (0)