use crate::errors::PriceLevelError;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use std::fmt;
use std::str::FromStr;
use ulid::Ulid;
use uuid::Uuid;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Id {
Uuid(Uuid),
Ulid(Ulid),
Sequential(u64),
}
impl FromStr for Id {
type Err = PriceLevelError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if let Ok(id) = s.parse::<u64>() {
return Ok(Self::Sequential(id));
}
if let Ok(uuid) = Uuid::from_str(s) {
Ok(Self::Uuid(uuid))
} else if let Ok(ulid) = Ulid::from_string(s) {
Ok(Self::Ulid(ulid))
} else {
Err(PriceLevelError::ParseError {
message: format!("Failed to parse Id as u64, UUID, or ULID: {s}"),
})
}
}
}
impl fmt::Display for Id {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Uuid(uuid) => write!(f, "{uuid}"),
Self::Ulid(ulid) => write!(f, "{ulid}"),
Self::Sequential(id) => write!(f, "{id}"),
}
}
}
impl Serialize for Id {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(&self.to_string())
}
}
impl<'de> Deserialize<'de> for Id {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
Self::from_str(&s).map_err(serde::de::Error::custom)
}
}
impl Default for Id {
fn default() -> Self {
Self::new()
}
}
impl Id {
#[must_use]
pub fn new() -> Self {
Self::Ulid(Ulid::new())
}
#[must_use]
pub fn new_uuid() -> Self {
Self::Uuid(Uuid::new_v4())
}
#[must_use]
pub fn new_ulid() -> Self {
Self::Ulid(Ulid::new())
}
#[must_use]
pub fn nil() -> Self {
Self::Uuid(Uuid::nil())
}
#[must_use]
pub fn from_uuid(uuid: Uuid) -> Self {
Self::Uuid(uuid)
}
#[must_use]
pub fn from_ulid(ulid: Ulid) -> Self {
Self::Ulid(ulid)
}
#[must_use]
pub fn as_bytes(&self) -> [u8; 16] {
match self {
Self::Uuid(uuid) => *uuid.as_bytes(),
Self::Ulid(ulid) => ulid.to_bytes(),
Self::Sequential(id) => {
let mut bytes = [0_u8; 16];
bytes[8..16].copy_from_slice(&id.to_be_bytes());
bytes
}
}
}
#[must_use]
pub fn sequential(id: u64) -> Self {
Self::Sequential(id)
}
#[must_use]
pub fn from_u64(id: u64) -> Self {
let bytes = [
((id >> 56) & 0xFF) as u8,
((id >> 48) & 0xFF) as u8,
((id >> 40) & 0xFF) as u8,
((id >> 32) & 0xFF) as u8,
((id >> 24) & 0xFF) as u8,
((id >> 16) & 0xFF) as u8,
((id >> 8) & 0xFF) as u8,
(id & 0xFF) as u8,
0,
0,
0,
0,
0,
0,
0,
0,
];
Self::Uuid(Uuid::from_bytes(bytes))
}
#[must_use]
pub fn as_u64(&self) -> Option<u64> {
match self {
Self::Sequential(id) => Some(*id),
_ => None,
}
}
#[must_use]
pub fn is_sequential(&self) -> bool {
matches!(self, Self::Sequential(_))
}
#[must_use]
pub fn is_uuid(&self) -> bool {
matches!(self, Self::Uuid(_))
}
#[must_use]
pub fn is_ulid(&self) -> bool {
matches!(self, Self::Ulid(_))
}
}
#[cfg(test)]
mod tests {
use super::Id;
use crate::Side;
use std::str::FromStr;
use uuid::Uuid;
#[test]
fn test_id_creation() {
let id = Id::from_u64(12345);
assert_eq!(id, Id::from_u64(12345));
let id1 = Id::new();
let id2 = Id::new();
assert_ne!(id1, id2);
let uuid = Uuid::new_v4();
let id = Id::from_uuid(uuid);
assert_eq!(id, Id::Uuid(uuid));
let nil_id = Id::nil();
assert_eq!(nil_id, Id::Uuid(Uuid::nil()));
}
#[test]
fn test_id_serialize_deserialize() {
let id = Id::from_u64(12345);
let serialized = serde_json::to_string(&id).unwrap();
let expected_uuid = id.to_string();
assert!(serialized.contains(&expected_uuid));
let deserialized: Id = serde_json::from_str(&serialized).unwrap();
assert_eq!(deserialized, id);
}
#[test]
fn test_from_str_valid() {
let uuid_str = "550e8400-e29b-41d4-a716-446655440000";
let id = Id::from_str(uuid_str).unwrap();
assert_eq!(id.to_string(), uuid_str);
let id_from_u64 = Id::from_u64(12345);
let parsed = Id::from_str(&id_from_u64.to_string()).unwrap();
assert_eq!(id_from_u64, parsed);
}
#[test]
fn test_from_str_invalid() {
assert!(Id::from_str("").is_err());
assert!(Id::from_str("not-a-uuid").is_err());
}
#[test]
fn test_side_opposite() {
assert_eq!(Side::Buy.opposite(), Side::Sell);
assert_eq!(Side::Sell.opposite(), Side::Buy);
}
#[test]
fn test_sequential_helpers() {
let id = Id::sequential(42);
assert!(id.is_sequential());
assert_eq!(id.as_u64(), Some(42));
assert_eq!(id.to_string(), "42");
let parsed: Id = "42".parse().unwrap();
assert_eq!(parsed, id);
}
}