Skip to content
This repository has been archived by the owner on Dec 12, 2024. It is now read-only.

Commit

Permalink
Add key sort (#140)
Browse files Browse the repository at this point in the history
* Add key sort

* add new error type
  • Loading branch information
nitro-neal authored Oct 15, 2024
1 parent 9d50a69 commit 2e56ab6
Show file tree
Hide file tree
Showing 5 changed files with 66 additions and 5 deletions.
3 changes: 3 additions & 0 deletions crates/tbdex/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ pub enum TbdexError {
Web5Error(#[from] Web5Error),
#[error(transparent)]
ErrorResponseBody(#[from] ErrorResponseBody),

#[error("{0}")]
Generic(String),
}

impl From<SerdeJsonError> for TbdexError {
Expand Down
19 changes: 17 additions & 2 deletions crates/tbdex/src/messages/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,10 @@ use quote::Quote;
use rfq::Rfq;
use serde::{de::Visitor, Deserialize, Deserializer, Serialize};
use std::{fmt, str::FromStr, sync::Arc};
use std::sync::atomic::{AtomicU64, Ordering};
use chrono::Utc;
use type_safe_id::{DynamicType, TypeSafeId};
use uuid::Uuid;
use uuid::{NoContext, Timestamp, Uuid};

#[derive(Debug, Default, Deserialize, PartialEq, Serialize, Clone)]
#[serde(rename_all = "lowercase")]
Expand Down Expand Up @@ -66,10 +68,23 @@ impl fmt::Display for MessageKind {
}
}

static COUNTER: AtomicU64 = AtomicU64::new(0);

impl MessageKind {
pub fn typesafe_id(&self) -> Result<String> {
let dynamic_type = DynamicType::new(&self.to_string())?;
Ok(TypeSafeId::from_type_and_uuid(dynamic_type, Uuid::new_v4()).to_string())
let timestamp_nanos = Utc::now()
.timestamp_nanos_opt()
.ok_or_else(|| TbdexError::Generic("Failed to get timestamp nanos".to_string()))?;

let seconds = (timestamp_nanos / 1_000_000_000) as u64;
let subsec_nanos = (timestamp_nanos % 1_000_000_000) as u32;

let count = COUNTER.fetch_add(1, Ordering::SeqCst);
let unique_seconds = seconds.wrapping_add(count);

let timestamp = Timestamp::from_unix(NoContext, unique_seconds, subsec_nanos);
Ok(TypeSafeId::from_type_and_uuid(dynamic_type, Uuid::new_v7(timestamp)).to_string())
}
}

Expand Down
14 changes: 14 additions & 0 deletions crates/tbdex/src/messages/rfq.rs
Original file line number Diff line number Diff line change
Expand Up @@ -633,6 +633,20 @@ mod tests {

assert_eq!(rfq, parsed_rfq);
}

#[test]
fn test_typesafe_id_sorting() {
let resource_kind = MessageKind::Rfq;
let ids: Vec<String> = (0..1000).map(|_| resource_kind.typesafe_id().unwrap()).collect();

let mut sorted_ids = ids.clone();
sorted_ids.sort();

assert_eq!(ids, sorted_ids, "IDs should be generated in sortable order");

let unique_ids: std::collections::HashSet<_> = ids.into_iter().collect();
assert_eq!(unique_ids.len(), 1000, "All generated IDs should be unique");
}
}

#[cfg(test)]
Expand Down
20 changes: 17 additions & 3 deletions crates/tbdex/src/resources/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@ pub mod balance;
pub mod offering;

use std::{fmt, str::FromStr};

use std::sync::atomic::{AtomicU64, Ordering};
use chrono::Utc;
use crate::errors::{Result, TbdexError};
use serde::{Deserialize, Serialize};
use type_safe_id::{DynamicType, TypeSafeId};
use uuid::Uuid;
use uuid::{NoContext, Timestamp, Uuid};

#[derive(Debug, Deserialize, PartialEq, Serialize, Clone)]
#[serde(rename_all = "lowercase")]
Expand Down Expand Up @@ -36,10 +37,23 @@ impl fmt::Display for ResourceKind {
}
}

static COUNTER: AtomicU64 = AtomicU64::new(0);

impl ResourceKind {
pub fn typesafe_id(&self) -> Result<String> {
let dynamic_type = DynamicType::new(&self.to_string())?;
Ok(TypeSafeId::from_type_and_uuid(dynamic_type, Uuid::new_v4()).to_string())
let timestamp_nanos = Utc::now()
.timestamp_nanos_opt()
.ok_or_else(|| TbdexError::Generic("Failed to get timestamp nanos".to_string()))?;

let seconds = (timestamp_nanos / 1_000_000_000) as u64;
let subsec_nanos = (timestamp_nanos % 1_000_000_000) as u32;

let count = COUNTER.fetch_add(1, Ordering::SeqCst);
let unique_seconds = seconds.wrapping_add(count);

let timestamp = Timestamp::from_unix(NoContext, unique_seconds, subsec_nanos);
Ok(TypeSafeId::from_type_and_uuid(dynamic_type, Uuid::new_v7(timestamp)).to_string())
}
}

Expand Down
15 changes: 15 additions & 0 deletions crates/tbdex/src/resources/offering.rs
Original file line number Diff line number Diff line change
Expand Up @@ -326,6 +326,21 @@ mod tests {

assert_eq!(offering, parsed_offering);
}

#[test]
fn test_typesafe_id_sorting() {
let resource_kind = ResourceKind::Offering;

let ids: Vec<String> = (0..1000).map(|_| resource_kind.typesafe_id().unwrap()).collect();

let mut sorted_ids = ids.clone();
sorted_ids.sort();

assert_eq!(ids, sorted_ids, "IDs should be generated in sortable order");

let unique_ids: std::collections::HashSet<_> = ids.into_iter().collect();
assert_eq!(unique_ids.len(), 1000, "All generated IDs should be unique");
}
}

#[cfg(test)]
Expand Down

0 comments on commit 2e56ab6

Please sign in to comment.