diff --git a/lib/paginator/cursor.ex b/lib/paginator/cursor.ex index 204931d..c42a7f6 100644 --- a/lib/paginator/cursor.ex +++ b/lib/paginator/cursor.ex @@ -1,16 +1,17 @@ defmodule Paginator.Cursor do @moduledoc false - def decode(nil), do: nil def decode(encoded_cursor) do encoded_cursor |> Base.url_decode64!() |> :erlang.binary_to_term([:safe]) + |> Enum.map(&Paginator.Cursor.Decode.convert/1) end def encode(values) when is_list(values) do values + |> Enum.map(&Paginator.Cursor.Encode.convert/1) |> :erlang.term_to_binary() |> Base.url_encode64() end @@ -19,3 +20,23 @@ defmodule Paginator.Cursor do encode([value]) end end + +defprotocol Paginator.Cursor.Encode do + @fallback_to_any true + + def convert(term) +end + +defprotocol Paginator.Cursor.Decode do + @fallback_to_any true + + def convert(term) +end + +defimpl Paginator.Cursor.Encode, for: Any do + def convert(term), do: term +end + +defimpl Paginator.Cursor.Decode, for: Any do + def convert(term), do: term +end diff --git a/mix.exs b/mix.exs index 6c7b367..a24d2ee 100644 --- a/mix.exs +++ b/mix.exs @@ -11,6 +11,7 @@ defmodule Paginator.Mixfile do elixirc_paths: elixirc_paths(Mix.env()), build_embedded: Mix.env() == :prod, start_permanent: Mix.env() == :prod, + consolidate_protocols: Mix.env() != :test, deps: deps(), # Hex diff --git a/test/paginator/cursor_test.exs b/test/paginator/cursor_test.exs index 55ecddc..062e3cf 100644 --- a/test/paginator/cursor_test.exs +++ b/test/paginator/cursor_test.exs @@ -1,9 +1,42 @@ defmodule Paginator.CursorTest do use ExUnit.Case, async: true + defmodule MYTEST1 do + defstruct id: nil + end + + defmodule MYTEST2 do + defstruct id: nil + end + + defimpl Paginator.Cursor.Encode, for: MYTEST1 do + def convert(term), do: {:m1, term.id} + end + + defimpl Paginator.Cursor.Decode, for: Tuple do + def convert({:m1, id}), do: %MYTEST1{id: id} + end + alias Paginator.Cursor describe "encoding and decoding terms" do + test "cursor for struct with custom implementation is shorter" do + cursor1 = Cursor.encode([%MYTEST1{id: 1}]) + + assert Cursor.decode(cursor1) == [%MYTEST1{id: 1}] + + cursor2 = Cursor.encode([%MYTEST2{id: 1}]) + + assert Cursor.decode(cursor2) == [%MYTEST2{id: 1}] + assert bit_size(cursor1) < bit_size(cursor2) + end + + test "list of lists " do + cursor = Cursor.encode([[1]]) + + assert Cursor.decode(cursor) == [[1]] + end + test "it wraps terms into lists" do cursor = Cursor.encode(1)