Skip to main content

quorum_set/tree/
canonical_id.rs

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