     end
   end
 
-  defp empty_val?(nil), do: true
-  defp empty_val?(false), do: true
-  defp empty_val?(0), do: true
-  defp empty_val?(0.0), do: true
-  defp empty_val?(""), do: true
-  defp empty_val?([]), do: true
-  defp empty_val?(v) when map_size(v) == 0, do: true
-  defp empty_val?(_) do
-    false
-  end
-
   defp is_enum_default({_, type}, v) when is_atom(v), do: type.value(v) == 0
   defp is_enum_default({_, _}, v) when is_integer(v), do: v == 0
   defp is_enum_default({_, _}, _), do: false

  @spec encode(struct, keyword) :: iodata
  def encode(%mod{} = struct, opts \\ []) do
    res = encode!(struct, mod.__message_props__())
    res = Enum.reverse(res)

    case Keyword.fetch(opts, :iolist) do
      {:ok, true} -> res

    syntax = props.syntax
    oneofs = oneof_actual_vals(props, struct)

    encode_fields(Map.values(field_props), syntax, struct, oneofs, [])
    |> Enum.reverse()
  end

  def encode_fields([], _, _, _, acc) do
    acc
  end
  def encode_fields([prop|tail], syntax, struct, oneofs, acc) do
    %{name_atom: name, oneof: oneof, enum?: is_enum, type: type} = prop

    val =
      if oneof do
        oneofs[name]
      else
        case struct do
          %{^name => v} ->
            v
          _ ->
            nil
        end
      end

    if skip_field?(syntax, val, prop) || (is_enum && is_enum_default(type, val)) do
      encode_fields(tail, syntax, struct, oneofs, acc)
    else
      acc = [encode_field(class_field(prop), val, prop) | acc]
      encode_fields(tail, syntax, struct, oneofs, acc)
    end
  rescue
    error ->
      stacktrace = System.stacktrace()

      msg =
        "Got error when encoding #{inspect(struct.__struct__)}##{prop.name_atom}: #{
          inspect(error)
        }"

      reraise Protobuf.EncodeError, [message: msg], stacktrace
  end

  @doc false
  def skip_field?(syntax, val, prop)
  def skip_field?(_, [], _), do: true
  def skip_field?(_, v, _) when map_size(v) == 0, do: true
  def skip_field?(:proto2, nil, %{optional?: true}), do: true
  def skip_field?(:proto3, nil, _), do: true
  def skip_field?(:proto3, 0, %{oneof: nil}), do: true
  def skip_field?(:proto3, 0.0, %{oneof: nil}), do: true
  def skip_field?(:proto3, "", %{oneof: nil}), do: true
  def skip_field?(:proto3, false, %{oneof: nil}), do: true
  def skip_field?(_, _, _), do: false

  @spec encode_field(atom, any, FieldProps.t()) :: iodata
  def encode_field(:normal, val, %{encoded_fnum: fnum, type: type, repeated?: is_repeated}) do
