defmodule Parameter do @moduledoc """ `Parameter` is a library for dealing with complex datatypes by solving the following problems: - Schema creation and validation - Input data validation - Deserialization - Serialization ## Example Create a schema defmodule UserParam do use Parameter.Schema alias Parameter.Validators param do field :first_name, :string, key: "firstName", required: true field :last_name, :string, key: "lastName" field :email, :string, validator: &Validators.email(&1) has_one :address, Address do field :city, :string, required: true field :street, :string field :number, :integer end end end Load (deserialize) the schema against external parameters: params = %{ "firstName" => "John", "lastName" => "Doe", "email" => "john@email.com", "address" => %{"city" => "New York", "street" => "York"} } Parameter.load(User, params) {:ok, %{ first_name: "John", last_name: "Doe", email: "john@email.com", main_address: %{city: "New York", street: "York"} }} or Dump (serialize) a populated schema to params: schema = %{ first_name: "John", last_name: "Doe", email: "john@email.com", main_address: %{city: "New York", street: "York"} } Parameter.dump(User, params) {:ok, %{ "firstName" => "John", "lastName" => "Doe", "email" => "john@email.com", "address" => %{"city" => "New York", "street" => "York"} }} For more schema options checkout `Parameter.Schema` """ alias Parameter.Dumper alias Parameter.Loader alias Parameter.Types @unknown_opts [:error, :ignore] @spec load(module() | atom(), map(), Keyword.t()) :: {:ok, any()} | {:error, any()} def load(schema, input, opts \\ []) do opts = parse_opts(opts) Loader.load(schema, input, opts) end @spec dump(module() | atom(), map(), Keyword.t()) :: {:ok, any()} | {:error, any()} def dump(schema, input, opts \\ []) when is_map(input) do exclude = Keyword.get(opts, :exclude, []) Types.validate!(:list, exclude) Dumper.dump(schema, input, exclude: exclude) end defp parse_opts(opts) do unknown = Keyword.get(opts, :unknown, :ignore) if unknown not in @unknown_opts do raise("unknown field options should be #{inspect(@unknown_opts)}") end struct = Keyword.get(opts, :struct, false) Types.validate!(:boolean, struct) exclude = Keyword.get(opts, :exclude, []) Types.validate!(:list, exclude) [struct: struct, unknown: unknown, exclude: exclude] end end