quorum_set/tree/canonical_id.rs
1use std::fmt;
2
3pub(crate) const MAX_CANONICAL_ID_LEN: usize = 64;
4
5pub(crate) fn fmt_escaped<W>(s: &str, f: &mut W) -> fmt::Result
6where W: fmt::Write + ?Sized {
7 for b in s.bytes() {
8 if b.is_ascii_alphanumeric() || b == b'_' {
9 write!(f, "{}", char::from(b))?;
10 } else {
11 write!(f, "%{b:02X}")?;
12 }
13 }
14 Ok(())
15}
16
17/// Generates a deterministic canonical ID.
18///
19/// `QuorumTree` uses canonical IDs for equality and ordering. Implementations
20/// for application node IDs should be stable across process restarts and
21/// software versions whenever the logical node identity is unchanged.
22///
23/// Implementations must be consistent with [`Eq`]: two IDs must emit the same
24/// canonical ID if and only if they are equal. If two distinct IDs shared one
25/// canonical ID, two structurally different trees could compare as equal.
26///
27/// User-provided node IDs may emit any string. When a user ID is embedded in a
28/// [`Node`](crate::Node), this crate escapes short IDs and hashes long IDs to
29/// keep tree IDs unambiguous and bounded.
30pub trait CanonicalId {
31 /// Writes this value's canonical ID into `f`.
32 ///
33 /// Implement this method directly when the ID can be written without an
34 /// intermediate allocation.
35 fn fmt_canonical_id<W>(&self, f: &mut W) -> fmt::Result
36 where W: fmt::Write + ?Sized;
37
38 /// Returns this value's canonical ID as a [`String`].
39 fn canonical_id(&self) -> String {
40 let mut s = String::new();
41 self.fmt_canonical_id(&mut s).expect("writing to String should not fail");
42 s
43 }
44}
45
46macro_rules! impl_canonical_id {
47 ($($t:ty),* $(,)?) => {
48 $(impl CanonicalId for $t {
49 fn fmt_canonical_id<W>(&self, f: &mut W) -> fmt::Result
50 where W: fmt::Write + ?Sized {
51 write!(f, "{}", self)
52 }
53 })*
54 };
55}
56
57impl_canonical_id!(
58 u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize, String, &str
59);