Skip to content

Commit

Permalink
Remove legacy decode option before release (#10)
Browse files Browse the repository at this point in the history
In order to simplify the code and future maintenance do not support a legacy option in decode.

Migrating legacy ShortUUID to the new format is a simple as reversing the string so better not add debt to support it.
  • Loading branch information
gpedic committed Jul 14, 2023
1 parent 88b47b1 commit 57f0b42
Show file tree
Hide file tree
Showing 4 changed files with 50 additions and 48 deletions.
40 changes: 32 additions & 8 deletions CHANGELOG.md
Expand Up @@ -5,8 +5,24 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

## v3.0.0
## v3.0.0 (15.07.2023)

Breaking change, ShortUUIDs created by `< v3.0.0` will produce bad results when decoded.
The new output of `encode` is the reverse of the previous output.
This follows a change in other languages ShortUUID libraries.

To migrate ShortUUIDs created by `< v3.0.0` reverse them before passing to `decode`.

```elixir
# UUID "00000001-0001-0001-0001-000000000001" encoded using v2.1.2 to "UD6ibhr3V4YXvriP822222"
# reversing the encoded string before decode with v3.0.0 will produce the correct result
iex> "UD6ibhr3V4YXvriP822222" |> String.reverse() |> ShortUUID.decode!()
"00000001-0001-0001-0001-000000000001"
```

### Changed
* move most significant bit to the beginning of the encoded result similar to libraries in other languages (most importantly python shortuuid)
* drop support for decoding of un-padded ShortUUIDs
* drop support for formats other than regular hyphenated and unhyphenated UUIDs, MS format and binary UUIDs like are stored in PostgreSQL uuid type
Expand All @@ -15,13 +31,21 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Benchmarks
Results are not comparable to previous benchmarks due to them being run on a different system

Operating System: macOS
CPU Information: Apple M2 Max
Number of Available Cores: 12
Available memory: 32 GB
Elixir 1.15.2
Erlang 25.3.2.3
```
Operating System: macOS
CPU Information: Apple M2 Max
Number of Available Cores: 12
Available memory: 32 GB
Elixir 1.15.2
Erlang 25.3.2.3
Benchmark suite executing with the following configuration:
warmup: 2 s
time: 5 s
memory time: 0 ns
parallel: 1
inputs: none specified
```

* v3.0.0
```
Expand Down
16 changes: 7 additions & 9 deletions README.md
Expand Up @@ -43,18 +43,16 @@ iex> "00000001-0001-0001-0001-000000000001" |> ShortUUID.encode

```

To decode ShortUUID created with version < v3.0.0 use one of two methods
To migrate ShortUUIDs created using `< v3.0.0` reverse them before passing to `decode`.

```elixir
iex> "UD6ibhr3V4YXvriP822222" |> String.reverse() |> ShortUUID.decode()
{:ok, "00000001-0001-0001-0001-000000000001"}

iex> "UD6ibhr3V4YXvriP822222" |> ShortUUID.decode(legacy: true)
{:ok, "00000001-0001-0001-0001-000000000001"}

# UUID "00000001-0001-0001-0001-000000000001" encoded using v2.1.2 to "UD6ibhr3V4YXvriP822222"
# reversing the encoded string before decode with v3.0.0 will produce the correct result
iex> "UD6ibhr3V4YXvriP822222" |> String.reverse() |> ShortUUID.decode!()
"00000001-0001-0001-0001-000000000001"
```

Decoding legacy ShortUUIDs without either reversing the string first or using the legacy option will not fail but produce an incorrect result
*Warning:* Decoding ShortUUIDs created using a version `< v3.0.0` without reversing the string first will not fail but produce an incorrect result

```elixir
iex> "UD6ibhr3V4YXvriP822222" |> ShortUUID.decode!() === "00000001-0001-0001-0001-000000000001"
Expand All @@ -70,7 +68,7 @@ Add `:shortuuid` to your list of dependencies in `mix.exs`:
```elixir
def deps do
[
{:shortuuid, "~> 2.0"}
{:shortuuid, "~> 3.0"}
]
end
```
Expand Down
27 changes: 6 additions & 21 deletions lib/shortuuid.ex
Expand Up @@ -122,13 +122,10 @@ defmodule ShortUUID do
@doc """
Decodes a ShortUUID string into a UUID.
"""
@spec decode(String.t(), keyword()) :: {:ok, String.t()} | {:error, String.t()}
def decode(shortuuid, opts \\ [legacy: false])

@spec decode(String.t()) :: {:ok, String.t()} | {:error, String.t()}
def decode(
<<c1::8, c2::8, c3::8, c4::8, c5::8, c6::8, c7::8, c8::8, c9::8, c10::8, c11::8, c12::8,
c13::8, c14::8, c15::8, c16::8, c17::8, c18::8, c19::8, c20::8, c21::8, c22::8>>,
legacy: false
c13::8, c14::8, c15::8, c16::8, c17::8, c18::8, c19::8, c20::8, c21::8, c22::8>>
) do
uuid_int_value =
[
Expand Down Expand Up @@ -178,19 +175,7 @@ defmodule ShortUUID do
{:error, "Invalid input"}
end

def decode(
<<c1::8, c2::8, c3::8, c4::8, c5::8, c6::8, c7::8, c8::8, c9::8, c10::8, c11::8, c12::8,
c13::8, c14::8, c15::8, c16::8, c17::8, c18::8, c19::8, c20::8, c21::8, c22::8>>,
legacy: true
) do
decode(
<<c22, c21, c20, c19, c18, c17, c16, c15, c14, c13, c12, c11, c10, c9, c8, c7, c6, c5, c4,
c3, c2, c1>>,
legacy: false
)
end

def decode(_string, _opts) do
def decode(_string) do
{:error, "Invalid input"}
end

Expand All @@ -199,9 +184,9 @@ defmodule ShortUUID do
Raises an ArgumentError if the ShortUUID is invalid.
"""
@spec decode!(String.t(), keyword()) :: String.t() | no_return()
def decode!(string, opts \\ [legacy: false]) do
case decode(string, opts) do
@spec decode!(String.t()) :: String.t() | no_return()
def decode!(string) do
case decode(string) do
{:ok, uuid} -> uuid
{:error, message} -> raise ArgumentError, message: message
end
Expand Down
15 changes: 5 additions & 10 deletions test/shortuuid_test.exs
Expand Up @@ -118,7 +118,7 @@ defmodule ShortUUIDTest do
end
end

describe "decode/2" do
describe "decode/1" do
test "decodes a valid shortuuid" do
assert {:ok, "2a162ee5-02f4-4701-9e87-72762cbce5e2"} =
ShortUUID.decode("9VprZJ9U7Tgg2PJ8BfTAek")
Expand Down Expand Up @@ -177,18 +177,13 @@ defmodule ShortUUIDTest do
assert {:error, _} = ShortUUID.decode(false)
end

test "decodes legacy shortuuid" do
assert {:ok, "2a162ee5-02f4-4701-9e87-72762cbce5e2"} =
ShortUUID.decode("keATfB8JP2ggT7U9JZrpV9", legacy: true)
test "reversed legacy short UUID will produce correct result" do
assert {:ok, "00000001-0001-0001-0001-000000000001"} =
"UD6ibhr3V4YXvriP822222" |> String.reverse() |> ShortUUID.decode()
end
end

describe "decode!/2" do
test "decodes legacy shortuuid" do
assert "2a162ee5-02f4-4701-9e87-72762cbce5e2" =
ShortUUID.decode!("keATfB8JP2ggT7U9JZrpV9", legacy: true)
end

describe "decode!/1" do
test "raises an ArgumentError for and invalid ShortUUID" do
assert_raise ArgumentError, fn ->
ShortUUID.decode!("invalid-shortuuid")
Expand Down

0 comments on commit 57f0b42

Please sign in to comment.