Current section
Files
Jump to
Current section
Files
lib/uuidv7/boxed.ex
defmodule UUIDv7.Boxed do
@moduledoc """
Ecto type that autogenerates `%UUID{}` structs holding raw UUIDv7 bytes.
## Why
The default `Ecto.UUID` (and `UUIDv7` by delegation) stores UUIDs as
36-char hex strings in your schemas. Every `Repo.insert/1` pays a hex
→ binary decode in `dump/1`, and every `autogenerate/0` pays a binary
→ hex encode it will undo a moment later.
`UUIDv7.Boxed` cuts the round-trip: it autogenerates `%UUID{}` structs
carrying the raw 16 bytes, and `dump/1` returns those bytes directly.
Hex encoding is deferred to `to_string/1`, `inspect/1`, and JSON
encoding — paid once at display time, not on every write.
## Use as a primary key
schema "users" do
@primary_key {:id, UUIDv7.Boxed, autogenerate: true}
...
end
Values come back as `%UUID{bytes: <<_::128>>}`. `cast/1` accepts the
struct, a 16-byte raw binary, or a 36-char hex string.
"""
if Code.ensure_loaded?(Ecto.Type) do
use Ecto.Type
@impl true
def type, do: :uuid
@impl true
def autogenerate, do: %UUID{bytes: UUIDv7.bingenerate()}
@impl true
def cast(%UUID{bytes: <<_::128>>} = uuid), do: {:ok, uuid}
def cast(<<_::128>> = raw), do: {:ok, %UUID{bytes: raw}}
def cast(<<_::288>> = hex), do: UUID.parse(hex)
def cast(_), do: :error
@impl true
def dump(%UUID{bytes: <<_::128>> = raw}), do: {:ok, raw}
def dump(<<_::128>> = raw), do: {:ok, raw}
def dump(_), do: :error
@impl true
def load(<<_::128>> = raw), do: {:ok, %UUID{bytes: raw}}
def load(_), do: :error
end
@doc """
Bulk-generates `n` boxed UUIDv7 values sharing the same millisecond.
Delegates to `UUIDv7.bingenerate_many/1`; see that function for the
ordering and counter semantics.
## Example
iex> uuids = UUIDv7.Boxed.generate_many(4)
iex> length(uuids)
4
iex> Enum.all?(uuids, &match?(%UUID{}, &1))
true
"""
@spec generate_many(pos_integer()) :: [UUID.t()]
def generate_many(n) do
for bytes <- UUIDv7.bingenerate_many(n), do: %UUID{bytes: bytes}
end
end