IvanRublev / Nestru

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Optional type hints

cschmatzler opened this issue · comments

Environment

  • Elixir version (elixir -v): Elixir 1.14.4 (compiled with Erlang/OTP 25)
  • Nestru version (mix deps | grep nestru | head -1): nestru 0.3.3 (Hex package) (mix)

Actual behavior

Having a struct defined like:

defmodule Kratos.Models.UIContainer do
  @moduledoc false

  @derive {Nestru.Decoder, hint: %{nodes: [Kratos.Models.UINode], messages: [Kratos.Models.UIText]}}
  defstruct [
    :action,
    :method,
    :nodes,
    :messages
  ]

  @type t :: %__MODULE__{
          :action => String.t(),
          :method => String.t(),
          :nodes => [Kratos.Models.UINode.t()],
          :messages => [Kratos.Models.UIText.t()] | nil
        }
end

the messages attribute can either be a list or nil.
Currently, when messages comes back as nil from the API, I am expectedly getting a

{:error,
 %{
   get_in_keys: [#Function<8.65378251/3 in Access.key!/1>,
    #Function<8.65378251/3 in Access.key!/1>],
   message: %{message: "The first argument should be a list. Got nil instead."},
   path: ["ui", :messages]
 }}

response.

Expected behavior

Is there a way to handle optional hints? Something like

@derive {Nestru.Decoder,
           hint: %{
             nodes: [Kratos.Models.UINode],
           },
           hint_optional: %{
             messages: [Kratos.Models.UIText]
           }}

maybe.

I guess you might want to replace nil with a default empty list to process it without an error message at all. To do so you can adapt Nestru.PreDecoder protocol like the following:

defmodule Kratos.Models.UIContainer do
  @moduledoc false

  defimpl Nestru.PreDecoder do
    def gather_fields_from_map(_value, _context, map) do
      {:ok, Map.update(map, "messages", [], &if(is_nil(&1), do: [], else: &1))}
    end
  end
  
  @derive {Nestru.Decoder, hint: %{nodes: [Kratos.Models.UINode], messages: [Kratos.Models.UIText]}}

  ...
end

Then the decoding of the map with missing messages field or when its value is set to null will return:

{:ok,
 %Kratos.Models.UIContainer{
   ...
   messages: []
 }}

How about that?

That works beautifully, sorry for missing it in the first place. Thanks!