plugmem_core/id.rs
1//! Typed identifiers.
2//!
3//! All ids are `u32`, allocated monotonically, never reused (journal replay
4//! stays deterministic; holes after a purge are normal). `0` is a valid id;
5//! "no value" is encoded as [`u32::MAX`] — every newtype exposes it as its
6//! `NONE` constant. The 4.29-billion ceiling per kind is far above the 1M
7//! capacity passport.
8
9/// The raw "no value" sentinel shared by every id kind.
10pub const NONE_U32: u32 = u32::MAX;
11
12/// Declares one id newtype with the shared sentinel plumbing.
13macro_rules! id_type {
14 ($(#[$doc:meta])* $name:ident) => {
15 $(#[$doc])*
16 #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
17 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
18 pub struct $name(pub u32);
19
20 impl $name {
21 /// The "no value" sentinel ([`u32::MAX`]).
22 pub const NONE: Self = Self(NONE_U32);
23
24 /// `true` when this is the [`Self::NONE`] sentinel.
25 pub fn is_none(self) -> bool {
26 self.0 == NONE_U32
27 }
28
29 /// `Some(self)` for a real id, `None` for the sentinel.
30 pub fn some(self) -> Option<Self> {
31 if self.is_none() { None } else { Some(self) }
32 }
33
34 /// Collapses an optional id back into the sentinel encoding.
35 pub fn from_opt(opt: Option<Self>) -> Self {
36 opt.unwrap_or(Self::NONE)
37 }
38 }
39 };
40}
41
42id_type! {
43 /// Identifier of a fact — the unit of memory.
44 FactId
45}
46
47id_type! {
48 /// Identifier of an entity — a graph node created lazily on first
49 /// mention.
50 EntityId
51}