Skip to content

Commit

Permalink
Fix decoding of maps to match conformance tests (#391)
Browse files Browse the repository at this point in the history
* Fix decoding of maps to match conformance tests

* fix warning in test

* fix regression test name

* improve code styles, ensure booleans correct
  • Loading branch information
v0idpwn authored Jan 4, 2025
1 parent 65faf65 commit 2653f60
Show file tree
Hide file tree
Showing 2 changed files with 26 additions and 2 deletions.
22 changes: 20 additions & 2 deletions lib/protobuf/decoder.ex
Original file line number Diff line number Diff line change
Expand Up @@ -233,8 +233,17 @@ defmodule Protobuf.Decoder do
%FieldProps{type: type, map?: map?, oneof: oneof, name_atom: name_atom, repeated?: repeated?} =
prop

embedded_msg = decode(bin, type)
val = if map?, do: %{embedded_msg.key => embedded_msg.value}, else: embedded_msg
embed_msg = decode(bin, type)

val =
if map? do
key = if is_nil(embed_msg.key), do: map_default(prop, :key), else: embed_msg.key
value = if is_nil(embed_msg.value), do: map_default(prop, :value), else: embed_msg.value
%{key => value}
else
embed_msg
end

val = if oneof, do: {name_atom, val}, else: val

cond do
Expand All @@ -255,6 +264,15 @@ defmodule Protobuf.Decoder do
end
end

defp map_default(prop, key_or_value) do
prop.type.__message_props__().field_props
|> Enum.find(fn {_key, field_props} -> field_props.name_atom == key_or_value end)
|> then(fn {_key, field_props} ->
# Conformance only works when we use proto3 defaults here, even for proto2...
Protobuf.DSL.field_default(:proto3, field_props)
end)
end

defp deep_merge(_oneof1 = {tag1, val1}, oneof2 = {tag2, val2}, props) do
if tag1 == tag2 do
# If the field is a oneof, we merge its value and keep the tag.
Expand Down
6 changes: 6 additions & 0 deletions test/protobuf/conformance_regressions_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,12 @@ defmodule Protobuf.ConformanceRegressionsTest do

@describetag message_type: "protobuf_test_messages.proto3.TestAllTypesProto3"

test "Required.Proto2.ProtobufInput.ValidDataMap.INT32.INT32.MissingDefault.JsonOutput" do
mod = ProtobufTestMessages.Proto2.TestAllTypesProto2
problematic_payload = <<194, 3, 0>>
assert %{map_int32_int32: %{0 => 0}} = mod.decode(problematic_payload)
end

test "Required.Proto3.JsonInput.Int32FieldQuotedExponentialValue.JsonOutput" do
mod = ProtobufTestMessages.Proto3.TestAllTypesProto3
problematic_payload = ~S({"optionalInt32": "1e5"})
Expand Down

0 comments on commit 2653f60

Please sign in to comment.