Current section

Files

Jump to
oaskit lib oaskit internal controller_builder.ex
Raw

lib/oaskit/internal/controller_builder.ex

defmodule Oaskit.Internal.ControllerBuilder do
# Module used to build Operation structs and sub structs from the operation
# macro.
@moduledoc false
@undef :__undef__
def make(opts, target) when is_list(opts) when is_map(opts) do
{target, opts, %{}}
end
def make(other, target) do
raise ArgumentError,
"invalid value for Open API model #{inspect(target)} in controller, " <>
"expected keyword list or map, got: #{inspect(other)}"
end
def nocast(value) do
{:ok, value}
end
defp with_cast(target, input, output, key, value, caster) do
case cast(value, caster) do
{:ok, cast_value} ->
{target, input, Map.put(output, key, cast_value)}
{:error, errmsg} when is_binary(errmsg) ->
raise ArgumentError,
message:
"could not cast key #{inspect(key)} when building #{inspect(target)}, got: #{errmsg}"
end
end
defp cast(value, {caster, errmsg}) when is_function(caster, 1) do
cast(value, caster, errmsg)
end
defp cast(value, caster) when is_function(caster, 1) do
cast(value, caster, "invalid value")
end
# cast value expect result tuples because we may want to use generic casts
# from other libraries. But the whole spec building otherwise just raises
# exceptions.
defp cast(value, caster, errmsg) do
case caster.(value) do
{:ok, _} = fine -> fine
{:error, reason} -> {:error, "#{errmsg}, #{inspect(reason)}, value: #{inspect(value)}"}
end
end
defp pop(container, key) when is_map(container) do
case Map.pop(container, key, @undef) do
{@undef, _} -> :error
{value, container} -> {:ok, value, container}
end
end
defp pop(container, key) when is_list(container) do
case Keyword.pop(container, key, @undef) do
{@undef, _} -> :error
{value, container} -> {:ok, value, container}
end
end
defp pop(container, key) do
raise "cannot fetch key #{inspect(key)} from data, expected a map or keyword list, got: #{inspect(container)}"
end
defp set(container, key, value) when is_list(container) do
Keyword.put(container, key, value)
end
defp set(container, key, value) when is_map(container) do
Map.put(container, key, value)
end
def put({target, input, output}, key, value) when is_atom(key) do
{target, input, set(output, key, value)}
end
def rename_input({target, input, output}, inkey, outkey) do
case pop(input, inkey) do
{:ok, value, input} -> {target, set(input, outkey, value), output}
:error -> {target, input, output}
end
end
def take_required({target, input, output}, key, cast \\ &nocast/1) do
case pop(input, key) do
{:ok, value, input} ->
with_cast(target, input, output, key, value, cast)
:error ->
raise ArgumentError, "key #{inspect(key)} is required when building #{inspect(target)}"
end
end
def take_default({target, input, output}, key, default, cast \\ &nocast/1) do
case pop(input, key) do
{:ok, value, input} -> with_cast(target, input, output, key, value, cast)
:error -> {target, input, Map.put(output, key, default)}
end
end
def take_default_lazy({target, input, output}, key, generate, cast \\ &nocast/1)
when is_function(generate, 0) do
case pop(input, key) do
{:ok, value, input} -> with_cast(target, input, output, key, value, cast)
:error -> {target, input, Map.put(output, key, generate.())}
end
end
def into({target, _, output}) do
struct!(target, output)
end
end