fission_ir/widget_id.rs
1//! Widget-level identity, separate from IR node identity.
2//!
3//! A [`WidgetNodeId`] identifies a *widget* rather than an IR node. Widgets may
4//! compile to multiple IR nodes, but they share a single `WidgetNodeId`. This is
5//! used primarily by [`LayoutOp::Embed`](crate::op::LayoutOp::Embed) to reference
6//! a platform-native surface (video player, web view, etc.) that the framework
7//! does not render itself.
8
9use blake3;
10use serde::{Deserialize, Serialize};
11
12/// A 128-bit identity for a widget.
13///
14/// Like [`NodeId`](crate::NodeId), this is derived from a BLAKE3 hash, but it uses
15/// the `"widget:"` prefix so that widget IDs and node IDs never collide. Widget IDs
16/// are interconvertible with node IDs via `From` impls.
17///
18/// # Example
19///
20/// ```rust
21/// use fission_ir::WidgetNodeId;
22/// let wid = WidgetNodeId::explicit("video-player");
23/// ```
24#[derive(Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, PartialOrd, Ord, Debug)]
25pub struct WidgetNodeId(u128);
26
27impl WidgetNodeId {
28 /// Creates a `WidgetNodeId` from a raw 128-bit value.
29 ///
30 /// Intended for internal use or deserialization.
31 pub const fn from_u128(val: u128) -> Self {
32 Self(val)
33 }
34
35 /// Returns the underlying 128-bit value.
36 pub fn as_u128(&self) -> u128 {
37 self.0
38 }
39
40 /// Creates a `WidgetNodeId` from a user-provided name string.
41 ///
42 /// The name is hashed with BLAKE3 (prefixed with `"widget:"`), producing a
43 /// deterministic ID. Use this to give stable identities to platform-embedded
44 /// widgets.
45 ///
46 /// # Example
47 ///
48 /// ```rust
49 /// use fission_ir::WidgetNodeId;
50 /// let a = WidgetNodeId::explicit("camera-preview");
51 /// let b = WidgetNodeId::explicit("camera-preview");
52 /// assert_eq!(a, b);
53 /// ```
54 pub fn explicit(name: &str) -> Self {
55 let mut hasher = blake3::Hasher::new();
56 hasher.update(b"widget:");
57 hasher.update(name.as_bytes());
58 let hash = hasher.finalize();
59 Self(u128::from_le_bytes(
60 hash.as_bytes()[0..16].try_into().unwrap(),
61 ))
62 }
63}
64
65impl From<crate::node_id::NodeId> for WidgetNodeId {
66 fn from(node: crate::node_id::NodeId) -> Self {
67 Self(node.as_u128())
68 }
69}
70
71impl From<WidgetNodeId> for crate::node_id::NodeId {
72 fn from(id: WidgetNodeId) -> Self {
73 crate::node_id::NodeId::from_u128(id.0)
74 }
75}