1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103
#![doc = include_str!("../Readme.md")]
use std::marker::PhantomData;
use rand::prelude::*;
mod common_traits;
mod conversions;
/// An identifier with a statically associated label type
///
/// Use the `Label` generic to describe what this `Id` is for. If you have an existing struct, you can use that:
///
/// ```
/// # use slid::Id;
/// struct User {
/// name: String,
/// id: Id<User>,
/// }
///
/// // It might be convenient to define a type alias:
/// type UserId = Id<User>;
/// ```
///
/// By default, `Id`s are 16 bytes long. This should provide good collision resistance – i.e. you can assume that 2 randomly generated `Id`s are distinct for all practical purposes. You can customize the `Id` size with the `SIZE` const generic:
///
/// ```
/// # use slid::Id;
/// # struct User;
/// type TinyId<Label> = Id<Label, 4>;
///
/// let _id: TinyId<User> = [0, 0, 0, 42].into();
/// ```
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Id<Label, const SIZE: usize = 16> {
#[cfg_attr(feature = "serde", serde(with = "serde_arrays"))]
data: [u8; SIZE],
_phantom: PhantomData<Label>,
}
// IDs are Send and Sync, regardless of whether their type label is.
// The PhantomData messes with us, so we implement these manually.
//
// [Similar PR by someone smarter than me](https://github.com/rust-lang/rust/pull/68348/files)
unsafe impl<Label, const SIZE: usize> Send for Id<Label, SIZE> {}
unsafe impl<Label, const SIZE: usize> Sync for Id<Label, SIZE> {}
impl<Label, const SIZE: usize> Id<Label, SIZE> {
/// Generate a new random [`Id`]
///
/// ```
/// # use slid::Id;
/// struct User;
///
/// let a = Id::<User>::new_random();
/// let b = Id::<User>::new_random();
///
/// // For all practical purposes, we can assume that 2 random IDs will be different
/// // (As long as you use the default ID size or larger)
/// assert_ne!(a, b);
/// ```
pub fn new_random() -> Self {
let mut rng = rand::thread_rng();
let mut data = [0; SIZE];
rng.fill(data.as_mut_slice());
Self {
data,
_phantom: PhantomData,
}
}
/// The bytes representing this [`Id`]
///
/// ```
/// # use slid::Id;
/// struct Product;
///
/// let id: Id<Product, 4> = [1,2,3,4].into();
/// assert_eq!(id.as_bytes(), [1,2,3,4].as_slice());
/// ```
pub fn as_bytes(&self) -> &[u8; SIZE] {
&self.data
}
/// Change the label type of this [`Id`]
///
/// ```
/// # use slid::Id;
/// struct User;
/// struct Player;
///
/// let user_id :Id<User>= Id::new_random();
/// let player_id: Id<Player> = user_id.cast_label();
/// assert_eq!(user_id.as_bytes(), player_id.as_bytes());
/// ```
pub fn cast_label<NewLabel>(self) -> Id<NewLabel, SIZE> {
Id {
data: self.data,
_phantom: PhantomData,
}
}
}