use serde::{Deserialize, Serialize};
#[cfg(feature = "typescript")]
use ts_rs::TS;
use uuid::Uuid;
#[derive(Serialize, Deserialize, Clone, Debug)]
#[cfg_attr(feature = "typescript", derive(TS))]
#[cfg_attr(feature = "typescript", ts(export))]
pub struct MutualPeer {
#[cfg_attr(feature = "typescript", ts(type = "bigint"))]
pub parent_icid: u64,
#[cfg_attr(feature = "typescript", ts(type = "bigint"))]
pub cid: u64,
pub username: Option<String>,
}
#[derive(Serialize, Deserialize, Clone, Debug)]
#[cfg_attr(feature = "typescript", derive(TS))]
#[cfg_attr(feature = "typescript", ts(export))]
pub struct PeerInfo {
#[cfg_attr(feature = "typescript", ts(type = "bigint"))]
pub cid: u64,
pub username: String,
pub full_name: String,
}
impl PartialEq for MutualPeer {
fn eq(&self, other: &Self) -> bool {
self.parent_icid == other.parent_icid
&& self.cid == other.cid
&& self.username.as_ref() == other.username.as_ref()
}
}
pub fn username_to_cid(username: &str) -> u64 {
use sha3::{Digest, Sha3_256};
let digest = Sha3_256::digest(username.as_bytes());
u64::from_be_bytes(
digest[..8]
.try_into()
.expect("SHA3-256 digest is always 32 bytes"),
)
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "typescript", derive(TS))]
#[cfg_attr(feature = "typescript", ts(export))]
pub enum UserIdentifier {
ID(#[cfg_attr(feature = "typescript", ts(type = "bigint"))] u64),
Username(String),
}
impl From<String> for UserIdentifier {
fn from(username: String) -> Self {
Self::Username(username)
}
}
impl From<&str> for UserIdentifier {
fn from(username: &str) -> Self {
Self::Username(username.to_string())
}
}
impl From<u64> for UserIdentifier {
fn from(cid: u64) -> Self {
Self::ID(cid)
}
}
impl From<Uuid> for UserIdentifier {
fn from(uuid: Uuid) -> Self {
Self::Username(uuid.to_string())
}
}
#[cfg(test)]
mod cid_tests {
use super::username_to_cid;
#[test]
fn deterministic_and_distinct() {
assert_eq!(username_to_cid("alice"), username_to_cid("alice"));
assert_ne!(username_to_cid("alice"), username_to_cid("bob"));
assert_ne!(username_to_cid("alice"), username_to_cid("alicf"));
assert_ne!(username_to_cid("user"), username_to_cid("user "));
}
#[test]
fn nonzero_for_typical_usernames() {
for name in ["a", "alice", "bob", "server-admin", "user@example.com"] {
assert_ne!(
username_to_cid(name),
0,
"{name:?} hashed to reserved CID 0"
);
}
}
}