use uuids with original schema

This commit is contained in:
Joe Ardent 2023-07-13 14:41:29 -07:00
parent a71420fc7b
commit 85473b4938
3 changed files with 31 additions and 181 deletions

22
Cargo.lock generated
View file

@ -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]]

View file

@ -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"

View file

@ -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<Self, ulid::DecodeError> {
let id = Ulid::from_string(s)?;
Ok(id.into())
pub fn from_string(s: &str) -> Result<Self, Error> {
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<Utc> {
self.0.datetime().into()
chrono::DateTime::default()
}
}
@ -62,153 +48,14 @@ impl Debug for DbId {
}
}
impl From<Ulid> for DbId {
fn from(value: Ulid) -> Self {
impl From<Uuid> for DbId {
fn from(value: Uuid) -> Self {
DbId(value)
}
}
impl From<u128> for DbId {
fn from(value: u128) -> Self {
DbId(value.into())
}
}
//-************************************************************************
// sqlx traits for going in and out of the db
//-************************************************************************
impl sqlx::Type<sqlx::Sqlite> for DbId {
fn type_info() -> <sqlx::Sqlite as sqlx::Database>::TypeInfo {
<&[u8] as sqlx::Type<sqlx::Sqlite>>::type_info()
}
}
impl<'q> Encode<'q, Sqlite> for DbId {
fn encode_by_ref(&self, args: &mut Vec<SqliteArgumentValue<'q>>) -> IsNull {
args.push(SqliteArgumentValue::Blob(Cow::Owned(self.bytes().to_vec())));
IsNull::No
}
}
impl Decode<'_, Sqlite> for DbId {
fn decode(value: SqliteValueRef<'_>) -> Result<Self, sqlx::error::BoxDynError> {
let bytes = <&[u8] as Decode<Sqlite>>::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<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
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<E>(self, v: &[u8]) -> Result<Self::Value, E>
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<E>(self, v: String) -> Result<Self::Value, E>
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<E>(self, v: &str) -> Result<Self::Value, E>
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<E>(self, v: Vec<u8>) -> Result<Self::Value, E>
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<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
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<D>(deserializer: D) -> Result<Self, D::Error>
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))
}
}