Current section

Files

Jump to
ash_sqids lib type.ex
Raw

lib/type.ex

defmodule AshSqids.Type do
@moduledoc """
`AshSqids.Type` implements `Ash.Type` behaviour in a module that `use`s it.
By default Sqids options are looked up by doing `Application.compile_env!(:ash_sqids, [:opts, __MODULE__])`.
`use AshSqids.Type` accepts two optional exclusive options to change that - `opts` and `config`:
- `opts` provides options directly and disables config lookup from environment.
- `config` specifies a key to be used instead of a module name.
Having either options in config environment or in `opts` is required, even if you want to use default ones
(in that case pass empty list `[]`).
Sqids options include `alphabet`, `min_length` and `blocklist`. It is recommended to provide `alphabet`.
It can be a default one - `abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789` - but just shuffled.
"""
defmacro __using__(opts) do
quote bind_quoted: [opts: opts] do
use Ash.Type
defstruct [:integer, :string]
@sqids Sqids.new!(opts[:opts] || Application.compile_env!(:ash_sqids, [:opts, opts[:config] || __MODULE__]))
def sqids(), do: @sqids
@impl true
def storage_type(_), do: :integer
@impl true
def cast_input(string, _) when is_binary(string) do
try do
[integer] = Sqids.decode!(@sqids, string)
{:ok, %__MODULE__{integer: integer, string: string}}
rescue _ ->
:error
end
end
def cast_input(%__MODULE__{} = struct, _), do: {:ok, struct}
def cast_input(nil, _), do: {:ok, nil}
def cast_input(_, _), do: :error
@impl true
def cast_stored(integer, _) when is_integer(integer) and integer >= 0 do
try do
string = Sqids.encode!(@sqids, [integer])
{:ok, %__MODULE__{integer: integer, string: string}}
rescue _ ->
:error
end
end
def cast_stored(%__MODULE__{} = struct, _), do: {:ok, struct}
def cast_stored(nil, _), do: {:ok, nil}
def cast_stored(_, _), do: :error
@impl true
def dump_to_native(%__MODULE__{} = struct, _), do: {:ok, struct.integer}
def dump_to_native(nil, _), do: {:ok, nil}
def dump_to_native(_, _), do: :error
@impl true
def generator(_), do: Ash.Type.Integer.generator(min: 0) |> Stream.map(&cast_stored(&1, nil))
defimpl String.Chars do
def to_string(struct), do: struct.string
end
defimpl Jason.Encoder do
def encode(struct, opts), do: Jason.Encode.string(struct.string, opts)
end
defimpl Inspect do
import Inspect.Algebra
def inspect(struct, opts) do
module = to_doc(struct.__struct__, opts)
integer = to_doc(struct.integer, opts)
string = to_doc(struct.string, opts)
concat(["#", module, "<", integer, ", ", string, ">"])
end
end
end
end
end