Packages
zoi
0.18.7
0.18.7
0.18.6
0.18.5
0.18.4
0.18.3
0.18.2
0.18.1
0.18.0
0.17.4
0.17.3
0.17.2
0.17.1
0.17.0
0.16.1
0.16.0
0.15.0
0.14.1
0.14.0
0.13.1
0.13.0
0.12.1
0.12.0
0.11.1
0.11.0
0.10.7
0.10.6
0.10.5
0.10.4
0.10.3
0.10.2
0.10.1
0.10.0
0.9.1
0.9.0
0.9.0-rc.1
0.9.0-rc.0
0.8.4
0.8.3
0.8.2
0.8.1
0.8.0
0.7.4
0.7.3
0.7.2
0.7.1
0.7.0
0.6.6
0.6.5
0.6.4
0.6.3
0.6.2
0.6.1
0.6.0
0.5.7
0.5.6
0.5.5
0.5.4
0.5.3
0.5.2
0.5.1
0.5.0
0.4.0
0.3.4
0.3.3
0.3.2
0.3.1
0.3.0
0.2.3
0.2.2
0.2.1
0.2.0
0.1.3
0.1.2
0.1.1
0.1.0
Zoi is a schema validation library for Elixir, designed to provide a simple and flexible way to define and validate data.
Current section
Files
Jump to
Current section
Files
lib/zoi/types/struct.ex
defmodule Zoi.Types.Struct do
@moduledoc false
use Zoi.Type.Def, fields: [:module, :fields, :unrecognized_keys, :coerce, empty_values: []]
def opts() do
Zoi.Opts.complex_type_opts()
end
def new(module, nil, opts) do
opts =
opts
|> resolve_unrecognized_keys()
|> Keyword.put_new(:coerce, false)
apply_type(opts ++ [module: module, fields: nil])
end
def new(module, fields, opts) when is_map(fields) or is_list(fields) do
fields =
Enum.map(fields, fn {key, type} ->
if type.meta.required == nil do
{key, Zoi.required(type)}
else
{key, type}
end
end)
keys = Enum.map(fields, fn {key, _type} -> key end)
if Enum.any?(keys, &(!is_atom(&1))) do
raise ArgumentError, "all keys in struct must be atoms"
end
opts =
opts
|> resolve_unrecognized_keys()
|> Keyword.put_new(:coerce, false)
apply_type(opts ++ [module: module, fields: fields])
end
def new(_module, _fields, _opts) do
raise ArgumentError, "struct must receive a map or nil"
end
defp resolve_unrecognized_keys(opts) do
opts =
case {opts[:unrecognized_keys], opts[:strict]} do
{nil, nil} -> Keyword.put(opts, :unrecognized_keys, :strip)
{nil, true} -> opts |> Keyword.delete(:strict) |> Keyword.put(:unrecognized_keys, :error)
{nil, false} -> opts |> Keyword.delete(:strict) |> Keyword.put(:unrecognized_keys, :strip)
{_, _} -> Keyword.delete(opts, :strict)
end
case opts[:unrecognized_keys] do
:preserve ->
raise ArgumentError, "unrecognized_keys: :preserve is not supported for structs"
{:preserve, _} ->
raise ArgumentError, "unrecognized_keys: {:preserve, schema} is not supported for structs"
_ ->
:ok
end
opts
end
defimpl Zoi.Type do
# When fields is nil, just validate the struct type without field validation
def parse(%Zoi.Types.Struct{module: module, fields: nil}, %module{} = input, _opts) do
{:ok, input}
end
def parse(%Zoi.Types.Struct{module: module} = struct, %module{} = input, opts) do
input = Map.from_struct(input)
Zoi.Types.KeyValue.parse(struct, input, opts)
|> case do
{:ok, map} -> {:ok, struct!(module, map)}
error -> error
end
end
def parse(%Zoi.Types.Struct{module: module, coerce: true} = struct, input, opts)
when is_map(input) do
Zoi.Types.KeyValue.parse(struct, input, opts)
|> case do
{:ok, map} -> {:ok, struct!(module, map)}
error -> error
end
end
def parse(schema, _, _) do
{:error, Zoi.Error.invalid_type(:struct, error: schema.meta.error)}
end
end
defimpl Zoi.TypeSpec do
alias Zoi.Types.Meta
def spec(%Zoi.Types.Struct{module: module, fields: nil}, _opts) do
quote(do: %unquote(module){})
end
def spec(%Zoi.Types.Struct{module: module, fields: fields}, opts) do
fields
|> Enum.map(fn {key, type} ->
type = maybe_nil(type)
type_spec = Zoi.type_spec(type, opts)
quote do: {unquote(key), unquote(type_spec)}
end)
|> then("e(do: %unquote(module){unquote_splicing(&1)}))
end
defp maybe_nil(type) do
if Meta.optional?(type.meta) do
Zoi.Types.Nullable.new(type)
else
type
end
end
end
defimpl Inspect do
import Inspect.Algebra
def inspect(%{fields: nil} = type, opts) do
Zoi.Inspect.build(type, opts, module: type.module)
end
def inspect(type, opts) do
fields_doc =
container_doc("%{", type.fields, "}", %Inspect.Opts{limit: 10}, fn
{key, schema}, _opts -> concat("#{key}: ", Inspect.inspect(schema, opts))
end)
Zoi.Inspect.build(type, opts, fields: fields_doc, module: type.module)
end
end
defimpl Zoi.Describe.Encoder do
def encode(%{module: module}) do
"struct of type `#{inspect(module)}`"
end
end
end