ulid クレートを使って ULID を生成してみたいと思います。
ULID を生成する
- ulid クレートを使います
ulid::new()
で生成to_string()
で変換
ULID を生成する
ULID とは
ULID は Universally Unique Lexicographically Sortable Identifier のことで、
簡単に説明すると ソートできる UUID です。
UUID の上位互換である ULID を使う機会が多くなってきた気がします。
あくまで自分自身が観測できる範囲の話ではありますが…笑
ulid クレート
crates.io で ulid と検索すると、一番にヒットするクレート ulid を使います。
[dependencies]
ulid = "1.0.0"
ulid::new() で生成できる
ざっと、簡単にまとめると以下のようになります。
new
で構造体 Ulid を生成to_string
で String に変換from_string
で構造体 Ulid に戻すことも可能
use ulid::Ulid;
fn main() {
// Ulid を生成
let ulid = Ulid::new();
println!("{:?}", ulid);
// String に変換
let id = ulid.to_string();
println!("{:?}", id);
// Ulid に戻す
let result = Ulid::from_string(&id);
println!("{:?}", result);
assert_eq!(Ok(ulid), result);
}
Id オブジェクトとして実装してみる
Axum と SQLx を使って Todo アプリ を作成しているので、そこからコードを拝借します。
各モデルが共通で id としてId<T>
を持つように設計しています。
Todo
モデルや新規作成時に使用するNewTodo
は以下のようになっています。
pub struct Todo {
pub id: Id<Todo>,
pub title: String,
pub description: String,
pub status: TodoStatus,
pub created_at: DateTime<Utc>,
pub updated_at: DateTime<Utc>,
}
pub struct NewTodo {
pub id: Id<Todo>,
pub title: String,
pub description: String,
}
impl NewTodo {
pub fn new(id: Id<Todo>, title: String, description: String) -> Self {
Self {
id,
title,
description,
}
}
}
Id<T>
の実装は以下です。
use anyhow::anyhow;
use std::marker::PhantomData;
use ulid::Ulid;
pub mod todo;
#[derive(Debug)]
pub struct Id<T> {
pub value: Ulid,
_marker: PhantomData<T>,
}
impl<T> Id<T> {
pub fn new(value: Ulid) -> Self {
Self {
value,
_marker: PhantomData,
}
}
pub fn gen() -> Id<T> {
Id::new(Ulid::new())
}
}
impl<T> TryFrom<String> for Id<T> {
type Error = anyhow::Error;
fn try_from(value: String) -> Result<Self, Self::Error> {
Ulid::from_string(&value)
.map(|id| Self::new(id))
.map_err(|err| anyhow!("{:?}", err))
}
}
ユースケース層に定義した Todo 作成時に使用するCreateTodo
からNewTodo
へ値の詰め替えをおこなう際、Id::gen()
を呼び出して ULID を生成しています。
pub struct CreateTodo {
pub title: String,
pub description: String,
}
impl CreateTodo {
pub fn new(title: String, description: String) -> Self {
Self { title, description }
}
}
impl TryFrom<CreateTodo> for NewTodo {
type Error = anyhow::Error;
fn try_from(ct: CreateTodo) -> Result<Self, Self::Error> {
Ok(NewTodo::new(Id::gen(), ct.title, ct.description))
}
}
また、パスパラメータで渡ってきた String 型の Todo ID をId<Todo>に変換する際など、ULIDの形式か検証をおこなう必要があるので、TryFrom
を実装しています。
pub async fn get_todo(&self, id: String) -> anyhow::Result<Option<TodoView>> {
let resp = self
.repositories
.todo_repository()
.get(&id.try_into()?)
.await?;
match resp {
Some(t) => Ok(Some(t.into())),
None => Ok(None),
}
}
rusty_ulid というクレートもある
ulid クレート以外にも、rusty_ulid というクレートもありました。
new
なのか、generate
なのかの差ぐらいでしょうか。
use std::str::FromStr;
use rusty_ulid::Ulid;
fn main() {
// Ulid を生成
let ulid = Ulid::generate();
// String に変換
let ulid_string = ulid.to_string();
// Ulid に戻す
let result = Ulid::from_str(&ulid_string);
assert_eq!(Ok(ulid), result);
}
2022/9/3 時点のダウンロード数や Github Star の数をまとめました。
All-Time のダウンロード数には、かなり差がありました。
ただ、Recent ではその差が埋まりつつあるので、甲乙つけがたい気がします…
バージョン | ダウンロード数 | Github Star | |
ulid | 1.0.0 | All-Time: 164,694 Recent: 60,813 | 143 |
rusty_ulid | 1.0.0 | All-Time: 549,778 Recent: 76,675 | 26 |
まとめ
Rust で ULID を扱う際に便利なクレート ulid のご紹介でした!
コメント