返回 CodeWhale
ids.rs
根目录 / crates / tui / src / work_graph / ids.rs
1 //! Deterministic identifiers for the work graph.
2 //!
3 //! Every ID derives from `(session_id, discriminator)` via SHA-256 — there is
4 //! no RNG anywhere in this module tree — so replaying the same change
5 //! sequence (or re-importing the same legacy state) yields byte-identical
6 //! graphs, IDs included. That determinism is what makes import idempotent and
7 //! snapshots comparable across processes.
8 //!
9 //! Format: `<prefix>` + first 12 hex chars of
10 //! `sha256(prefix U+001F session_id U+001F discriminator)`. The unit
11 //! separator keeps `("ab", "c")` and `("a", "bc")` from colliding, and the
12 //! prefix participates in the hash so distinct ID types never share digests
13 //! for the same discriminator.
14
15 use serde::{Deserialize, Serialize};
16
17 use crate::hashing::sha256_hex;
18
19 fn derive_raw(prefix: &str, session_id: &str, discriminator: &str) -> String {
20 let digest = sha256_hex(format!("{prefix}\u{1f}{session_id}\u{1f}{discriminator}"));
21 format!("{prefix}{}", &digest[..12])
22 }
23
24 macro_rules! graph_id {
25 ($(#[$meta:meta])* $name:ident, $prefix:literal) => {
26 $(#[$meta])*
27 #[derive(
28 Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize,
29 )]
30 #[serde(transparent)]
31 pub struct $name(String);
32
33 impl $name {
34 pub const PREFIX: &'static str = $prefix;
35
36 /// Deterministically derive an ID from the owning session and a
37 /// caller-chosen discriminator (e.g. `"plan:3"`, `"objective"`).
38 #[must_use]
39 pub fn derive(session_id: &str, discriminator: &str) -> Self {
40 Self(derive_raw($prefix, session_id, discriminator))
41 }
42
43 #[must_use]
44 pub fn as_str(&self) -> &str {
45 &self.0
46 }
47 }
48
49 impl std::fmt::Display for $name {
50 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
51 f.write_str(&self.0)
52 }
53 }
54 };
55 }
56
57 graph_id!(
58 /// Identity of a node in the work graph (`"wn:" + sha256(...)[..12]`).
59 WorkNodeId,
60 "wn:"
61 );
62 graph_id!(
63 /// Identity of an edge in the work graph.
64 WorkEdgeId,
65 "we:"
66 );
67 graph_id!(
68 /// Identity of an applied change (recorded on its [`ChangeReceipt`](super::ChangeReceipt)).
69 ChangeId,
70 "ch:"
71 );
72 graph_id!(
73 /// Identity of a proposed plan diff awaiting review.
74 ProposalId,
75 "pp:"
76 );
77 graph_id!(
78 /// Identity of an operation binding; used with an owner-reported sequence
79 /// number as the reducer idempotency key.
80 BindingId,
81 "bd:"
82 );
83
83 lines RUST