# Elixact
Elixact is a powerful schema definition and validation library for Elixir, inspired by Python's Pydantic.
It provides a rich DSL for defining schemas with strong type validation, automatic JSON Schema generation, and excellent developer experience.
## Features
- 🎯 **Rich Schema DSL** - Intuitive and expressive schema definitions
- 🔍 **Strong Type Validation** - Comprehensive validation for basic and complex types
- 📊 **JSON Schema Support** - Automatic generation of JSON Schema from your Elixir schemas
- 🧩 **Custom Types** - Easily define reusable custom types
- 🎄 **Nested Schemas** - Support for deeply nested data structures
- ⛓️ **Field Constraints** - Rich set of built-in constraints
- 🚨 **Structured Errors** - Clear and actionable error messages
## Installation
Add `elixact` to your list of dependencies in `mix.exs`:
```elixir
def deps do
[
{:elixact, "~> 0.1.0"}
]
end
```
## Quick Start
### Basic Schema Definition
```elixir
defmodule UserSchema do
use Elixact
schema "User account information" do
field :name, :string do
description "User's full name"
min_length 2
max_length 50
end
field :age, :integer do
description "User's age"
gt 0
lt 150
optional true
end
field :email, :string do
format ~r/^[^\s]+@[^\s]+$/
end
field :tags, {:array, :string} do
description "User tags"
min_items 0
max_items 5
default []
end
config do
title "User Schema"
strict true
end
end
end
```
### Data Validation
```elixir
# Validate data
case UserSchema.validate(%{
name: "John Doe",
email: "john@example.com",
age: 30,
tags: ["admin"]
}) do
{:ok, validated_data} ->
# Use validated data
IO.inspect(validated_data)
{:error, errors} ->
# Handle validation errors
Enum.each(errors, &IO.puts(Elixact.Error.format(&1)))
end
# Or use bang version which raises on error
validated = UserSchema.validate!(data)
```
### Complex Types
```elixir
defmodule ComplexSchema do
use Elixact
schema do
# Array of maps
field :metadata, {:array, {:map, {:string, :any}}} do
min_items 1
description "Metadata entries"
end
# Union type
field :id, {:union, [:string, :integer]} do
description "User ID (string or integer)"
end
# Nested schema
field :address, AddressSchema do
optional true
end
# Map with specific key/value types
field :settings, {:map, {:string, {:union, [:string, :boolean, :integer]}}} do
description "User settings"
default %{}
end
end
end
```
### Custom Types
```elixir
defmodule Types.Email do
use Elixact.Type
def type_definition do
Elixact.Types.string()
|> Elixact.Types.with_constraints([
format: ~r/^[^\s]+@[^\s]+$/
])
end
def json_schema do
%{
"type" => "string",
"format" => "email",
"pattern" => "^[^\\s]+@[^\\s]+$"
}
end
end
```
### JSON Schema Generation
```elixir
# Generate JSON Schema
json_schema = Elixact.JsonSchema.from_schema(UserSchema)
# The schema can be used with any JSON Schema validator
json = Jason.encode!(json_schema)
```
## Available Types
- Basic Types: `:string`, `:integer`, `:float`, `:boolean`, `:any`
- Complex Types:
- Arrays: `{:array, type}`
- Maps: `{:map, {key_type, value_type}}`
- Unions: `{:union, [type1, type2, ...]}`
- Custom Types: Any module implementing `Elixact.Type` behaviour
- Nested Schemas: References to other schema modules
## Field Constraints
- Strings: `min_length`, `max_length`, `format` (regex)
- Numbers: `gt`, `lt`, `gteq`, `lteq`
- Arrays: `min_items`, `max_items`
- General: `required`, `optional`, `default`, `choices`
## Error Handling
Elixact provides structured error messages with path information:
```elixir
{:error, [
%Elixact.Error{
path: [:email],
code: :format,
message: "invalid email format"
}
]}
```
## Contributing
1. Fork it
2. Create your feature branch (`git checkout -b feature/my-feature`)
3. Commit your changes (`git commit -am 'Add some feature'`)
4. Push to the branch (`git push origin feature/my-feature`)
5. Create a new Pull Request
## License
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.