re_entity_db/
instance_path.rs

1use std::hash::Hash;
2use std::str::FromStr;
3
4use re_chunk::RowId;
5use re_log_types::{DataPath, EntityPath, EntityPathHash, Instance, PathParseError};
6
7use crate::{EntityDb, VersionedInstancePath, VersionedInstancePathHash};
8
9// ----------------------------------------------------------------------------
10
11/// The path to either a specific instance of an entity, or the whole entity.
12#[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
13#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
14pub struct InstancePath {
15    pub entity_path: EntityPath,
16
17    /// If this is a concrete instance, what instance index are we?
18    ///
19    /// If we refer to all instances, [`Instance::ALL`] is used.
20    pub instance: Instance,
21}
22
23impl From<EntityPath> for InstancePath {
24    #[inline]
25    fn from(entity_path: EntityPath) -> Self {
26        Self::entity_all(entity_path)
27    }
28}
29
30impl InstancePath {
31    /// Indicate the whole entity (all instances of it).
32    ///
33    /// For example: the whole point cloud, rather than a specific point.
34    #[inline]
35    pub fn entity_all(entity_path: impl Into<EntityPath>) -> Self {
36        Self {
37            entity_path: entity_path.into(),
38            instance: Instance::ALL,
39        }
40    }
41
42    /// Indicate a specific instance of the entity,
43    /// e.g. a specific point in a point cloud entity.
44    #[inline]
45    pub fn instance(entity_path: impl Into<EntityPath>, instance: impl Into<Instance>) -> Self {
46        Self {
47            entity_path: entity_path.into(),
48            instance: instance.into(),
49        }
50    }
51
52    /// Do we refer to the whole entity (all instances of it)?
53    ///
54    /// For example: the whole point cloud, rather than a specific point.
55    #[inline]
56    pub fn is_all(&self) -> bool {
57        self.instance.is_all()
58    }
59
60    /// Versions this instance path by stamping it with the specified [`RowId`].
61    #[inline]
62    pub fn versioned(&self, row_id: RowId) -> VersionedInstancePath {
63        VersionedInstancePath {
64            instance_path: self.clone(),
65            row_id,
66        }
67    }
68
69    #[inline]
70    pub fn hash(&self) -> InstancePathHash {
71        InstancePathHash {
72            entity_path_hash: self.entity_path.hash(),
73            instance: self.instance,
74        }
75    }
76
77    /// Human-readable description of the kind
78    pub fn kind(&self) -> &'static str {
79        if self.instance.is_specific() {
80            "Entity instance"
81        } else {
82            "Entity"
83        }
84    }
85}
86
87impl std::fmt::Display for InstancePath {
88    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
89        if self.instance.is_all() {
90            self.entity_path.fmt(f)
91        } else {
92            format!("{}[{}]", self.entity_path, self.instance).fmt(f)
93        }
94    }
95}
96
97impl FromStr for InstancePath {
98    type Err = PathParseError;
99
100    fn from_str(s: &str) -> Result<Self, Self::Err> {
101        let DataPath {
102            entity_path,
103            instance,
104            component,
105        } = DataPath::from_str(s)?;
106
107        if let Some(component) = component {
108            return Err(PathParseError::UnexpectedComponent(component));
109        }
110
111        let instance = instance.unwrap_or(Instance::ALL);
112
113        Ok(Self {
114            entity_path,
115            instance,
116        })
117    }
118}
119
120#[test]
121fn test_parse_instance_path() {
122    assert_eq!(
123        InstancePath::from_str("world/points[#123]"),
124        Ok(InstancePath {
125            entity_path: EntityPath::from("world/points"),
126            instance: Instance::from(123)
127        })
128    );
129}
130
131// ----------------------------------------------------------------------------
132
133/// Hashes of the components of an [`InstancePath`].
134///
135/// This is unique to either a specific instance of an entity, or the whole entity.
136#[derive(Clone, Copy, Eq)]
137pub struct InstancePathHash {
138    pub entity_path_hash: EntityPathHash,
139
140    /// If this is a concrete instance, what instance index are we?
141    ///
142    /// If we refer to all instance, [`Instance::ALL`] is used.
143    ///
144    /// Note that this is NOT hashed, because we don't need to (it's already small).
145    pub instance: Instance,
146}
147
148impl std::fmt::Debug for InstancePathHash {
149    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
150        let Self {
151            entity_path_hash,
152            instance,
153        } = self;
154        write!(
155            f,
156            "InstancePathHash({:016X}, {})",
157            entity_path_hash.hash64(),
158            instance.get()
159        )
160    }
161}
162
163impl std::hash::Hash for InstancePathHash {
164    #[inline]
165    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
166        let Self {
167            entity_path_hash,
168            instance,
169        } = self;
170
171        state.write_u64(entity_path_hash.hash64());
172        state.write_u64(instance.get());
173    }
174}
175
176impl std::cmp::PartialEq for InstancePathHash {
177    #[inline]
178    fn eq(&self, other: &Self) -> bool {
179        let Self {
180            entity_path_hash,
181            instance,
182        } = self;
183
184        entity_path_hash == &other.entity_path_hash && instance == &other.instance
185    }
186}
187
188impl InstancePathHash {
189    pub const NONE: Self = Self {
190        entity_path_hash: EntityPathHash::NONE,
191        instance: Instance::ALL,
192    };
193
194    /// Indicate the whole entity (all instances of it).
195    ///
196    /// For example: the whole point cloud, rather than a specific point.
197    #[inline]
198    pub fn entity_all(entity_path: &EntityPath) -> Self {
199        Self {
200            entity_path_hash: entity_path.hash(),
201            instance: Instance::ALL,
202        }
203    }
204
205    /// Indicate a specific instance of the entity,
206    /// e.g. a specific point in a point cloud entity.
207    #[inline]
208    pub fn instance(entity_path: &EntityPath, instance: Instance) -> Self {
209        Self {
210            entity_path_hash: entity_path.hash(),
211            instance,
212        }
213    }
214
215    #[inline]
216    pub fn is_some(&self) -> bool {
217        self.entity_path_hash.is_some()
218    }
219
220    #[inline]
221    pub fn is_none(&self) -> bool {
222        self.entity_path_hash.is_none()
223    }
224
225    /// Versions this hashed instance path by stamping it with the specified [`RowId`].
226    #[inline]
227    pub fn versioned(&self, row_id: RowId) -> VersionedInstancePathHash {
228        VersionedInstancePathHash {
229            instance_path_hash: *self,
230            row_id,
231        }
232    }
233
234    pub fn resolve(&self, entity_db: &EntityDb) -> Option<InstancePath> {
235        let entity_path = entity_db
236            .entity_path_from_hash(&self.entity_path_hash)
237            .cloned()?;
238
239        let instance = self.instance;
240
241        Some(InstancePath {
242            entity_path,
243            instance,
244        })
245    }
246}
247
248impl Default for InstancePathHash {
249    fn default() -> Self {
250        Self::NONE
251    }
252}