defmodule EctoKsuid do
@moduledoc """
Custom Ecto type to support ksuids
"""
use Ecto.ParameterizedType
alias EctoKsuid.Options
alias EctoKsuid.Type
@typedoc """
An `EctoKsuid` is a base62 encoded string. That may or may not include an
optional configure prefix.
"""
@type t() :: String.t()
@typedoc """
A runtime_ksuid is the value of the EctoKsuid in the elixir application.
It contains the configured prefix and the ksuid stored in the database.
"""
@type runtime_ksuid() :: t()
@typedoc """
A database_ksuid is the value of the EctoKsuid in the database.
It contains just the ksuid without the configured prefix.
"""
@type database_ksuid() :: t()
@impl Ecto.ParameterizedType
defdelegate init(opts), to: Type
@impl Ecto.ParameterizedType
defdelegate type(options), to: Type
@impl Ecto.ParameterizedType
defdelegate cast(value, options), to: Type
@impl Ecto.ParameterizedType
defdelegate dump(value, dumper, options), to: Type
@impl Ecto.ParameterizedType
defdelegate load(value, loader, options), to: Type
@impl Ecto.ParameterizedType
defdelegate autogenerate(options), to: Type
@doc """
Removes the configured prefix from an EctoKsuid.
This can be used to get the raw ksuid without the runtime prefix.
## Examples
iex> EctoKsuid.remove_prefix("prefix_2EXLuF3dU8v4L4JLInQXNWpjKE0", %EctoKsuid.Options{prefix: "prefix_"})
"2EXLuF3dU8v4L4JLInQXNWpjKE0"
iex> EctoKsuid.remove_prefix("2EXLuF3dU8v4L4JLInQXNWpjKE0", %EctoKsuid.Options{prefix: ""})
"2EXLuF3dU8v4L4JLInQXNWpjKE0"
"""
@spec remove_prefix(String.t(), Options.t()) :: database_ksuid()
def remove_prefix(value, %Options{} = options) when is_binary(value) do
case options.prefix do
"" ->
value
prefix ->
String.replace_leading(value, prefix, "")
end
end
@doc """
Generates a new ksuid.
This function returns a 20 byte Ksuid which has 4 bytes as timestamp and 16
bytes of crypto string bytes.
This function delegates to the `Ksuid` library and is functionally similar to
`Ecto.UUID.generate/0`
## Examples
iex> EctoKsuid.generate()
"#{Ksuid.generate()}"
"""
@spec generate() :: t()
def generate() do
Ksuid.generate()
end
@doc """
Returns the primitive migration type for an EctoKsuid.
This can be used in your migrations, so you don't have to rememeber exactly
how large a ksuid is.
## Examples
### In an ecto migration:
```elixir
def change() do
create table(:table_name, primary_key: false) do
add(:id, EctoKsuid.column(), primary_key: true)
end
end
```
### In your repo config
```elixir
config :my_app, MyApp.Repo,
migration_primary_key: [name: :id, type: EctoKsuid.column()],
migration_foreign_key: [name: :id, type: EctoKsuid.column()]
```
"""
@spec column() :: :"char(27)"
def column() do
:"char(27)"
end
end