diff --git a/Cargo.lock b/Cargo.lock index 6acc775..821b59c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1868,6 +1868,7 @@ dependencies = [ "time", "tokio-stream", "url", + "uuid", "webpki-roots", ] @@ -2228,15 +2229,6 @@ version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" -[[package]] -name = "ulid" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13a3aaa69b04e5b66cc27309710a569ea23593612387d67daaf102e73aa974fd" -dependencies = [ - "rand", -] - [[package]] name = "unicase" version = "2.6.0" @@ -2308,6 +2300,16 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" +[[package]] +name = "uuid" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d023da39d1fde5a8a3fe1f3e01ca9632ada0a63e9797de55a879d6e2236277be" +dependencies = [ + "getrandom", + "serde", +] + [[package]] name = "valuable" version = "0.1.0" @@ -2454,8 +2456,8 @@ dependencies = [ "tower-http 0.4.1", "tracing", "tracing-subscriber", - "ulid", "unicode-segmentation", + "uuid", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 5fcd935..d2654a4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,7 +22,8 @@ justerror = "1" password-hash = { version = "0.5", features = ["std", "getrandom"] } rand = "0.8" serde = { version = "1", features = ["derive"] } -sqlx = { version = "0.6", default-features = false, features = ["runtime-tokio-rustls", "any", "sqlite", "chrono", "time"] } +sqlx = { version = "0.6", default-features = false, features = ["runtime-tokio-rustls", "any", +"sqlite", "chrono", "time", "uuid"] } thiserror = "1" tokio = { version = "1", features = ["full", "tracing"], default-features = false } tokio-retry = "0.3.0" @@ -31,9 +32,9 @@ tower = { version = "0.4", features = ["util", "timeout"], default-features = fa tower-http = { version = "0.4", features = ["add-extension", "trace"] } tracing = "0.1" tracing-subscriber = { version = "0.3", features = ["env-filter"] } -ulid = { version = "1", features = ["rand"] } unicode-segmentation = "1" rand_distr = "0.4.3" +uuid = { version = "1.4.0", features = ["serde", "v4"] } [dev-dependencies] axum-test = "9.0.0" diff --git a/src/db_id.rs b/src/db_id.rs index 24ba42d..3a27eef 100644 --- a/src/db_id.rs +++ b/src/db_id.rs @@ -1,48 +1,34 @@ -use std::{ - borrow::Cow, - fmt::{Debug, Display}, -}; +use std::fmt::{Debug, Display}; use chrono::Utc; -use serde::{de::Visitor, Deserialize, Serialize}; -use sqlx::{ - encode::IsNull, - sqlite::{SqliteArgumentValue, SqliteValueRef}, - Decode, Encode, Sqlite, -}; -use ulid::Ulid; +use serde::{Deserialize, Serialize}; +use uuid::{Error, Uuid}; -#[derive(Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct DbId(pub Ulid); +#[derive( + Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, sqlx::Type, Serialize, Deserialize, +)] +#[sqlx(transparent)] +pub struct DbId(pub Uuid); impl DbId { - pub fn bytes(&self) -> [u8; 16] { - self.to_be_bytes() - } - - pub fn to_be_bytes(self) -> [u8; 16] { - self.0 .0.to_be_bytes() - } - pub fn is_nil(&self) -> bool { self.0.is_nil() } pub fn new() -> Self { - Self(Ulid::new()) + Self(Uuid::new_v4()) } - pub fn from_string(s: &str) -> Result { - let id = Ulid::from_string(s)?; - Ok(id.into()) + pub fn from_string(s: &str) -> Result { + Ok(Self(Uuid::try_parse(s)?)) } pub fn as_string(&self) -> String { - self.0.to_string() + self.0.simple().to_string() } pub fn created_at(&self) -> chrono::DateTime { - self.0.datetime().into() + chrono::DateTime::default() } } @@ -62,153 +48,14 @@ impl Debug for DbId { } } -impl From for DbId { - fn from(value: Ulid) -> Self { +impl From for DbId { + fn from(value: Uuid) -> Self { DbId(value) } } impl From for DbId { fn from(value: u128) -> Self { - DbId(value.into()) - } -} - -//-************************************************************************ -// sqlx traits for going in and out of the db -//-************************************************************************ - -impl sqlx::Type for DbId { - fn type_info() -> ::TypeInfo { - <&[u8] as sqlx::Type>::type_info() - } -} - -impl<'q> Encode<'q, Sqlite> for DbId { - fn encode_by_ref(&self, args: &mut Vec>) -> IsNull { - args.push(SqliteArgumentValue::Blob(Cow::Owned(self.bytes().to_vec()))); - IsNull::No - } -} - -impl Decode<'_, Sqlite> for DbId { - fn decode(value: SqliteValueRef<'_>) -> Result { - let bytes = <&[u8] as Decode>::decode(value)?; - let bytes: [u8; 16] = bytes.try_into().unwrap_or_default(); - Ok(u128::from_be_bytes(bytes).into()) - } -} - -//-************************************************************************ -// serde traits -//-************************************************************************ -impl Serialize for DbId { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - serializer.serialize_bytes(&self.bytes()) - } -} - -struct DbIdVisitor; - -impl<'de> Visitor<'de> for DbIdVisitor { - type Value = DbId; - - fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { - formatter.write_str("16 bytes") - } - - fn visit_bytes(self, v: &[u8]) -> Result - where - E: serde::de::Error, - { - match std::convert::TryInto::<[u8; 16]>::try_into(v) { - Ok(v) => Ok(u128::from_be_bytes(v).into()), - Err(_) => Err(serde::de::Error::invalid_length(v.len(), &self)), - } - } - - fn visit_string(self, v: String) -> Result - where - E: serde::de::Error, - { - DbId::from_string(&v) - .map_err(|_| serde::de::Error::invalid_value(serde::de::Unexpected::Str(&v), &self)) - } - - fn visit_str(self, v: &str) -> Result - where - E: serde::de::Error, - { - DbId::from_string(v) - .map_err(|_| serde::de::Error::invalid_value(serde::de::Unexpected::Str(v), &self)) - } - - fn visit_byte_buf(self, v: Vec) -> Result - where - E: serde::de::Error, - { - let len = v.len(); - match std::convert::TryInto::<[u8; 16]>::try_into(v) { - Ok(v) => Ok(u128::from_be_bytes(v).into()), - Err(_) => Err(serde::de::Error::invalid_length(len, &self)), - } - } - - fn visit_seq(self, mut seq: A) -> Result - where - A: serde::de::SeqAccess<'de>, - { - let mut raw_bytes_from_db = [0u8; 16]; - let size = seq.size_hint().unwrap_or(0); - let mut count = 0; - while let Some(val) = seq.next_element()? { - if count > 15 { - break; - } - raw_bytes_from_db[count] = val; - count += 1; - } - if count != 16 || size > 16 { - let sz = if count < 16 { count } else { size }; - Err(serde::de::Error::invalid_length(sz, &self)) - } else { - Ok(u128::from_be_bytes(raw_bytes_from_db).into()) - } - } -} - -impl<'de> Deserialize<'de> for DbId { - fn deserialize(deserializer: D) -> Result - where - D: serde::Deserializer<'de>, - { - deserializer.deserialize_bytes(DbIdVisitor) - } -} - -//-************************************************************************ -// serialization tests -//-************************************************************************ - -#[cfg(test)] -mod test { - use serde_test::{assert_tokens, Token}; - - use super::*; - - #[test] - fn test_ser_de() { - let bytes: [u8; 16] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]; - let id: DbId = u128::from_be_bytes(bytes).into(); - - assert_tokens( - &id, - &[Token::Bytes(&[ - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, - ])], - ); + DbId(Uuid::from_u128(value)) } }