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

Helpers to define schemas in plain Elixir code.

# `all_of`

```elixir
@spec all_of([JSV.Schema.schema()], JSV.Schema.attributes() | nil) ::
  JSV.Schema.schema()
```

Returns a JSON Schema with `allOf: schemas`.

# `any_of`

```elixir
@spec any_of([JSV.Schema.schema()], JSV.Schema.attributes() | nil) ::
  JSV.Schema.schema()
```

Returns a JSON Schema with `anyOf: schemas`.

# `array_of`

```elixir
@spec array_of(JSV.Schema.schema(), JSV.Schema.attributes() | nil) ::
  JSV.Schema.schema()
```

Returns a JSON Schema with `type: :array` and `items: item_schema`.

# `boolean`

```elixir
@spec boolean(JSV.Schema.attributes() | nil) :: JSV.Schema.schema()
```

Returns a JSON Schema with `type: :boolean`.

# `const`

```elixir
@spec const(term(), JSV.Schema.attributes() | nil) :: JSV.Schema.schema()
```

Returns a JSON Schema with `const: const`.

# `date`

```elixir
@spec date(JSV.Schema.attributes() | nil) :: JSV.Schema.schema()
```

Returns a JSON Schema with `type: :string` and `format: :date`.

# `datetime`

```elixir
@spec datetime(JSV.Schema.attributes() | nil) :: JSV.Schema.schema()
```

Returns a JSON Schema with `type: :string` and `format: :"date-time"`.

# `email`

```elixir
@spec email(JSV.Schema.attributes() | nil) :: JSV.Schema.schema()
```

Returns a JSON Schema with `type: :string` and `format: :email`.

# `enum`

```elixir
@spec enum(list(), JSV.Schema.attributes() | nil) :: JSV.Schema.schema()
```

Returns a JSON Schema with `enum: enum`.

Note that in the JSON Schema specification, if the enum contains `1` then
`1.0` is a valid value.

# `format`

```elixir
@spec format(term(), JSV.Schema.attributes() | nil) :: JSV.Schema.schema()
```

Returns 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.attributes() | nil) :: JSV.Schema.schema()
```

Returns a JSON Schema with `type: :integer`.

# `neg_integer`

```elixir
@spec neg_integer(JSV.Schema.attributes() | nil) :: JSV.Schema.schema()
```

Returns a JSON Schema with `type: :integer` and `maximum: -1`.

# `non_empty_string`

```elixir
@spec non_empty_string(JSV.Schema.attributes() | nil) :: JSV.Schema.schema()
```

Returns a JSON Schema with `type: :string` and `minLength: 1`.

# `non_neg_integer`

```elixir
@spec non_neg_integer(JSV.Schema.attributes() | nil) :: JSV.Schema.schema()
```

Returns a JSON Schema with `type: :integer` and `minimum: 0`.

# `number`

```elixir
@spec number(JSV.Schema.attributes() | nil) :: JSV.Schema.schema()
```

Returns a JSON Schema with `type: :number`.

# `object`

```elixir
@spec object(JSV.Schema.attributes() | nil) :: JSV.Schema.schema()
```

Returns a JSON Schema with `type: :object`.

See the `props/2` function that accepts properties as a first argument.

# `one_of`

```elixir
@spec one_of([JSV.Schema.schema()], JSV.Schema.attributes() | nil) ::
  JSV.Schema.schema()
```

Returns a JSON Schema with `oneOf: schemas`.

# `pos_integer`

```elixir
@spec pos_integer(JSV.Schema.attributes() | nil) :: JSV.Schema.schema()
```

Returns a JSON Schema with `type: :integer` and `minimum: 1`.

# `properties`

```elixir
@spec properties(
  properties(),
  JSV.Schema.attributes() | nil
) :: JSV.Schema.schema()
```

Returns 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(
  properties(),
  JSV.Schema.attributes() | nil
) :: JSV.Schema.schema()
```

Returns a JSON Schema with `type: :object` and `properties: properties`.

# `ref`

```elixir
@spec ref(String.t(), JSV.Schema.attributes() | nil) :: JSV.Schema.schema()
```

Returns a JSON Schema with `$ref: ref`.

Returns a schema referencing the given `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))
```

# `string`

```elixir
@spec string(JSV.Schema.attributes() | nil) :: JSV.Schema.schema()
```

Returns a JSON Schema with `type: :string`.

# `string_enum_to_atom`

```elixir
@spec string_enum_to_atom([atom()], JSV.Schema.attributes() | nil) ::
  JSV.Schema.schema()
```

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

Accepts a list of atoms and returns a schema that validates  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 = props(status: string_enum_to_atom([: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`. See `string_enum_to_atom_or_nil/2`.

# `string_enum_to_atom_or_nil`

```elixir
@spec string_enum_to_atom_or_nil([atom()], JSV.Schema.attributes() | nil) ::
  JSV.Schema.schema()
```

Returns a JSON Schema with `type: [:string, :null]`, `enum: enum` and `jsv-cast: JSV.Cast.string_to_atom_or_nil()`.

Like `string_enum_to_atom/2` but also accepts the `null` JSON value as part of the
enum.

# `string_of`

```elixir
@spec string_of(term(), JSV.Schema.attributes() | nil) :: JSV.Schema.schema()
```

Returns a JSON Schema with `type: :string` and `format: format`.

# `string_to_atom`

```elixir
@spec string_to_atom(JSV.Schema.attributes() | nil) :: JSV.Schema.schema()
```

Returns a JSON Schema with `type: :string` and `jsv-cast: JSV.Cast.string_to_atom()`.

# `string_to_boolean`

```elixir
@spec string_to_boolean(JSV.Schema.attributes() | nil) :: JSV.Schema.schema()
```

Returns 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.attributes() | nil) :: JSV.Schema.schema()
```

Returns 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.attributes() | nil) :: JSV.Schema.schema()
```

Returns a JSON Schema with `type: :string` and `jsv-cast: JSV.Cast.string_to_float()`.

# `string_to_integer`

```elixir
@spec string_to_integer(JSV.Schema.attributes() | nil) :: JSV.Schema.schema()
```

Returns a JSON Schema with `type: :string` and `jsv-cast: JSV.Cast.string_to_integer()`.

# `string_to_number`

```elixir
@spec string_to_number(JSV.Schema.attributes() | nil) :: JSV.Schema.schema()
```

Returns a JSON Schema with `type: :string` and `jsv-cast: JSV.Cast.string_to_number()`.

# `uri`

```elixir
@spec uri(JSV.Schema.attributes() | nil) :: JSV.Schema.schema()
```

Returns a JSON Schema with `type: :string` and `format: :uri`.

# `uuid`

```elixir
@spec uuid(JSV.Schema.attributes() | nil) :: JSV.Schema.schema()
```

Returns a JSON Schema with `type: :string` and `format: :uuid`.

# `properties`

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

# `property_key`

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

# `nullable`

```elixir
@spec nullable(map() | module()) :: map()
```

Makes a schema nullable by adding `:null` to the allowed types.

### Example

    iex> nullable(integer())
    %{type: [:integer, :null]}

    iex> nullable(%{type: :integer, anyOf: [%{minimum: 1}, %{maximum: -1}]})
    %{
      type: [:integer, :null],
      anyOf: [%{type: :null}, %{minimum: 1}, %{maximum: -1}]
    }

    iex> nullable(%{type: :integer, oneOf: [%{minimum: 1}, %{maximum: -1}]})
    %{
      type: [:integer, :null],
      oneOf: [%{type: :null}, %{minimum: 1}, %{maximum: -1}]
    }

When given a schema module, wraps it in an `anyOf` that allows either the
module's schema or null:

    iex> defmodule Position do
    ...>   use JSV.Schema
    ...>   defschema x: integer(), y: integer()
    ...> end
    iex> nullable(Position)
    %{anyOf: [%{type: :null}, Position]}

    iex> defmodule Point do
    ...>   def json_schema do
    ...>     %{
    ...>       "properties" => %{
    ...>         "x" => %{"type" => "integer"},
    ...>         "y" => %{"type" => "integer"}
    ...>       }
    ...>     }
    ...>   end
    ...> end
    iex> nullable(Point)
    %{anyOf: [%{type: :null}, Point]}

# `optional`

```elixir
@spec optional(
  term(),
  keyword()
) :: {:__optional__, term(), keyword()}
```

Marks a schema as optional when using the keyword list syntax with
`JSV.defschema/1` or `JSV.defschema/3`.

This is useful for recursive module references where you want to avoid
infinite nesting requirements. When used in property list syntax with
`defschema`, the property will not be marked as required.

```
defschema name: string(),
          parent: optional(MySelfReferencingModule)
```

### Skipping optional keys during JSON serialization

**This is only applicable to schema defined with `JSV.defschema/3`**. The
more generic macro `JSV.defschema/1` let you implement a full module so you
must implement the protocols yourself, or use anyOf: null/sub schema for some
properties.

When encoding a struct to JSON, optional value (set as `nil` in the struct)
are still rendered, which may be invalid if someone needs to validate the
serialized value with the original schema. As the optional properties are not
required, the `:nskip` option (for "normalization skip") with a constant value
can be given. The value will not be serialized if it matches the value.

```
defschema name: string(),
          parent: optional(MySelfReferencingModule, nskip: nil)
```

# `sigil_SD`
*macro* 

The Schema Description sigil.

A sigil used to embed long texts in schemas descriptions. Replaces all
combinations of whitespace by a single whitespace and trims the string.

It does not support any modifier.

Note that newlines are perfectly fine in schema descriptions, as they are
simply encoded as `"\n"`. This sigil is intended for schemas that need to be
compressed because they are sent over the wire repeatedly (like in HTTP APIs
or when working with LLMs).

### Example

    iex> ~SD"""
    ...> This schema represents an elixir.
    ...>
    ...> An elixir is a potion with positive outcomes!
    ...> """
    "This schema represents an elixir. An elixir is a potion with positive outcomes!"

# `~>`

An alias for `JSV.Schema.combine/2`.

### Example

    iex> object(description: "a user")
    ...> ~> any_of([AdminSchema, CustomerSchema])
    ...> ~> properties(foo: integer())
    %{
      type: :object,
      description: "a user",
      properties: %{foo: %{type: :integer}},
      anyOf: [AdminSchema, CustomerSchema]
    }

---

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