Setting up structures

use Estructura or use Estructura.Nested

Basic structure definition

defmodule MyStruct do
  use Estructura
  defstruct [:field1, :field2]
end

Nested structure definition with types and validation

defmodule User do
  use Estructura.Nested
  
  defstruct [
    name: "",
    address: %{city: "", postal_code: ""}
  ]
  
  def type(:name), do: Estructura.Nested.Type.String
  def validate(:name, value), do: String.length(value) > 0
end

use Estructura.WIA

With Indifferent Access (atom and binary keys)

defmodule Config do
  use Estructura.WIA,
    fields: [
      host: [default: "localhost"],
      port: [default: 4000, coerce: true, validate: true]
    ]

  @impl Config.Coercible
  def coerce_port(value) when is_integer(value), do: {:ok, value}
  def coerce_port(value) when is_binary(value) do
    case Integer.parse(value) do
      {int, ""} -> {:ok, int}
      _ -> {:error, "invalid port"}
    end
  end

  @impl Config.Validatable
  def validate_port(port) when port in 1..65535, do: {:ok, port}
  def validate_port(_port), do: {:error, "port out of range"}
end

# Access with both atom and binary keys
config = %Config{}
config[:port]    #=> 4000
config["port"]   #=> 4000
put_in(config, ["port"], "8080")  #=> %Config{host: "localhost", port: 8080}

Type System

Built-in Types

Available types for common data structures

Estructura.Nested.Type.DateTime
Estructura.Nested.Type.Date
Estructura.Nested.Type.Time
Estructura.Nested.Type.URI
Estructura.Nested.Type.IP
Estructura.Nested.Type.String
Estructura.Nested.Type.UUID

Type Scaffolds

Enum Types for predefined values

defmodule Status do
  use Estructura.Nested.Type.Enum,
    elements: [:pending, :active, :completed]
end

Tag Sets for multiple predefined values

defmodule Categories do
  use Estructura.Nested.Type.Tags,
    elements: [:tech, :art, :science]
end

Validation and Coercion

validate/2

Define validation rules for fields

def validate(:age, value), do: value >= 0
def validate("address.postal_code", value), do: String.match?(value, ~r/^\d{5}$/)

coerce/2

Define coercion rules for data transformation

def coerce(:temperature, str) when is_binary(str) do
  case Float.parse(str) do
    {num, ""} -> {:ok, num}
    _ -> {:error, "Invalid number"}
  end
end

Special Features

Lazy Values

Defer computation until needed

defmodule Cache do
  use Estructura.Nested
  defstruct value: Estructura.Lazy.new(&expensive_computation/1)
end

Flattening

Convert nested structures to flat maps

# Enable flattening
use Estructura.Nested, flattenable: true

# Usage
Estructura.Flattenable.flatten(struct)
# => %{"name" => "value", "address_city" => "London"}

Property Testing

Generate test data automatically

property "valid structures are validated" do
  check all struct <- MyStruct.__generator__() do
    assert {:ok, ^struct} = MyStruct.validate(struct)
  end
end

Common Options

  • flattenable: true - Enable structure flattening
  • jason: true - Enable JSON encoding
  • transformer: true - Enable transformation capabilities
  • indifferent: true - Enable indifferent access (atom and binary keys)

Version: 1.14.0