re_viewer_context/
blueprint_id.rs1use std::hash::BuildHasher as _;
2
3use re_log_types::{EntityPath, EntityPathPart};
4
5pub trait BlueprintIdRegistry {
6 fn registry_name() -> &'static str;
7 fn registry_path() -> &'static EntityPath;
8}
9
10#[derive(
12 Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, serde::Deserialize, serde::Serialize,
13)]
14pub struct BlueprintId<T: BlueprintIdRegistry> {
15 id: uuid::Uuid,
16 #[serde(skip)]
17 _registry: std::marker::PhantomData<T>,
18}
19
20impl<T: BlueprintIdRegistry> re_byte_size::SizeBytes for BlueprintId<T> {
21 fn heap_size_bytes(&self) -> u64 {
22 0
23 }
24
25 fn is_pod() -> bool {
26 true
27 }
28}
29
30impl<T: BlueprintIdRegistry> BlueprintId<T> {
31 pub fn invalid() -> Self {
32 Self {
33 id: uuid::Uuid::nil(),
34 _registry: std::marker::PhantomData,
35 }
36 }
37
38 pub fn random() -> Self {
39 Self {
40 id: uuid::Uuid::new_v4(),
41 _registry: std::marker::PhantomData,
42 }
43 }
44
45 pub const fn from_bytes(bytes: uuid::Bytes) -> Self {
46 Self {
47 id: uuid::Uuid::from_bytes(bytes),
48 _registry: std::marker::PhantomData,
49 }
50 }
51
52 pub fn from_entity_path(path: &EntityPath) -> Self {
53 if !path.is_child_of(T::registry_path()) {
54 return Self::invalid();
55 }
56
57 path.last()
58 .and_then(|last| uuid::Uuid::parse_str(last.unescaped_str()).ok())
59 .map_or_else(Self::invalid, |id| Self {
60 id,
61 _registry: std::marker::PhantomData,
62 })
63 }
64
65 pub fn hashed_from_str(s: &str) -> Self {
66 use std::hash::{Hash as _, Hasher as _};
67
68 let salt1: u64 = 0x307b_e149_0a3a_5552;
69 let salt2: u64 = 0x6651_522f_f510_13a4;
70
71 let hash1 = {
72 let mut hasher = ahash::RandomState::with_seeds(1, 2, 3, 4).build_hasher();
73 salt1.hash(&mut hasher);
74 s.hash(&mut hasher);
75 hasher.finish()
76 };
77
78 let hash2 = {
79 let mut hasher = ahash::RandomState::with_seeds(1, 2, 3, 4).build_hasher();
80 salt2.hash(&mut hasher);
81 s.hash(&mut hasher);
82 hasher.finish()
83 };
84
85 let uuid = uuid::Uuid::from_u64_pair(hash1, hash2);
86
87 uuid.into()
88 }
89
90 pub fn gpu_readback_id(self) -> re_renderer::GpuReadbackIdentifier {
91 re_log_types::hash::Hash64::hash(self.id).hash64()
92 }
93
94 #[inline]
95 pub fn as_entity_path(&self) -> EntityPath {
96 T::registry_path()
97 .iter()
98 .cloned()
99 .chain(std::iter::once(EntityPathPart::new(self.id.to_string())))
100 .collect()
101 }
102
103 #[inline]
104 pub fn registry() -> &'static EntityPath {
105 T::registry_path()
106 }
107
108 #[inline]
109 pub fn registry_part() -> &'static EntityPathPart {
110 &T::registry_path().as_slice()[0]
111 }
112
113 #[inline]
114 pub fn uuid(&self) -> uuid::Uuid {
115 self.id
116 }
117
118 #[inline]
119 pub fn hash(&self) -> u64 {
120 re_log_types::hash::Hash64::hash(self.id).hash64()
121 }
122}
123
124impl<T: BlueprintIdRegistry> From<uuid::Uuid> for BlueprintId<T> {
125 #[inline]
126 fn from(id: uuid::Uuid) -> Self {
127 Self {
128 id,
129 _registry: std::marker::PhantomData,
130 }
131 }
132}
133
134impl<T: BlueprintIdRegistry> From<re_sdk_types::datatypes::Uuid> for BlueprintId<T> {
135 #[inline]
136 fn from(id: re_sdk_types::datatypes::Uuid) -> Self {
137 Self {
138 id: id.into(),
139 _registry: std::marker::PhantomData,
140 }
141 }
142}
143
144impl<T: BlueprintIdRegistry> From<BlueprintId<T>> for re_sdk_types::datatypes::Uuid {
145 #[inline]
146 fn from(id: BlueprintId<T>) -> Self {
147 id.id.into()
148 }
149}
150
151impl<T: BlueprintIdRegistry> std::fmt::Display for BlueprintId<T> {
152 #[inline]
153 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
154 write!(f, "{}({})", T::registry_name(), self.id.simple())
155 }
156}
157
158impl<T: BlueprintIdRegistry> std::fmt::Debug for BlueprintId<T> {
159 #[inline]
160 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
161 write!(f, "{}({})", T::registry_name(), self.id.simple())
162 }
163}
164
165macro_rules! define_blueprint_id_type {
168 ($type:ident, $registry:ident, $registry_name:expr) => {
169 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
170 pub struct $registry;
171
172 impl $registry {
173 const REGISTRY: &'static str = $registry_name;
174 }
175
176 impl BlueprintIdRegistry for $registry {
177 fn registry_name() -> &'static str {
178 stringify!($type)
179 }
180
181 fn registry_path() -> &'static EntityPath {
182 static REGISTRY_PATH: std::sync::LazyLock<EntityPath> =
183 std::sync::LazyLock::new(|| $registry::REGISTRY.into());
184 ®ISTRY_PATH
185 }
186 }
187
188 pub type $type = BlueprintId<$registry>;
189 };
190}
191
192define_blueprint_id_type!(ViewId, ViewIdRegistry, "view");
195define_blueprint_id_type!(ContainerId, ContainerIdRegistry, "container");
196
197pub const GLOBAL_VIEW_ID: ViewId = ViewId::from_bytes([
205 0x5C, 0x0D, 0xCA, 0x6A, 0xE6, 0x3F, 0x9C, 0xF7, 0xF6, 0x57, 0x26, 0x02, 0x59, 0x04, 0x74, 0xCC,
206]);
207
208#[cfg(test)]
211mod tests {
212 use super::*;
213
214 #[test]
215 fn test_blueprint_id() {
216 let id = ViewId::random();
217 let path = id.as_entity_path();
218 assert!(path.is_child_of(&EntityPath::parse_forgiving("view/")));
219
220 let id = ContainerId::random();
221 let path = id.as_entity_path();
222 assert!(path.is_child_of(&EntityPath::parse_forgiving("container/")));
223
224 let roundtrip = ContainerId::from_entity_path(&id.as_entity_path());
225 assert_eq!(roundtrip, id);
226
227 let crossed = ContainerId::from_entity_path(&ViewId::random().as_entity_path());
228 assert_eq!(crossed, ContainerId::invalid());
229 }
230}