Home

Awesome

TypedStructLens

Build Status hex.pm version

<!-- @moduledoc -->

TypedStructLens is a TypedStruct plugin for defining a Lens on each field without writing boilerplate code.

Rationale

If you define your structs with TypedStruct and use Lens alongside, you may end up defining lenses for your fields:

defmodule Person do
  use TypedStruct

  import Lens.Macros

  typedstruct do
    field :name, String.t(), enforce: true
    field :age, non_neg_integer()
    field :happy?, boolean(), default: true
    field :phone, String.t()
  end

  deflens name, do: Lens.key(:name)
  deflens age, do: Lens.key(:age)
  deflens happy?, do: Lens.key(:happy?)
  deflens phone, do: Lens.key(:phone)
end

But if you are using TypedStruct, it is also highly probable that you do not like to write boilerplate code. TypedStructLens is here to write the deflens for you:

defmodule Person do
  use TypedStruct

  typedstruct do
    plugin TypedStructLens

    field :name, String.t(), enforce: true
    field :age, non_neg_integer()
    field :happy?, boolean(), default: true
    field :phone, String.t()
  end
end

Usage

Setup

To use this plugin in your project, add this to your Mix dependencies:

{:typed_struct_lens, "~> 0.1.1"}

If you do not plan to compile modules using this TypedStruct plugin at runtime, you can add runtime: false to the dependency tuple as it is only used during compilation.

General usage

To use this plugin in a typed struct, simply register it in the typedstruct block:

defmodule MyStruct do
  use TypedStruct

  typedstruct do
    # Just add this line to your struct.
    plugin TypedStructLens

    field :a_field, String.t()
    field :other_field, atom()
  end

  @spec change(t()) :: t()
  def change(data) do
    # a_field/0 is generated by TypedStructLens.
    lens = a_field()
    put_in(data, [lens], "Changed")
  end
end

Options

You can generate private lenses:

defmodule MyStruct do
  use TypedStruct

  typedstruct do
    # Define private lenses instead.
    plugin TypedStructLens, lens: :private

    field :a_field, String.t()
    # You can still make it public for a given field.
    field :other_field, atom(), lens: :public
  end
end

Conversely, you can make only a given lens private:

defmodule MyStruct do
  use TypedStruct

  typedstruct do
    # By default lenses are public.
    plugin TypedStructLens

    field :a_field, String.t()
    # You can still make it private for a given field.
    field :other_field, atom(), lens: :private
  end
end

To avoid naming clashes, you can also prefix or postfix the generated function names:

defmodule MyStruct do
  use TypedStruct

  typedstruct do
    # Configure a prefix and postfix.
    plugin TypedStructLens, prefix: :demo_, postfix: :_lens

    field :a_field, String.t()
    field :other_field, atom()
  end

  @spec change(t()) :: t()
  def change(data) do
    # demo_a_field_lens/0 is generated by TypedStructLens instead of
    # a_field/0.
    lens = demo_a_field_lens()
    put_in(data, [lens], "Changed")
  end
end
<!-- @moduledoc -->

Contributing

Before contributing to this project, please read the CONTRIBUTING.md.

License

Copyright © 2020, 2022 Jean-Philippe Cugnet

This project is licensed under the MIT license.