ExOutlines.Spec.Schema (ExOutlines v0.1.0)

Copy Markdown View Source

JSON schema-based constraint specification.

Defines field types, required fields, enums, and validation rules.

Supported Types

  • :string - String values
  • :integer - Integer values (supports :positive constraint)
  • :boolean - Boolean values (true/false)
  • :number - Numeric values (integer or float)
  • {:enum, values} - Enumerated values from a list
  • {:array, item_spec} - Array/list of items with validation

Field Specification

Each field can have:

  • type - The field type (required)
  • required - Whether the field must be present (default: false)
  • description - Documentation for the field (optional)
  • positive - For integers, must be > 0 (optional, default: false)
  • min_length - For strings, minimum length in characters (optional)
  • max_length - For strings, maximum length in characters (optional)
  • min - For integers/numbers, minimum value (optional)
  • max - For integers/numbers, maximum value (optional)
  • min_items - For arrays, minimum number of items (optional)
  • max_items - For arrays, maximum number of items (optional)
  • unique_items - For arrays, whether items must be unique (default: false)
  • pattern - For strings, custom regex pattern (Regex.t() or string)
  • format - For strings, built-in format (:email, :url, :uuid, :phone, :date)

Example

address_schema = %ExOutlines.Spec.Schema{
  fields: %{
    city: %{type: :string, required: true},
    zip_code: %{type: :string, required: true, min_length: 5}
  }
}

schema = %ExOutlines.Spec.Schema{
  fields: %{
    name: %{type: :string, required: true, description: "User's full name"},
    age: %{type: :integer, required: true, positive: true},
    role: %{type: {:enum, ["admin", "user"]}, required: false},
    active: %{type: :boolean, required: false},
    tags: %{type: {:array, %{type: :string, max_length: 20}}, max_items: 10},
    address: %{type: {:object, address_schema}, required: true}
  }
}

# Valid input with nested object
ExOutlines.Spec.validate(schema, %{
  "name" => "Alice",
  "age" => 30,
  "tags" => ["elixir"],
  "address" => %{"city" => "NYC", "zip_code" => "10001"}
})
# => {:ok, %{name: "Alice", age: 30, tags: ["elixir"], address: %{city: "NYC", zip_code: "10001"}}}

# Invalid input (missing required field)
ExOutlines.Spec.validate(schema, %{"name" => "Bob"})
# => {:error, %Diagnostics{...}}

Summary

Functions

Add a field to an existing schema.

Create a new schema with field specifications.

Get required field names from the schema.

Types

field_spec()

@type field_spec() :: %{
  type: field_type(),
  required: boolean(),
  description: String.t() | nil,
  positive: boolean(),
  min_length: non_neg_integer() | nil,
  max_length: pos_integer() | nil,
  min: number() | nil,
  max: number() | nil,
  min_items: non_neg_integer() | nil,
  max_items: pos_integer() | nil,
  unique_items: boolean(),
  pattern: Regex.t() | nil
}

field_type()

@type field_type() ::
  :string
  | :integer
  | :boolean
  | :number
  | {:enum, [any()]}
  | {:array, item_spec()}

format()

@type format() :: :email | :url | :uuid | :phone | :date

item_spec()

@type item_spec() :: %{
  type: :string | :integer | :boolean | :number | {:enum, [any()]},
  min_length: non_neg_integer() | nil,
  max_length: pos_integer() | nil,
  min: number() | nil,
  max: number() | nil
}

t()

@type t() :: %ExOutlines.Spec.Schema{fields: %{required(atom()) => field_spec()}}

Functions

add_field(schema, name, type, opts \\ [])

@spec add_field(t(), atom(), field_type(), keyword()) :: t()

Add a field to an existing schema.

Examples

iex> schema = ExOutlines.Spec.Schema.new(%{})
iex> schema = ExOutlines.Spec.Schema.add_field(schema, :name, :string, required: true)
iex> Map.has_key?(schema.fields, :name)
true

new(fields)

@spec new(map()) :: t()

Create a new schema with field specifications.

Examples

iex> schema = ExOutlines.Spec.Schema.new(%{
...>   name: %{type: :string, required: true},
...>   age: %{type: :integer, required: true, positive: true}
...> })
iex> is_struct(schema, ExOutlines.Spec.Schema)
true

required_fields(schema)

@spec required_fields(t()) :: [atom()]

Get required field names from the schema.

Examples

iex> schema = ExOutlines.Spec.Schema.new(%{
...>   name: %{type: :string, required: true},
...>   age: %{type: :integer, required: false}
...> })
iex> ExOutlines.Spec.Schema.required_fields(schema)
[:name]