README.md

# SimpleSchema

SimpleSchema is a schema for validating JSON and storing it in a specific type.

[日本語ドキュメントはこちら](TODO)

## Examples

Basic usage:

```elixir
iex> person_schema = %{name: :string, age: :integer}
iex>
iex> json = %{"name" => "John Smith", "age" => 42}
iex> SimpleSchema.from_json(person_schema, json)
{:ok, %{name: "John Smith", age: 42}}
iex>
iex> invalid_json = %{"name" => "John Smith", "age" => "invalid"}
iex> SimpleSchema.from_json(person_schema, invalid_json)
{:error, [{"Type mismatch. Expected Integer but got String.", "#/age"}]}
```

Name a simple schema:

```elixir
defmodule Person do
  @behaviour SimpleSchema

  @impl SimpleSchema
  def schema(_opts) do
    %{
      name: :string,
      age: :integer,
    }
  end

  @impl SimpleSchema
  def from_json(schema, json) do
    SimpleSchema.Schema.from_json(schema, json)
  end

  @impl SimpleSchema
  def to_json(schema, json) do
    SimpleSchema.Schema.to_json(schema, json)
  end
end
```

```elixir
iex> json = %{"name" => "John Smith", "age" => 42}
iex> SimpleSchema.from_json(Person, json)
{:ok, %{name: "John Smith", age: 42}}
```

```elixir
# Nesting
defmodule Group do
  @behaviour SimpleSchema

  @impl SimpleSchema
  def schema(_opts) do
    %{
      name: :string,
      persons: [Person],
    }
  end

  @impl SimpleSchema
  def from_json(schema, json) do
    SimpleSchema.Schema.from_json(schema, json)
  end

  @impl SimpleSchema
  def to_json(schema, json) do
    SimpleSchema.Schema.to_json(schema, json)
  end
end
```

```elixir
iex> json = %{"name" => "My Group",
...>          "persons" => [%{"name" => "John Smith", "age" => 42},
...>                        %{"name" => "Hans Schmidt", "age" => 18}]}
iex> SimpleSchema.from_json(Group, json)
{:ok, %{name: "My Group",
        persons: [%{name: "John Smith", age: 42},
                  %{name: "Hans Schmidt", age: 18}]}}
```

With struct:

```elixir
defmodule StructPerson do
  import SimpleSchema, only: [defschema: 1]
  defschema [
    name: :string,
    age: :integer,
  ]
end
```

```elixir
iex> json = %{"name" => "John Smith", "age" => 42}
iex> SimpleSchema.from_json(StructPerson, json)
{:ok, %StructPerson{name: "John Smith", age: 42}}
```

```elixir
# Nesting
defmodule StructGroup do
  import SimpleSchema, only: [defschema: 1]
  defschema [
    name: :string,
    persons: [StructPerson],
  ]
end
```

```elixir
iex> json = %{"name" => "My Group",
...>          "persons" => [%{"name" => "John Smith", "age" => 42},
...>                        %{"name" => "Hans Schmidt", "age" => 18}]}
iex> SimpleSchema.from_json(StructGroup, json)
{:ok, %StructGroup{name: "My Group",
                   persons: [%StructPerson{name: "John Smith", age: 42},
                             %StructPerson{name: "Hans Schmidt", age: 18}]}}
```

With restrictions:

```elixir
defmodule StrictPerson do
  import SimpleSchema, only: [defschema: 1]
  defschema [
    name: {:string, min_length: 4},
    age: {:integer, minimum: 20, maximum: 65},
  ]
end
```

```elixir
iex> json = %{"name" => "John Smith", "age" => 42}
iex> SimpleSchema.from_json(StrictPerson, json)
{:ok, %StrictPerson{name: "John Smith", age: 42}}
```

```elixir
# Nesting
defmodule StrictGroup do
  import SimpleSchema, only: [defschema: 1]
  defschema [
    name: {:string, min_length: 4},
    persons: {[StrictPerson], min_items: 2},
  ]
end
```

```elixir
iex> json = %{"name" => "My Group",
...>          "persons" => [%{"name" => "John Smith", "age" => 42},
...>                        %{"name" => "Hans Schmidt", "age" => 18}]}
iex> SimpleSchema.from_json(StrictGroup, json)
{:error, [{"Expected the value to be >= 20", "#/persons/1/age"}]}
```

## Types

Primitive types:

- `:boolean`
- `:integer`
- `:number`
- `:null`
- `:string`
- `:any`

Composite types:

- `%{...}`
- `[...]`

Module type:

- A module that implements `SimpleSchema` behavior

## Restrictions

You can add restrictions such as `:maximum` and `:min_length` to types.

- `{:nullable, boolean}`: If `true`, you can be set `nil`. Can specify it to all types except `:null`.
- `{:minimum, non_neg_integer}`: Minimum value. Can specify it to `:integer` and `:number` types.
- `{:maximum, non_neg_integer}`: Maximum value. Can specify it to `:integer` and `:number` types.
- `{:min_items, non_neg_integer}`: Minimum element count. Can specify it to `:array` type.
- `{:max_items, non_neg_integer}`: Maximum element count. Can specify it to `:array` type.
- `{:min_length, non_neg_integer}`: Minimum length. Can specify it to `:string` type.
- `{:max_length, non_neg_integer}`: Maximum length. Can specify it to `:string` type.
- `{:enum, [...]}`: List of possible values for elements. Can specify it to `:integer` and `:string` types.
- `{:format, :datetime | :email}`: Validate by pre-defined format. Can specify it to `:string` type.
- `{:optional, boolean}`: If true, an element as a child element of `%{...}` is not required. You can only specify it to a type of the child element of `%{...}`.
- `{:field, string}`: Corresponding JSON field name. You can only specify it to a type of the child element of `%{...}`.