# TypedStructLens
[![Build Status](https://travis-ci.com/ejpcmac/typed_struct_lens.svg?branch=develop)](https://travis-ci.com/ejpcmac/typed_struct_lens)
[![hex.pm version](http://img.shields.io/hexpm/v/typed_struct_lens.svg?style=flat)](https://hex.pm/packages/typed_struct_lens)
<!-- @moduledoc -->
TypedStructLens is a [TypedStruct](https://github.com/ejpcmac/typed_struct)
plugin for defining a [Lens](https://github.com/obrok/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:
```elixir
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:
```elixir
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:
```elixir
{: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:
```elixir
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:
```elixir
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:
```elixir
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:
```elixir
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](CONTRIBUTING.md)
Before contributing to this project, please read the
[CONTRIBUTING.md](CONTRIBUTING.md).
## License
Copyright © 2020, 2022 Jean-Philippe Cugnet
This project is licensed under the [MIT license](LICENSE).