defmodule OcppModel do @moduledoc """ OcppModel Module: Contains helper functions """ @spec to_struct(map, atom | struct()) :: struct() @doc """ Converts a Map into a struct of the given kind """ def to_struct(map, kind) when is_map(map) do struct = struct(kind) map_to_struct = fn {k, _}, acc -> case Map.fetch(map, k) do {:ok, v} -> %{acc | k => v} :error -> acc end end struct |> Map.to_list() |> Enum.reduce(struct, map_to_struct) end @spec to_map!(struct()) :: map() @doc """ Converts a struct into a map including any nested structs """ def to_map!(st, filter_empty \\ true) when is_struct(st) do struct_to_map = fn {k, v}, acc -> cond do is_list(v) -> Map.put_new(acc, k, v |> Enum.map(&to_map!(&1))) is_struct(v) -> Map.put_new(acc, k, to_map!(v)) is_nil(v) && filter_empty -> acc true -> Map.put_new(acc, k, v) end end st |> Map.from_struct() |> Enum.reduce(%{}, struct_to_map) end @spec to_map( {{:ok, struct()}, any()} | {{:error, atom(), String.t()}, any()} ) :: {{:ok, map()}, any()} | {{:error, atom(), String.t()}, any()} @doc """ same as `to_map!()` but takes an `{{:ok, struct()}, state}` and returns an `{{:ok, map()}, state}` in case of een `{{:error, atom(), String.t()}, state}` argument it will let it pass through """ def to_map({{:ok, st}, state}) when is_struct(st), do: {{:ok, to_map!(st)}, state} def to_map({{:error, error, desc}, state}), do: {{:error, error, desc}, state} @doc """ Transforms the payload into a struct and calls the implemented behaviour function and returns the response back into a map the implemented behaviour can alter the state if it wants to """ def execute(payload, model, method, state) do payload |> to_struct(model) |> method.(state) |> to_map() end end