Skip to content

TypedStructor is a library for defining typed structs, exceptions and record macros with effortlessly.

License

Notifications You must be signed in to change notification settings

elixir-typed-structor/typed_structor

Repository files navigation

TypedStructor

Build Status Hex.pm Documentation

TypedStructor is a library for defining structs with types effortlessly. (This library is a rewritten version of TypedStruct because it is no longer actively maintained.)

Installation

Add :typed_structor to the list of dependencies in mix.exs:

def deps do
  [
    {:typed_structor, "~> 0.1"}
  ]
end

Add :typed_structor to your .formatter.exs file

[
  # import the formatter rules from `:typed_structor`
  import_deps: [..., :typed_structor],
  inputs: [...]
]

Usage

General usage

To define a struct with types, use TypedStructor, and then define fields under the TypedStructor.typed_structor/2 macro, using the TypedStructor.field/3 macro to define each field.

defmodule User do
  # use TypedStructor to import the `typed_structor` macro
  use TypedStructor

  typed_structor do
    # Define each field with the `field` macro.
    field :id, pos_integer()

    # set a default value
    field :name, String.t(), default: "Unknown"

    # enforce a field
    field :age, non_neg_integer(), enforce: true
  end
end

This is equivalent to:

defmodule User do
  defstruct [:id, :name, :age]

  @type t() :: %__MODULE__{
    id: pos_integer() | nil,
    # Note: The 'name' can not be nil, for it has a default value.
    name: String.t(),
    age: non_neg_integer()
  }
end

Check TypedStructor.typed_structor/2 and TypedStructor.field/3 for more information.

Options

You can also generate an opaque type for the struct, even changing the type name:

defmodule User do
  use TypedStructor

  typed_structor type_kind: :opaque, type_name: :profile do
    field :id, pos_integer()
    field :name, String.t()
    field :age, non_neg_integer()
  end
end

This is equivalent to:

defmodule User do
  use TypedStructor

  defstruct [:id, :name, :age]

  @opaque profile() :: %__MODULE__{
    id: pos_integer() | nil,
    name: String.t() | nil,
    age: non_neg_integer() | nil
  }
end

Type parameters also can be defined:

defmodule User do
  use TypedStructor

  typed_structor do
    parameter :id
    parameter :name

    field :id, id
    field :name, name
    field :age, non_neg_integer()
  end
end

becomes:

defmodule User do
  @type t(id, name) :: %__MODULE__{
    id: id | nil,
    name: name | nil,
    age: non_neg_integer() | nil
  }

  defstruct [:id, :name, :age]
end

If you prefer to define a struct in a submodule, pass the module option.

defmodule User do
  use TypedStructor

  # `%User.Profile{}` is generated
  typed_structor module: Profile do
    field :id, pos_integer()
    field :name, String.t()
    field :age, non_neg_integer()
  end
end

You can define the type only without defining the struct, it is useful when the struct is defined by another library(like Ecto.Schema).

defmodule User do
  use Ecto.Schema
  use TypedStructor

  typed_structor define_struct: false do
    field :id, pos_integer()
    field :name, String.t()
    field :age, non_neg_integer(), default: 0
  end

  schema "users" do
    field :name, :string
    field :age, :integer, default: 0
  end
end

Documentation

To add a @typedoc to the struct type, just add the attribute in the typed_structor block:

typed_structor do
  @typedoc "A typed user"

  field :id, pos_integer()
  field :name, String.t()
  field :age, non_neg_integer()
end

You can also document submodules this way:

typedstructor module: Profile do
  @moduledoc "A user profile struct"
  @typedoc "A typed user profile"

  field :id, pos_integer()
  field :name, String.t()
  field :age, non_neg_integer()
end

Plugins

TypedStructor offers a plugin system to enhance functionality. For details on creating a plugin, refer to the TypedStructor.Plugin module.

Here is a example of TypedStructor.Plugins.Accessible plugin to define Access behavior for the struct.

defmodule User do
  use TypedStructor

  typed_structor do
    plugin TypedStructor.Plugins.Accessible

    field :id, pos_integer()
    field :name, String.t()
    field :age, non_neg_integer()
  end
end

user = %User{id: 1, name: "Phil", age: 20}
get_in(user, [:name]) # => "Phil"

About

TypedStructor is a library for defining typed structs, exceptions and record macros with effortlessly.

Resources

License

Stars

Watchers

Forks

Languages