fission_ir/widget_id.rs
1//! Stable identity for widgets and lowered IR nodes.
2//!
3//! A [`WidgetId`] is the single identity type used across authoring widgets,
4//! lowered IR nodes, layout, rendering, hit testing, and runtime state. The old
5//! split between widget identity and node identity is intentionally gone: a
6//! widget may lower to one or more IR nodes, and those nodes use derived
7//! `WidgetId` values when they need child identities.
8
9use serde::{Deserialize, Serialize};
10use std::fmt;
11
12/// A stable 128-bit identity for widgets and lowered IR nodes.
13///
14/// `WidgetId` values are derived from BLAKE3 hashes. Two construction strategies
15/// are available:
16///
17/// * [`WidgetId::explicit`] hashes a user-provided stable key.
18/// * [`WidgetId::derived`] hashes a parent identity plus a child-index path.
19///
20/// # Example
21///
22/// ```rust
23/// use fission_ir::WidgetId;
24///
25/// let sidebar = WidgetId::explicit("sidebar");
26/// let first_item = WidgetId::derived(sidebar.as_u128(), &[0]);
27/// assert_ne!(sidebar, first_item);
28/// ```
29#[derive(Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, PartialOrd, Ord)]
30pub struct WidgetId(u128);
31
32impl WidgetId {
33 /// Creates a `WidgetId` from a raw 128-bit value.
34 ///
35 /// This is intended for internal use or deserialization. In normal code use
36 /// [`WidgetId::explicit`] or [`WidgetId::derived`] instead.
37 pub const fn from_u128(val: u128) -> Self {
38 Self(val)
39 }
40
41 /// Returns the underlying 128-bit value.
42 pub fn as_u128(&self) -> u128 {
43 self.0
44 }
45
46 /// Creates a `WidgetId` from a user-provided stable key.
47 ///
48 /// The key is hashed with BLAKE3 using the same explicit-identity domain as
49 /// the original IR identity system. Keep the key stable across rebuilds when
50 /// you want runtime state, focus, scroll, animation, or host-surface state to
51 /// follow a widget through tree changes.
52 pub fn explicit(key: &str) -> Self {
53 let mut hasher = blake3::Hasher::new();
54 hasher.update(b"explicit:");
55 hasher.update(key.as_bytes());
56 let hash = hasher.finalize();
57 Self(u128::from_le_bytes(
58 hash.as_bytes()[0..16].try_into().unwrap(),
59 ))
60 }
61
62 /// Creates a `WidgetId` derived from a parent identity and child-index path.
63 ///
64 /// This provides structural identity for children that do not have explicit
65 /// keys. Dynamic/reorderable lists should provide explicit IDs for list items;
66 /// purely structural children can use derived IDs.
67 pub fn derived(parent: u128, path: &[u32]) -> Self {
68 let mut hasher = blake3::Hasher::new();
69 hasher.update(b"derived:");
70 hasher.update(&parent.to_le_bytes());
71 for index in path {
72 hasher.update(&index.to_le_bytes());
73 }
74 let hash = hasher.finalize();
75 Self(u128::from_le_bytes(
76 hash.as_bytes()[0..16].try_into().unwrap(),
77 ))
78 }
79}
80
81impl fmt::Debug for WidgetId {
82 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
83 write!(f, "WidgetId({:032x})", self.0)
84 }
85}
86
87impl fmt::Display for WidgetId {
88 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
89 write!(f, "{:032x}", self.0)
90 }
91}