README.md

# Smelter 🔥⚗️

JSON Schema to Elixir code generator. Extracts pure Elixir types from raw JSON Schema ore.

Generates Ecto.Schema modules with `embedded_schema` and `changeset/2` for validation.

## Features

- Full `$ref` resolution (local, cross-file, JSON pointers)
- Schema composition (`oneOf`, `anyOf`, `allOf`)
- `$defs` extraction and module generation
- Enum and const handling
- Format specifiers (date-time, uri, email, uuid)
- Nested object and array handling
- Batch generation from schema directories

## Installation

Add `smelter` to your list of dependencies in `mix.exs`:

```elixir
def deps do
  [
    {:smelter, "~> 0.1.0"}
  ]
end
```

## Usage

### Single Schema

```elixir
# Parse and resolve a schema
{:ok, schema} = Smelter.parse("path/to/schema.json")

# Generate Elixir code
code = Smelter.generate(schema, module: "MyApp.Schemas.User")

# Or do both in one step
{:ok, code} = Smelter.compile("path/to/schema.json", module: "MyApp.Schemas.User")
```

### Batch Generation

Smelter can process entire directories of JSON Schemas, preserving the folder structure:

```elixir
Smelter.Batch.generate(
  schema_dir: "priv/schemas/2026-01-11",
  output_dir: "lib/my_app/schemas",
  module_prefix: "MyApp.Schemas"
)
```

This processes all `.json` files in the directory, including:
- Root schemas with `properties`
- Union schemas (`oneOf`/`anyOf`)
- `$defs` entries within schemas

## Generated Code

Given a JSON Schema like:

```json
{
  "title": "User",
  "type": "object",
  "required": ["name", "email"],
  "properties": {
    "name": { "type": "string" },
    "email": { "type": "string", "format": "email" },
    "age": { "type": "integer", "minimum": 0 }
  }
}
```

Smelter generates:

```elixir
defmodule MyApp.Schemas.User do
  @moduledoc """
  User
  """
  use Ecto.Schema
  import Ecto.Changeset

  @primary_key false
  embedded_schema do
    field :age, :integer
    field :email, :string
    field :name, :string
  end

  def changeset(struct \\ %__MODULE__{}, params) do
    struct
    |> cast(params, [:age, :email, :name])
    |> validate_required([:email, :name])
  end
end
```

## Schema Composition

Smelter handles JSON Schema composition keywords:

### allOf

Properties from all schemas are merged together:

```json
{
  "allOf": [
    { "$ref": "base.json" },
    { "properties": { "extra": { "type": "string" } } }
  ]
}
```

### oneOf / anyOf

Generates union type modules that delegate to the appropriate variant:

```json
{
  "oneOf": [
    { "$ref": "error.json" },
    { "$ref": "warning.json" }
  ]
}
```

Generated code uses a discriminator field (commonly `type`) to route to the correct schema module.

### $defs

Entries in `$defs` are extracted and generated as separate modules. A schema like:

```json
{
  "$defs": {
    "Address": {
      "type": "object",
      "properties": { "city": { "type": "string" } }
    }
  }
}
```

Generates `MyApp.Schemas.ContainerName.Address` module.

## Real-World Usage: Bazaar

[Bazaar](https://github.com/georgeguimaraes/bazaar) uses Smelter to generate UCP schemas:

```bash
mix bazaar.gen.schemas priv/ucp_schemas/2026-01-11
```

This generates all Elixir modules in `lib/bazaar/schemas/` from the official UCP JSON Schemas.

## License

Apache-2.0