diff --git a/lib/uuid.ex b/lib/uuid.ex index b55bd3c..c9f0f57 100644 --- a/lib/uuid.ex +++ b/lib/uuid.ex @@ -500,7 +500,7 @@ defmodule Uniq.UUID do # Parse version defp parse_raw(<<_::48, version::uint(4), _::bitstring>> = bin, acc) do case version do - v when v in [1, 3, 4, 5, 6, 7] -> + v when v in [1, 3, 4, 5, 6, 7, 8] -> with {:ok, uuid} <- parse_raw(version, bin, acc) do {:ok, %__MODULE__{uuid | bytes: bin}} end @@ -564,6 +564,23 @@ defmodule Uniq.UUID do end end + # Generic parse proposed version 8 uuids. + # We can't validate the meaningful part of a uuid itself, but we can extract version and check variant + defp parse_raw(8, <<1::1, 0::1>> = variant, pre_variant, post_variant, acc) do + with <<_custom_a::biguint(48), _version::4, _custom_b::12>> <- <>, + <<_custom_c::62>> <- post_variant do + {:ok, + %__MODULE__{ + acc + | version: 8, + variant: variant + }} + else + _ -> + {:error, {:invalid_format, :v8}} + end + end + # Parses proposed version 7 uuids defp parse_raw(7, <<1::1, 0::1>> = variant, time, rest, acc) do with <> <- <>, @@ -604,7 +621,7 @@ defmodule Uniq.UUID do defp parse_raw(6, variant, _time, _rest, _acc), do: {:error, {:invalid_variant, variant}} - # Handles proposed version 7 and 8 uuids + # Handles other version uuids defp parse_raw(version, _variant, _time, _rest, _acc), do: {:error, {:unsupported_version, version}} diff --git a/test/support/generators.ex b/test/support/generators.ex index 1a051a1..7e5e4f5 100644 --- a/test/support/generators.ex +++ b/test/support/generators.ex @@ -9,7 +9,7 @@ defmodule Uniq.Test.Generators do @reserved_ms <<6::3>> @reserved_future <<7::3>> @rfc_versions [1, 3, 4, 5] - @versions [1, 3, 4, 5, 6, 7] + @versions [1, 3, 4, 5, 6, 7, 8] @variants [@reserved_ncs, @rfc_variant, @reserved_ms, @reserved_future] @reserved_variants [@reserved_ncs, @reserved_ms, @reserved_future] @reserved_variants_uniform [<<0::3>>, <<6::3>>, <<7::3>>] @@ -48,7 +48,7 @@ defmodule Uniq.Test.Generators do bind(bitstring(length: 128), fn <> = bits -> case v do - v when v in [6, 7] -> + v when v in [6, 7, 8] -> # Version 6 specifically only allows a single variant to be considered valid case var do <<@rfc_variant, _::1>> -> @@ -65,7 +65,7 @@ defmodule Uniq.Test.Generators do v when v in @rfc_versions -> # Any 3-bit pattern is technically valid as a variant in a UUID per the RFC, so we instead generate # a known-invalid version. - bind(integer(8..15), fn version -> + bind(integer(9..15), fn version -> constant(<>) end) diff --git a/test/uniq_test.exs b/test/uniq_test.exs index 1810179..6ded31b 100644 --- a/test/uniq_test.exs +++ b/test/uniq_test.exs @@ -17,7 +17,8 @@ defmodule Uniq.Test do # generated in the :dns namespace, name "test" 5 => "4be0643f-1d98-573b-97cd-ca98a65347dd", 6 => "1e7126af-f130-6780-adb4-8bbe7368fc2f", - 7 => "0182b66c-29e7-7ae8-b60e-4b669fe07c77" + 7 => "0182b66c-29e7-7ae8-b60e-4b669fe07c77", + 8 => "34e3992d-8a73-83de-9486-0f622063b665" } hex = @@ -68,6 +69,10 @@ defmodule Uniq.Test do assert parse(7, uuids) end + test "can parse version 8", %{uuids: uuids} do + assert parse(8, uuids) + end + property "can parse any 128-bit binary with valid version/variant values" do check all({version, variant, uuid} <- valid_uuid()) do assert {:ok, %UUID{version: ^version, variant: ^variant}} = UUID.parse(uuid) @@ -133,6 +138,10 @@ defmodule Uniq.Test do test "can format version 7", %{uuids: uuids} do assert format(7, uuids) end + + test "can format version 8", %{uuids: uuids} do + assert format(8, uuids) + end end describe "generating" do