From 6af1efe6d3d62e1d6c5534053b1073c3547bb15a Mon Sep 17 00:00:00 2001
From: Dennis Beatty <dennis@dcbeatty.com>
Date: Tue, 16 Jul 2024 23:54:01 -0600
Subject: [PATCH] allow binary type

---
 README.md           | 12 +++++++-----
 lib/type_id/ecto.ex |  9 +++++----
 2 files changed, 12 insertions(+), 9 deletions(-)

diff --git a/README.md b/README.md
index 63f4b0b..2188d88 100644
--- a/README.md
+++ b/README.md
@@ -31,7 +31,7 @@ TypeIDs as fields in your Ecto schemas.
 defmodule MyApp.Accounts.User do
   use Ecto.Schema
 
-  @primary_key {:id, TypeID, autogenerate: true, prefix: "acct", type: :binary_id}
+  @primary_key {:id, TypeID, autogenerate: true, prefix: "acct", type: :binary}
   @foreign_key_type TypeID
 
   # ...
@@ -40,9 +40,11 @@ end
 
 ### Underlying types
 
-`TypeID`s can be stored as either `:string` or `:binary_id`. `:string` will
-store the entire TypeID including the prefix. `:binary_id` stores only the
-UUID portion and requires a `:uuid` or `:binary` column.
+`TypeID`s can be stored as `:string`, `:binary_id`, or `:binary`. `:string` will
+store the entire TypeID including the prefix. `:binary` and `:binary_id` store
+only the UUID portion and require a `:uuid` or `:binary` column. `:binary_id`
+will use UUIDv4 (non K-sortable), whereas `:binary` will use UUIDv7, so
+`:binary` is recommended over `:binary_id` for that reason.
 
 #### Default type
 
@@ -50,5 +52,5 @@ The type used can be set globally in the application config.
 
 ```elixir
 config :typeid_elixir,
-  default_type: :binary_id
+  default_type: :binary
 ```
diff --git a/lib/type_id/ecto.ex b/lib/type_id/ecto.ex
index 7787395..07abd40 100644
--- a/lib/type_id/ecto.ex
+++ b/lib/type_id/ecto.ex
@@ -49,6 +49,7 @@ if Code.ensure_loaded?(Ecto.ParameterizedType) do
       case {tid.prefix, type} do
         {^prefix, :string} -> {:ok, TypeID.to_string(tid)}
         {^prefix, :binary_id} -> {:ok, TypeID.uuid(tid)}
+        {^prefix, :binary} -> {:ok, TypeID.uuid_bytes(tid)}
         _ -> :error
       end
     end
@@ -66,12 +67,12 @@ if Code.ensure_loaded?(Ecto.ParameterizedType) do
       end
     end
 
-    def load(<<_::128>> = uuid, _, %{type: :binary_id} = params) do
+    def load(<<_::128>> = uuid, _, %{type: type} = params) when type in [:binary_id, :binary] do
       prefix = find_prefix(params)
       TypeID.from_uuid_bytes(prefix, uuid)
     end
 
-    def load(<<_::288>> = uuid, _, %{type: :binary_id} = params) do
+    def load(<<_::288>> = uuid, _, %{type: type} = params) when type in [:binary_id, :binary] do
       prefix = find_prefix(params)
       TypeID.from_uuid(prefix, uuid)
     rescue
@@ -95,8 +96,8 @@ if Code.ensure_loaded?(Ecto.ParameterizedType) do
         end
       end
 
-      unless type in ~w[string binary_id]a do
-        raise ArgumentError, "`type` must be `:string` or `:binary_id`"
+      unless type in ~w[string binary_id binary]a do
+        raise ArgumentError, "`type` must be `:string`, `:binary_id`, or `:binary`"
       end
 
       if primary_key do