Skip to content

Commit d0edc83

Browse files
authored
Implement custom alphabet support (#12)
* support multiple alphabets including custom alphabets * drop support for 128 bit binary uuid format * bump version to 4.0.0
1 parent 57f0b42 commit d0edc83

17 files changed

Lines changed: 1032 additions & 331 deletions

File tree

.github/workflows/ci.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ jobs:
1212
name: OTP ${{matrix.otp}} / Elixir ${{matrix.elixir}}
1313
strategy:
1414
matrix:
15-
elixir: ["1.14", "1.15"]
16-
otp: ["24.3.4.9", "25.3.2.3"]
15+
elixir: ["1.14", "1.15", "1.16"]
16+
otp: ["25.3.2.3"]
1717
env:
1818
MIX_ENV: test
1919
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,5 @@ shortuuid-*.tar
2424

2525
# Temporary files, for example, from tests.
2626
/tmp/
27+
28+
.elixir_ls

.tool-versions

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
elixir 1.15.2-otp-25
2-
erlang 25.3.2.3
1+
elixir v1.18.0-otp-27
2+
erlang 27.2

CHANGELOG.md

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,41 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8-
## [Unreleased]
8+
## v4.0.0 (25.12.2024)
9+
10+
Breaking changes:
11+
* Dropped support for binary UUID input
12+
13+
### Added
14+
* Support for custom alphabets
15+
* Predefined alphabets (base32, base58, base62, base64, etc.)
16+
* `ShortUUID.Builder` module for creating custom ShortUUID modules
17+
18+
### Changed
19+
* Moved core functionality to `ShortUUID.Core`
20+
* Simplified main `ShortUUID` module interface
21+
* Improved error messages and validation
22+
23+
```elixir
24+
# Old v3.x code still works, the ShortUUID module uses the same alphabet as in V3
25+
# If you just want to keep using ShortUUID no changes are required
26+
ShortUUID.encode(uuid)
27+
28+
# New in v4.x you can define use one of a list of predefined alphabets or define your own
29+
defmodule MyUUID do
30+
use ShortUUID.Builder, alphabet: :base58
31+
end
32+
33+
MyUUID.encode(uuid)
34+
35+
defmodule MyCustomUUID do
36+
use ShortUUID.Builder, alphabet: "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"
37+
end
38+
39+
MyCustomUUID.encode(uuid)
40+
```
41+
42+
## [Released]
943

1044
## v3.0.0 (15.07.2023)
1145

LICENSE.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# The MIT License
22

3-
Copyright (c) 2019 Goran Pedić
3+
Copyright (c) 2024 Goran Pedić
44

55
Permission is hereby granted, free of charge, to any person obtaining a copy
66
of this software and associated documentation files (the "Software"), to deal

README.md

Lines changed: 92 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,22 +10,39 @@
1010

1111
<!-- MDOC !-->
1212

13-
ShortUUID is a lightweight Elixir library that generates short and unique IDs for use in URLs. It provides a solution when you need IDs that are easy to use and understand for users.
13+
ShortUUID is a lightweight Elixir library for generating short, unique IDs in URLs. It turns standard UUIDs into smaller strings ideal for use in URLs.
14+
You can choose from a set of predefined alphabets or define your own.
15+
The default alphabet includes lowercase letters, uppercase letters, and digits, omitting characters like 'l', '1', 'I', 'O', and '0' to keep them readable.
1416

15-
Instead of using long and complex UUIDs, ShortUUID converts them into shorter strings using a combination of lowercase and uppercase letters, as well as digits. It avoids using similar-looking characters such as 'l', '1', 'I', 'O', and '0'.
17+
**Note:** Different ShortUUID implementations be compatible as long as they use the same alphabet. However, there is no official standard, so if you plan to use ShortUUID with other libraries, it's a good idea to research and test for compatibility.
1618

17-
**Note:** It's worth noting that different ShortUUID implementations should work together if they use the same set of characters. However, there is no official standard, so if you plan to use ShortUUID with other libraries, it's a good idea to research and test for compatibility.
18-
19-
Unlike some other libraries, ShortUUID doesn't generate UUIDs itself. Instead, you can input any valid UUID into the `ShortUUID.encode/1`. To generate UUIDs, you can use libraries like
19+
Unlike some other solutions, ShortUUID does not produce UUIDs on its own as there are already plenty of libraries to do so. To generate UUIDs, use libraries such as
2020
[Elixir UUID](https://github.com/zyro/elixir-uuid), [Erlang UUID](https://github.com/okeuday/uuid) and also [Ecto](https://hexdocs.pm/ecto/Ecto.UUID.html) as it can generate version 4 UUIDs.
2121

22-
ShortUUID supports common UUID formats and is case-insensitive. It also supports binary UUIDs returned from DBs like PostgreSQL when the uuid type is used to store the UUID.
22+
ShortUUID supports common UUID formats and is case-insensitive.
2323

2424
## Compatibility
2525

26-
Starting with version `v3.0.0`, this library will follow suit with changes in other language implementations and move the most significant bit of the encoded value to the start. This also means that padding will be applied to the end of the string, not the start
27-
This change will restore compatibility with other libraries like [shortuuid](https://github.com/skorokithakis/shortuuid) from v1.0.0 onwards and [short-uuid
28-
](https://github.com/oculus42/short-uuid).
26+
### v4.0.0 breaking changes
27+
28+
Raw binary UUID input (as `<<...>>`) is no longer supported. UUIDs must be provided as strings in standard UUID format (`"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"`) or as 32-character hex strings without hyphens.
29+
30+
Examples of supported formats:
31+
```elixir
32+
# Supported
33+
"550e8400-e29b-41d4-a716-446655440000" # With hyphens
34+
"550e8400e29b41d4a716446655440000" # Without hyphens
35+
36+
# No longer supported in v4.0.0
37+
<<85, 14, 132, 0, 226, 155, 65, 212, 167, 22, 68, 102, 85, 68, 0, 0>>
38+
```
39+
40+
### v3.0.0 breaking changes
41+
42+
Changed bit order and padding behavior to align with other language implementations:
43+
- Most significant bits are now encoded first
44+
- Padding characters appear at the end of the string
45+
- Compatible with Python's [shortuuid](https://github.com/skorokithakis/shortuuid) v1.0.0+ and Node.js [short-uuid](https://github.com/oculus42/short-uuid)
2946

3047
Before `v3.0.0`
3148
```elixir
@@ -68,7 +85,7 @@ Add `:shortuuid` to your list of dependencies in `mix.exs`:
6885
```elixir
6986
def deps do
7087
[
71-
{:shortuuid, "~> 3.0"}
88+
{:shortuuid, "~> 4.0"}
7289
]
7390
end
7491
```
@@ -95,6 +112,69 @@ If you would like to use ShortUUID with Ecto schemas try [Ecto.ShortUUID](https:
95112

96113
It provides a custom Ecto type which allows for ShortUUID primary and foreign keys while staying compatible with `:binary_key` (`EctoUUID`).
97114

115+
## Custom Alphabets
116+
117+
Starting with version `v4.0.0`, ShortUUID allows you to define custom alphabets for encoding and decoding UUIDs. You can use predefined alphabets or define your own.
118+
119+
### Restrictions
120+
121+
- The alphabet must contain at least 16 unique characters.
122+
- The alphabet must not contain duplicate characters.
123+
124+
### Predefined Alphabets
125+
126+
Starting with version `v4.0.0`, the following predefined alphabets are available:
127+
128+
- `:base57_shortuuid` - "23456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
129+
- `:base32` - "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"
130+
- `:base32_crockford` - "0123456789ABCDEFGHJKMNPQRSTVWXYZ"
131+
- `:base32_hex` - "0123456789ABCDEFGHIJKLMNOPQRSTUV"
132+
- `:base32_z` - "ybndrfg8ejkmcpqxot1uwisza345h769"
133+
- `:base58` - "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
134+
- `:base62` - "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
135+
- `:base64` - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
136+
- `:base64_url` - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"
137+
138+
### Using a custom or predefined alphabet
139+
140+
```elixir
141+
defmodule MyBase58UUID do
142+
use ShortUUID.Builder, alphabet: :base58
143+
end
144+
145+
defmodule MyCustomUUID do
146+
use ShortUUID.Builder, alphabet: "0123456789ABCDEF"
147+
end
148+
149+
iex> MyBase58UUID.encode("550e8400-e29b-41d4-a716-446655440000")
150+
{:ok, "BWBeN28Vb7cMEx7Ym8AUzs"}
151+
152+
iex> MyBase58UUID.decode("BWBeN28Vb7cMEx7Ym8AUzs")
153+
{:ok, "550e8400-e29b-41d4-a716-446655440000"}
154+
```
155+
156+
### Just for fun
157+
158+
Since v4.0.0 alphabets are not limited to alphanumeric characters either
159+
160+
```elixir
161+
defmodule UnicodeUUID do
162+
use ShortUUID.Builder, alphabet: "🌟💫✨⭐️🌙🌎🌍🌏🌑🌒🌓🌔🌕🌖🌗🌘"
163+
end
164+
165+
iex> UnicodeUUID.encode("550e8400-e29b-41d4-a716-446655440000")
166+
{:ok, "🌎🌎🌟🌗🌑🌙🌟🌟🌗✨🌒🌔🌙💫🌖🌙🌓🌏💫🌍🌙🌙🌍🌍🌎🌎🌙🌙🌟🌟🌟🌟"}
167+
168+
169+
defmodule SmileyUUID do
170+
use ShortUUID.Builder, alphabet: "😀😊😄😍🥰😘😜🤪😋🤔😌🧐😐😑😶😮😲😱😴🥱😪😢😭😤😎🤓😇😈👻👽🤖🤡💀"
171+
end
172+
173+
iex> SmileyUUID.encode("550e8400-e29b-41d4-a716-446655440000")
174+
{:ok, "😊🤪😢😘💀🥰😲😊🤡🤖🤔😊😘😤👽🤓👻😊👽😲😋😀😭😇😲🤖"}
175+
176+
```
177+
98178
## Documentation
99179

100180
Look up the full documentation at [https://hexdocs.pm/shortuuid](https://hexdocs.pm/shortuuid).
@@ -105,9 +185,9 @@ Inspired by [shortuuid](https://github.com/skorokithakis/shortuuid).
105185

106186
## Copyright and License
107187

108-
Copyright (c) 2019 Goran Pedić
188+
Copyright (c) 2024 Goran Pedić
109189

110190
This work is free. You can redistribute it and/or modify it under the
111191
terms of the MIT License.
112192

113-
See the [LICENSE.md](./LICENSE.md) file for more details.
193+
See the [LICENSE.md](./LICENSE.md) file for more details.

bench/encode.exs

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,4 @@
11
Benchee.run(%{
2-
"encode/1 binary uuid" => fn ->
3-
ShortUUID.encode(<<1, 96, 40, 15, 29, 112, 21, 104, 176, 151, 123, 220, 162, 128, 29, 227>>)
4-
end,
5-
62
"encode/1 hyphenated uuid string" => fn ->
73
ShortUUID.encode("2a162ee5-02f4-4701-9e87-72762cbce5e2")
84
end,

0 commit comments

Comments
 (0)