lib/ostara.ex

defmodule Ostara do
  @moduledoc """
  `Ostara` transforms [Ecto](https://hexdocs.pm/ecto/Ecto.html) schema
  modules into [JSON Schema](https://json-schema.org/) structures.

  ## Example

  Given the following schema module:

      defmodule Product do
        @moduledoc "A product from Acme's catalog",
        use Ecto.Schema

        @primary_key false

        embedded_schema do
         field :product_name, :string
         field :price, :float
        end

        def changeset(data, params) do
         data
         |> cast(params, [:product_name, :price])
         |> validate_required([:product_name])
         |> validate_number(:price, greater_than: 0)
        end
      end

  `transmute/1` will produce the following map:

      %{
        "$schema" => "https://json-schema.org/draft/2020-12/schema",
        "title" => "Product",
        "type" => "object",
        "description" => "A product from Acme's catalog",
        "properties" => %{
          "productName" => %{
            "type" => "string"
          },
          "price" => %{
            "type" => "number",
            "exclusiveMinimum" => 0
          }
        },
        "required" => ["productName"]
      }
  """

  alias __MODULE__.JSONSchema

  @doc """
  Produces a JSON Schema based on the given `source` module.

  ## Options

    * `:format` - If `:json`, returns the data as pretty-printed JSON

  """
  @spec transmute(atom(), [{:format, atom()}]) :: map() | String.t()
  def transmute(source, opts \\ []) when is_atom(source) do
    format = Keyword.get(opts, :format)
    schema = JSONSchema.generate(source, root: true)

    if format == :json do
      Jason.encode!(schema, pretty: true)
    else
      schema
    end
  end
end