Skip to content

Commit 7329a35

Browse files
committed
fix(builder): handle case better when message struct not matched
1 parent 4b149d1 commit 7329a35

File tree

6 files changed

+73
-41
lines changed

6 files changed

+73
-41
lines changed

lib/protobuf/builder.ex

Lines changed: 39 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -39,36 +39,51 @@ defmodule Protobuf.Builder do
3939

4040
defp new_maybe_strict(mod, attrs, strict?) do
4141
case attrs do
42-
%{__struct__: _} ->
42+
# If the attrs is the module, we just return it
43+
%{__struct__: ^mod} ->
4344
attrs
4445

46+
# If the module in the attrs doesn't match with mod
47+
# raise error in strict and try changing it to a map in non-strict
48+
%{__struct__: _} ->
49+
if strict? do
50+
raise ArgumentError,
51+
message: "The __struct__ in the struct doesn't with the message module"
52+
else
53+
do_new_maybe_strict(mod, Map.from_struct(attrs), strict?)
54+
end
55+
4556
_ ->
46-
props = mod.__message_props__()
47-
default_struct = mod.__default_struct__()
48-
msg = if strict?, do: struct!(default_struct, attrs), else: struct(default_struct, attrs)
57+
do_new_maybe_strict(mod, attrs, strict?)
58+
end
59+
end
4960

50-
Enum.reduce(props.embedded_fields, msg, fn k, acc ->
51-
case msg do
52-
%{^k => v} when not is_nil(v) ->
53-
f_props = props.field_props[props.field_tags[k]]
61+
defp do_new_maybe_strict(mod, attrs, strict?) do
62+
props = mod.__message_props__()
63+
default_struct = mod.__default_struct__()
64+
msg = if strict?, do: struct!(default_struct, attrs), else: struct(default_struct, attrs)
5465

55-
v =
56-
if f_props.embedded? do
57-
if f_props.repeated? do
58-
Enum.map(v, fn i -> f_props.type.new(i) end)
59-
else
60-
f_props.type.new(v)
61-
end
62-
else
63-
v
64-
end
66+
Enum.reduce(props.embedded_fields, msg, fn k, acc ->
67+
case msg do
68+
%{^k => v} when not is_nil(v) ->
69+
f_props = props.field_props[props.field_tags[k]]
6570

66-
Map.put(acc, k, v)
71+
v =
72+
if f_props.embedded? do
73+
if f_props.repeated? do
74+
Enum.map(v, fn i -> f_props.type.new(i) end)
75+
else
76+
f_props.type.new(v)
77+
end
78+
else
79+
v
80+
end
6781

68-
_ ->
69-
acc
70-
end
71-
end)
72-
end
82+
Map.put(acc, k, v)
83+
84+
_ ->
85+
acc
86+
end
87+
end)
7388
end
7489
end

mix.exs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ defmodule Protobuf.Mixfile do
3333
{:dialyxir, "~> 0.5", only: [:dev, :test], runtime: false},
3434
{:credo, "~> 0.8", only: [:dev, :test], runtime: false},
3535
{:ex_doc, "~> 0.14", only: :dev, runtime: false},
36-
{:eqc_ex, "~> 1.4", only: [:dev, :test]}
36+
{:eqc_ex, github: "tony612/eqc_ex", branch: "fix-elixir-1-10", only: [:dev, :test]}
3737
]
3838
end
3939

mix.lock

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
%{
2-
"bunt": {:hex, :bunt, "0.2.0", "951c6e801e8b1d2cbe58ebbd3e616a869061ddadcc4863d0a2182541acae9a38", [:mix], [], "hexpm"},
3-
"credo": {:hex, :credo, "0.9.3", "76fa3e9e497ab282e0cf64b98a624aa11da702854c52c82db1bf24e54ab7c97a", [:mix], [{:bunt, "~> 0.2.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:poison, ">= 0.0.0", [hex: :poison, repo: "hexpm", optional: false]}], "hexpm"},
4-
"dialyxir": {:hex, :dialyxir, "0.5.1", "b331b091720fd93e878137add264bac4f644e1ddae07a70bf7062c7862c4b952", [:mix], [], "hexpm"},
5-
"earmark": {:hex, :earmark, "1.3.2", "b840562ea3d67795ffbb5bd88940b1bed0ed9fa32834915125ea7d02e35888a5", [:mix], [], "hexpm"},
6-
"eqc_ex": {:hex, :eqc_ex, "1.4.2", "c89322cf8fbd4f9ddcb18141fb162a871afd357c55c8c0198441ce95ffe2e105", [:mix], [], "hexpm"},
7-
"ex_doc": {:hex, :ex_doc, "0.19.3", "3c7b0f02851f5fc13b040e8e925051452e41248f685e40250d7e40b07b9f8c10", [:mix], [{:earmark, "~> 1.2", [hex: :earmark, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.10", [hex: :makeup_elixir, repo: "hexpm", optional: false]}], "hexpm"},
8-
"makeup": {:hex, :makeup, "0.8.0", "9cf32aea71c7fe0a4b2e9246c2c4978f9070257e5c9ce6d4a28ec450a839b55f", [:mix], [{:nimble_parsec, "~> 0.5.0", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm"},
9-
"makeup_elixir": {:hex, :makeup_elixir, "0.13.0", "be7a477997dcac2e48a9d695ec730b2d22418292675c75aa2d34ba0909dcdeda", [:mix], [{:makeup, "~> 0.8", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm"},
10-
"nimble_parsec": {:hex, :nimble_parsec, "0.5.0", "90e2eca3d0266e5c53f8fbe0079694740b9c91b6747f2b7e3c5d21966bba8300", [:mix], [], "hexpm"},
11-
"poison": {:hex, :poison, "3.1.0", "d9eb636610e096f86f25d9a46f35a9facac35609a7591b3be3326e99a0484665", [:mix], [], "hexpm"},
2+
"bunt": {:hex, :bunt, "0.2.0", "951c6e801e8b1d2cbe58ebbd3e616a869061ddadcc4863d0a2182541acae9a38", [:mix], [], "hexpm", "7af5c7e09fe1d40f76c8e4f9dd2be7cebd83909f31fee7cd0e9eadc567da8353"},
3+
"credo": {:hex, :credo, "0.9.3", "76fa3e9e497ab282e0cf64b98a624aa11da702854c52c82db1bf24e54ab7c97a", [:mix], [{:bunt, "~> 0.2.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:poison, ">= 0.0.0", [hex: :poison, repo: "hexpm", optional: false]}], "hexpm", "dcd1d45626f6a02abeef3fc424eaf101b05a851d3cceb9535b8ea3e14c3c17e6"},
4+
"dialyxir": {:hex, :dialyxir, "0.5.1", "b331b091720fd93e878137add264bac4f644e1ddae07a70bf7062c7862c4b952", [:mix], [], "hexpm", "6c32a70ed5d452c6650916555b1f96c79af5fc4bf286997f8b15f213de786f73"},
5+
"earmark": {:hex, :earmark, "1.3.2", "b840562ea3d67795ffbb5bd88940b1bed0ed9fa32834915125ea7d02e35888a5", [:mix], [], "hexpm", "e3be2bc3ae67781db529b80aa7e7c49904a988596e2dbff897425b48b3581161"},
6+
"eqc_ex": {:git, "https://github.com/tony612/eqc_ex.git", "43759ba1fa56dec40b0346f2e23268d18f3ccda1", [branch: "fix-elixir-1-10"]},
7+
"ex_doc": {:hex, :ex_doc, "0.19.3", "3c7b0f02851f5fc13b040e8e925051452e41248f685e40250d7e40b07b9f8c10", [:mix], [{:earmark, "~> 1.2", [hex: :earmark, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.10", [hex: :makeup_elixir, repo: "hexpm", optional: false]}], "hexpm", "0e11d67e662142fc3945b0ee410c73c8c956717fbeae4ad954b418747c734973"},
8+
"makeup": {:hex, :makeup, "0.8.0", "9cf32aea71c7fe0a4b2e9246c2c4978f9070257e5c9ce6d4a28ec450a839b55f", [:mix], [{:nimble_parsec, "~> 0.5.0", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "5fbc8e549aa9afeea2847c0769e3970537ed302f93a23ac612602e805d9d1e7f"},
9+
"makeup_elixir": {:hex, :makeup_elixir, "0.13.0", "be7a477997dcac2e48a9d695ec730b2d22418292675c75aa2d34ba0909dcdeda", [:mix], [{:makeup, "~> 0.8", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "adf0218695e22caeda2820eaba703fa46c91820d53813a2223413da3ef4ba515"},
10+
"nimble_parsec": {:hex, :nimble_parsec, "0.5.0", "90e2eca3d0266e5c53f8fbe0079694740b9c91b6747f2b7e3c5d21966bba8300", [:mix], [], "hexpm", "5c040b8469c1ff1b10093d3186e2e10dbe483cd73d79ec017993fb3985b8a9b3"},
11+
"poison": {:hex, :poison, "3.1.0", "d9eb636610e096f86f25d9a46f35a9facac35609a7591b3be3326e99a0484665", [:mix], [], "hexpm", "fec8660eb7733ee4117b85f55799fd3833eb769a6df71ccf8903e8dc5447cfce"},
1212
}

test/protobuf/builder_test.exs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,4 +53,18 @@ defmodule Protobuf.BuilderTest do
5353
Foo.new!(nonexisting_field: "foo")
5454
end
5555
end
56+
57+
test "new!/2 raises for non matched strct" do
58+
assert_raise ArgumentError, fn ->
59+
Foo.new!(Foo2.new())
60+
end
61+
end
62+
63+
test "new/2 build correct message for non matched strct" do
64+
foo = Foo.new(Foo2.new(non_matched: 1))
65+
66+
assert_raise Protobuf.EncodeError, fn ->
67+
Foo.encode(foo)
68+
end
69+
end
5670
end

test/protobuf/dsl_test.exs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ defmodule Protobuf.DSLTest do
2121
msg_props = Foo.__message_props__()
2222

2323
tags_map =
24-
Enum.reduce([1, 2, 3] ++ Enum.to_list(5..17), %{}, fn i, acc -> Map.put(acc, i, i) end)
24+
Enum.reduce([1, 2, 3] ++ Enum.to_list(5..17) ++ [101], %{}, fn i, acc -> Map.put(acc, i, i) end)
2525

2626
assert tags_map == msg_props.tags_map
2727
field_props = msg_props.field_props
@@ -62,7 +62,7 @@ defmodule Protobuf.DSLTest do
6262

6363
test "saves ordered tags" do
6464
msg_props = Foo.__message_props__()
65-
assert [1, 2, 3] ++ Enum.to_list(5..17) == msg_props.ordered_tags
65+
assert [1, 2, 3] ++ Enum.to_list(5..17) ++ [101] == msg_props.ordered_tags
6666
end
6767

6868
test "supports embedded fields" do
@@ -176,7 +176,8 @@ defmodule Protobuf.DSLTest do
176176
m: :UNKNOWN,
177177
n: 0.0,
178178
o: [],
179-
p: ""
179+
p: "",
180+
non_matched: ""
180181
} == Foo.__default_struct__()
181182
end
182183

test/support/test_msg.ex

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ defmodule TestMsg do
3333
defmodule Foo do
3434
use Protobuf, syntax: :proto3
3535

36-
defstruct [:a, :b, :c, :d, :e, :f, :g, :h, :i, :j, :k, :l, :m, :n, :o, :p]
36+
defstruct [:a, :b, :c, :d, :e, :f, :g, :h, :i, :j, :k, :l, :m, :n, :o, :p, :non_matched]
3737

3838
field :a, 1, type: :int32
3939
field :b, 2, type: :fixed64
@@ -52,12 +52,14 @@ defmodule TestMsg do
5252
field :n, 15, type: :double
5353
field :o, 16, repeated: true, type: EnumFoo, enum: true
5454
field :p, 17, type: :string, deprecated: true
55+
# Used for testing against same field name with different types(in Foo2)
56+
field :non_matched, 101, type: :string
5557
end
5658

5759
defmodule Foo2 do
5860
use Protobuf, syntax: :proto2
5961

60-
defstruct [:a, :b, :c, :d, :e, :f, :g, :h, :i, :j, :k, :l, :m]
62+
defstruct [:a, :b, :c, :d, :e, :f, :g, :h, :i, :j, :k, :l, :m, :non_matched]
6163

6264
field :a, 1, required: true, type: :int32
6365
field :b, 2, optional: true, type: :fixed64, default: 5
@@ -72,7 +74,7 @@ defmodule TestMsg do
7274
# field :j, 11, optional: true, type: EnumFoo, enum: true
7375
# field :k, 12, optioanl: true, type: :bool
7476
field :l, 13, repeated: true, type: MapFoo, map: true
75-
# field :m, 14, optional: true, type: EnumFoo, default: :B, enum: true
77+
field :non_matched, 101, type: :int32, optional: true
7678
end
7779

7880
defmodule Oneof do

0 commit comments

Comments
 (0)