# `JSV.Schema.Composer`
[🔗](https://github.com/lud/jsv/blob/v0.18.3/lib/jsv/schema/composer.ex#L1)

This module contains a composable API to build schemas in a functionnal way.

Every function will return a schema and accepts an optional _first_ argument
to merge onto, using `JSV.Schema.merge/2`.

See `JSV.Schema.Helpers` to work with a more "presets" oriented API.

## Example

    iex> %JSV.Schema{}
    ...> |> object()
    ...> |> properties(foo: string())
    ...> |> required([:foo])
    %JSV.Schema{type: :object, properties: %{foo: %JSV.Schema{type: :string}}, required: [:foo]}

# `properties`

```elixir
@type properties() ::
  [{property_key(), JSV.Schema.schema()}]
  | %{optional(property_key()) =&gt; JSV.Schema.schema()}
```

# `property_key`

```elixir
@type property_key() :: atom() | binary()
```

# `all_of`

```elixir
@spec all_of(JSV.Schema.merge_base(), [JSV.Schema.schema()]) :: JSV.Schema.schema()
```

Defines or merges onto a JSON Schema with `allOf: schemas`.

# `any_of`

```elixir
@spec any_of(JSV.Schema.merge_base(), [JSV.Schema.schema()]) :: JSV.Schema.schema()
```

Defines or merges onto a JSON Schema with `anyOf: schemas`.

# `array_of`

```elixir
@spec array_of(JSV.Schema.merge_base(), JSV.Schema.schema()) :: JSV.Schema.schema()
```

Defines or merges onto a JSON Schema with `type: :array` and `items: item_schema`.

# `boolean`

```elixir
@spec boolean(JSV.Schema.merge_base()) :: JSV.Schema.schema()
```

Defines or merges onto a JSON Schema with `type: :boolean`.

# `date`

```elixir
@spec date(JSV.Schema.merge_base()) :: JSV.Schema.schema()
```

Defines or merges onto a JSON Schema with `type: :string` and `format: :date`.

# `datetime`

```elixir
@spec datetime(JSV.Schema.merge_base()) :: JSV.Schema.schema()
```

Defines or merges onto a JSON Schema with `type: :string` and `format: :"date-time"`.

# `email`

```elixir
@spec email(JSV.Schema.merge_base()) :: JSV.Schema.schema()
```

Defines or merges onto a JSON Schema with `type: :string` and `format: :email`.

# `format`

```elixir
@spec format(JSV.Schema.merge_base(), term()) :: JSV.Schema.schema()
```

Defines or merges onto a JSON Schema with `format: format`.

Does **not** set the `type: :string` on the schema. Use `string_of/2` for a
shortcut.

# `integer`

```elixir
@spec integer(JSV.Schema.merge_base()) :: JSV.Schema.schema()
```

Defines or merges onto a JSON Schema with `type: :integer`.

# `items`

```elixir
@spec items(JSV.Schema.merge_base(), JSV.Schema.schema()) :: JSV.Schema.schema()
```

Defines or merges onto a JSON Schema with `items: item_schema`.

Does **not** set the `type: :array` on the schema. Use `array_of/2` for a
shortcut.

# `neg_integer`

```elixir
@spec neg_integer(JSV.Schema.merge_base()) :: JSV.Schema.schema()
```

Defines or merges onto a JSON Schema with `type: :integer` and `maximum: -1`.

# `non_empty_string`

```elixir
@spec non_empty_string(JSV.Schema.merge_base()) :: JSV.Schema.schema()
```

Defines or merges onto a JSON Schema with `type: :string` and `minLength: 1`.

# `non_neg_integer`

```elixir
@spec non_neg_integer(JSV.Schema.merge_base()) :: JSV.Schema.schema()
```

Defines or merges onto a JSON Schema with `type: :integer` and `minimum: 0`.

# `number`

```elixir
@spec number(JSV.Schema.merge_base()) :: JSV.Schema.schema()
```

Defines or merges onto a JSON Schema with `type: :number`.

# `object`

```elixir
@spec object(JSV.Schema.merge_base()) :: JSV.Schema.schema()
```

Defines or merges onto a JSON Schema with `type: :object`.

See `props/2` to define the properties as well.

# `one_of`

```elixir
@spec one_of(JSV.Schema.merge_base(), [JSV.Schema.schema()]) :: JSV.Schema.schema()
```

Defines or merges onto a JSON Schema with `oneOf: schemas`.

# `pos_integer`

```elixir
@spec pos_integer(JSV.Schema.merge_base()) :: JSV.Schema.schema()
```

Defines or merges onto a JSON Schema with `type: :integer` and `minimum: 1`.

# `properties`

```elixir
@spec properties(
  JSV.Schema.merge_base(),
  properties()
) :: JSV.Schema.schema()
```

Defines or merges onto a JSON Schema with `properties: properties`.

Does **not** set the `type: :object` on the schema. Use `props/2` for a
shortcut.

# `props`

```elixir
@spec props(
  JSV.Schema.merge_base(),
  properties()
) :: JSV.Schema.schema()
```

Defines or merges onto a JSON Schema with `type: :object` and `properties: properties`.

# `ref`

```elixir
@spec ref(JSV.Schema.merge_base(), String.t()) :: JSV.Schema.schema()
```

Defines or merges onto a JSON Schema with `$ref: ref`.

A struct-based schema module name is not a valid reference. Modules should be
passed directly where a schema (and not a `$ref`) is expected.

#### Example

For instance to define a `user` property, this is valid:
```
props(user: UserSchema)
```

The following is invalid:
```
# Do not do this
props(user: ref(UserSchema))
```

# `required`

```elixir
@spec required(JSV.Schema.merge_base(), [atom() | binary()]) :: JSV.Schema.schema()
```

Defines a JSON Schema with `required: keys` or adds the given `keys` if the
[base schema](JSV.Schema.html#merge/2) already has a `:required`
definition.

Existing required keys are preserved.

### Examples

    iex> JSV.Schema.required(%{}, [:a, :b])
    %{required: [:a, :b]}

    iex> JSV.Schema.required(%{required: nil}, [:a, :b])
    %{required: [:a, :b]}

    iex> JSV.Schema.required(%{required: [:c]}, [:a, :b])
    %{required: [:a, :b, :c]}

    iex> JSV.Schema.required(%{required: [:a]}, [:a])
    %{required: [:a, :a]}

Use `JSV.Schema.merge/2` to replace existing required keys.

    iex> JSV.Schema.merge(%{required: [:a, :b, :c]}, required: [:x, :y, :z])
    %{required: [:x, :y, :z]}

# `string`

```elixir
@spec string(JSV.Schema.merge_base()) :: JSV.Schema.schema()
```

Defines or merges onto a JSON Schema with `type: :string`.

# `string_of`

```elixir
@spec string_of(JSV.Schema.merge_base(), term()) :: JSV.Schema.schema()
```

Defines or merges onto a JSON Schema with `type: :string` and `format: format`.

# `string_to_atom`

```elixir
@spec string_to_atom(JSV.Schema.merge_base()) :: JSV.Schema.schema()
```

Defines or merges onto a JSON Schema with `type: :string` and `jsv-cast: JSV.Cast.string_to_atom()`.

# `string_to_atom_enum`

```elixir
@spec string_to_atom_enum(JSV.Schema.merge_base(), [atom()]) :: JSV.Schema.schema()
```

Defines or merges onto a JSON Schema with `type: :string`, `enum: enum` and `jsv-cast: JSV.Cast.string_to_atom()`.

Accepts a list of atoms and validates that a given value is a string
representation of one of the given atoms.

On validation, a cast will be made to return the original atom value.

This is useful when dealing with enums that are represented as atoms in the
codebase, such as Oban job statuses or other Ecto enum types.

    iex> schema = JSV.Schema.props(status: JSV.Schema.Composer.string_to_atom_enum([:executing, :pending]))
    iex> root = JSV.build!(schema)
    iex> JSV.validate(%{"status" => "pending"}, root)
    {:ok, %{"status" => :pending}}

> #### Does not support `nil` {: .warning}
>
> This function sets the `string` type on the schema. If `nil` is given in the
> enum, the corresponding valid JSON value will be the `"nil"` string rather
> than `null`

# `string_to_boolean`

```elixir
@spec string_to_boolean(JSV.Schema.merge_base()) :: JSV.Schema.schema()
```

Defines or merges onto a JSON Schema with `type: :string` and `jsv-cast: JSV.Cast.string_to_boolean()`.

# `string_to_existing_atom`

```elixir
@spec string_to_existing_atom(JSV.Schema.merge_base()) :: JSV.Schema.schema()
```

Defines or merges onto a JSON Schema with `type: :string` and `jsv-cast: JSV.Cast.string_to_existing_atom()`.

# `string_to_float`

```elixir
@spec string_to_float(JSV.Schema.merge_base()) :: JSV.Schema.schema()
```

Defines or merges onto a JSON Schema with `type: :string` and `jsv-cast: JSV.Cast.string_to_float()`.

# `string_to_integer`

```elixir
@spec string_to_integer(JSV.Schema.merge_base()) :: JSV.Schema.schema()
```

Defines or merges onto a JSON Schema with `type: :string` and `jsv-cast: JSV.Cast.string_to_integer()`.

# `string_to_number`

```elixir
@spec string_to_number(JSV.Schema.merge_base()) :: JSV.Schema.schema()
```

Defines or merges onto a JSON Schema with `type: :string` and `jsv-cast: JSV.Cast.string_to_number()`.

# `uri`

```elixir
@spec uri(JSV.Schema.merge_base()) :: JSV.Schema.schema()
```

Defines or merges onto a JSON Schema with `type: :string` and `format: :uri`.

# `uuid`

```elixir
@spec uuid(JSV.Schema.merge_base()) :: JSV.Schema.schema()
```

Defines or merges onto a JSON Schema with `type: :string` and `format: :uuid`.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
