Skip to content

Commit

Permalink
Add Tid::now() constructor
Browse files Browse the repository at this point in the history
  • Loading branch information
DrChat committed Feb 1, 2025
1 parent a28705d commit 74e4573
Showing 1 changed file with 36 additions and 1 deletion.
37 changes: 36 additions & 1 deletion atrium-api/src/types/string.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,22 @@ use regex::Regex;
use serde::{de::Error, Deserialize, Deserializer, Serialize, Serializer};
use std::{cmp, ops::Deref, str::FromStr, sync::OnceLock};

// Reference: https://github.com/bluesky-social/indigo/blob/9e3b84fdbb20ca4ac397a549e1c176b308f7a6e1/repo/tid.go#L11-L19
fn s32_encode(mut i: u64) -> String {
const S32_CHAR: &[u8] = b"234567abcdefghijklmnopqrstuvwxyz";

let mut s = String::new();
for _ in 0..13 {
let c = i & 0x1F;
s.push(S32_CHAR[c as usize] as char);

i >>= 5;
}

// Reverse the string to convert it to big-endian format.
s.as_str().chars().rev().collect()
}

/// Common trait implementations for Lexicon string formats that are newtype wrappers
/// around `String`.
macro_rules! string_newtype {
Expand Down Expand Up @@ -410,7 +426,7 @@ impl Serialize for Language {

/// A [Timestamp Identifier].
///
/// [Timestamp Identifier]: https://atproto.com/specs/record-key#record-key-type-tid
/// [Timestamp Identifier]: https://atproto.com/specs/tid
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Hash)]
#[serde(transparent)]
pub struct Tid(String);
Expand All @@ -436,6 +452,19 @@ impl Tid {
}
}

/// Construct a new timestamp with the specified clock ID.
///
/// Clock IDs 0-31 can be used as an ad-hoc clock ID if you are not concerned
/// with this parameter.
pub fn now(cid: u32) -> Self {
let now = chrono::Utc::now().timestamp_micros() as u64;

// The TID is laid out as follows:
// 0TTTTTTTTTTTTTTT TTTTTTTTTTTTTTTT TTTTTTTTTTTTTTTT TTTTTTCCCCCCCCCC
let tid = (now << 10) & 0x7FFF_FFFF_FFFF_FC00 | (cid as u64) & 0x3FF;
Self(s32_encode(tid))
}

/// Returns the TID as a string slice.
pub fn as_str(&self) -> &str {
self.0.as_str()
Expand Down Expand Up @@ -766,6 +795,12 @@ mod tests {
}
}

#[test]
fn tid_encode() {
assert_eq!(s32_encode(0), "2222222222222");
assert_eq!(s32_encode(1), "2222222222223");
}

#[test]
fn valid_tid() {
for valid in ["3jzfcijpj2z2a", "7777777777777", "3zzzzzzzzzzzz"] {
Expand Down

0 comments on commit 74e4573

Please sign in to comment.