re_types_core/
archetype.rs

1use std::{borrow::Cow, sync::Arc};
2
3use crate::{
4    ComponentBatch, ComponentDescriptor, ComponentName, DeserializationResult, SerializationResult,
5    SerializedComponentBatch, _Backtrace,
6};
7
8#[expect(unused_imports, clippy::unused_trait_names)] // used in docstrings
9use crate::{Component, Loggable, LoggableBatch};
10
11// ---
12
13/// An archetype is a high-level construct that represents a set of [`Component`]s that usually
14/// play well with each other (i.e. they compose nicely).
15///
16/// Internally, it is no different than a collection of components, but working at that higher
17/// layer of abstraction opens opportunities for nicer APIs & tools that wouldn't be possible
18/// otherwise.
19///
20/// E.g. consider the `crate::archetypes::Points3D` archetype, which represents the set of
21/// components to consider when working with a 3D point cloud within Rerun.
22pub trait Archetype {
23    /// The associated indicator component, whose presence indicates that the high-level
24    /// archetype-based APIs were used to log the data.
25    ///
26    /// ## Internal representation
27    ///
28    /// Indicator components are always unit-length null arrays.
29    /// Their names follow the pattern `rerun.components.{ArchetypeName}Indicator`, e.g.
30    /// `rerun.components.Points3DIndicator`.
31    ///
32    /// Since null arrays aren't actually arrays and we don't actually have any data to shuffle
33    /// around per-se, we can't implement the usual [`Loggable`] traits.
34    /// For this reason, indicator components directly implement [`LoggableBatch`] instead, and
35    /// bypass the entire iterator machinery.
36    //
37    // TODO(rust-lang/rust#29661): We'd like to just default this to the right thing which is
38    // pretty much always `A::Indicator`, but defaults are unstable.
39    // type Indicator: ComponentBatch = A::Indicator;
40    type Indicator: 'static + ComponentBatch + Default;
41
42    /// The fully-qualified name of this archetype, e.g. `rerun.archetypes.Points2D`.
43    fn name() -> ArchetypeName;
44
45    /// Readable name for displaying in UI.
46    fn display_name() -> &'static str;
47
48    // ---
49
50    /// Creates a [`ComponentBatch`] out of the associated [`Self::Indicator`] component.
51    ///
52    /// This allows for associating arbitrary indicator components with arbitrary data.
53    fn indicator() -> SerializedComponentBatch;
54
55    /// Returns all component descriptors that _must_ be provided by the user when constructing this archetype.
56    fn required_components() -> std::borrow::Cow<'static, [ComponentDescriptor]>;
57
58    /// Returns all component descriptors that _should_ be provided by the user when constructing this archetype.
59    #[inline]
60    fn recommended_components() -> std::borrow::Cow<'static, [ComponentDescriptor]> {
61        std::borrow::Cow::Owned(vec![Self::indicator().descriptor.clone()])
62    }
63
64    /// Returns all component descriptors that _may_ be provided by the user when constructing this archetype.
65    #[inline]
66    fn optional_components() -> std::borrow::Cow<'static, [ComponentDescriptor]> {
67        std::borrow::Cow::Borrowed(&[])
68    }
69
70    /// Returns all component descriptors that must, should and may be provided by the user when constructing
71    /// this archetype.
72    ///
73    /// The default implementation always does the right thing, at the cost of some runtime
74    /// allocations.
75    /// If you know all your component descriptors statically, you can override this method to get rid of the
76    /// extra allocations.
77    #[inline]
78    fn all_components() -> std::borrow::Cow<'static, [ComponentDescriptor]> {
79        [
80            Self::required_components().into_owned(),
81            Self::recommended_components().into_owned(),
82            Self::optional_components().into_owned(),
83        ]
84        .into_iter()
85        .flatten()
86        .collect::<Vec<_>>()
87        .into()
88    }
89
90    // ---
91
92    /// Given an iterator of Arrow arrays and their respective field metadata, deserializes them
93    /// into this archetype.
94    ///
95    /// Arrow arrays that are unknown to this [`Archetype`] will simply be ignored and a warning
96    /// logged to stderr.
97    #[inline]
98    fn from_arrow(
99        data: impl IntoIterator<Item = (arrow::datatypes::Field, ::arrow::array::ArrayRef)>,
100    ) -> DeserializationResult<Self>
101    where
102        Self: Sized,
103    {
104        Self::from_arrow_components(
105            data.into_iter()
106                .map(|(field, array)| (ComponentDescriptor::from(field), array)),
107        )
108    }
109
110    /// Given an iterator of Arrow arrays and their respective `ComponentNames`, deserializes them
111    /// into this archetype.
112    ///
113    /// Arrow arrays that are unknown to this [`Archetype`] will simply be ignored and a warning
114    /// logged to stderr.
115    #[inline]
116    fn from_arrow_components(
117        data: impl IntoIterator<Item = (ComponentDescriptor, ::arrow::array::ArrayRef)>,
118    ) -> DeserializationResult<Self>
119    where
120        Self: Sized,
121    {
122        _ = data; // NOTE: do this here to avoid breaking users' autocomplete snippets
123        Err(crate::DeserializationError::NotImplemented {
124            fqname: Self::name().to_string(),
125            backtrace: _Backtrace::new_unresolved(),
126        })
127    }
128}
129
130/// Indicates that the archetype has reflection data available for it.
131pub trait ArchetypeReflectionMarker {}
132
133// ---
134
135re_string_interner::declare_new_type!(
136    /// The fully-qualified name of an [`Archetype`], e.g. `rerun.archetypes.Points3D`.
137    #[cfg_attr(feature = "serde", derive(::serde::Deserialize, ::serde::Serialize))]
138    pub struct ArchetypeName;
139);
140
141impl ArchetypeName {
142    /// Runs some asserts in debug mode to make sure the name is not weird.
143    #[inline]
144    #[track_caller]
145    pub fn sanity_check(&self) {
146        let full_name = self.0.as_str();
147        debug_assert!(
148            !full_name.starts_with("rerun.archetypes.rerun.archetypes.")
149                && !full_name.contains(':'),
150            "DEBUG ASSERT: Found archetype with full name {full_name:?}. Maybe some bad round-tripping?"
151        );
152    }
153
154    /// Returns the fully-qualified name, e.g. `rerun.archetypes.Points3D`.
155    ///
156    /// This is the default `Display` implementation for [`ArchetypeName`].
157    #[inline]
158    pub fn full_name(&self) -> &'static str {
159        self.sanity_check();
160        self.0.as_str()
161    }
162
163    /// Returns the unqualified name, e.g. `Points3D`.
164    ///
165    /// Used for most UI elements.
166    ///
167    /// ```
168    /// # use re_types_core::ArchetypeName;
169    /// assert_eq!(ArchetypeName::from("rerun.archetypes.Points3D").short_name(), "Points3D");
170    /// ```
171    #[inline]
172    pub fn short_name(&self) -> &'static str {
173        self.sanity_check();
174        let full_name = self.0.as_str();
175        if let Some(short_name) = full_name.strip_prefix("rerun.archetypes.") {
176            short_name
177        } else if let Some(short_name) = full_name.strip_prefix("rerun.blueprint.archetypes.") {
178            short_name
179        } else if let Some(short_name) = full_name.strip_prefix("rerun.") {
180            short_name
181        } else {
182            full_name
183        }
184    }
185}
186
187impl re_byte_size::SizeBytes for ArchetypeName {
188    #[inline]
189    fn heap_size_bytes(&self) -> u64 {
190        0
191    }
192}
193
194// ---
195
196re_string_interner::declare_new_type!(
197    /// The name of an [`Archetype`]'s field, e.g. `positions`.
198    #[cfg_attr(feature = "serde", derive(::serde::Deserialize, ::serde::Serialize))]
199    pub struct ArchetypeFieldName;
200);
201
202impl re_byte_size::SizeBytes for ArchetypeFieldName {
203    #[inline]
204    fn heap_size_bytes(&self) -> u64 {
205        0
206    }
207}
208
209// ---
210
211/// A generic [indicator component] that can be specialized for any [`Archetype`].
212///
213/// ```ignore
214/// type MyArchetypeIndicator = GenericIndicatorComponent<MyArchetype>;
215/// ```
216///
217/// [indicator component]: [`Archetype::Indicator`]
218#[derive(Debug, Clone, Copy)]
219pub struct GenericIndicatorComponent<A: Archetype> {
220    _phantom: std::marker::PhantomData<A>,
221}
222
223impl<A: Archetype> GenericIndicatorComponent<A> {
224    pub const DEFAULT: Self = Self {
225        _phantom: std::marker::PhantomData::<A>,
226    };
227
228    /// Create an array of indicator components of this type with the given length.
229    ///
230    /// This can be useful when sending columns of indicators with
231    /// `rerun::RecordingStream::send_columns`.
232    #[inline]
233    pub fn new_array(len: usize) -> GenericIndicatorComponentArray<A> {
234        GenericIndicatorComponentArray {
235            len,
236            _phantom: std::marker::PhantomData::<A>,
237        }
238    }
239}
240
241impl<A: Archetype> Default for GenericIndicatorComponent<A> {
242    fn default() -> Self {
243        Self::DEFAULT
244    }
245}
246
247impl<A: Archetype> crate::LoggableBatch for GenericIndicatorComponent<A> {
248    #[inline]
249    fn to_arrow(&self) -> SerializationResult<arrow::array::ArrayRef> {
250        Ok(Arc::new(arrow::array::NullArray::new(1)))
251    }
252}
253
254impl<A: Archetype> crate::ComponentBatch for GenericIndicatorComponent<A> {
255    #[inline]
256    fn descriptor(&self) -> Cow<'_, ComponentDescriptor> {
257        let component_name =
258            format!("{}Indicator", A::name().full_name()).replace("archetypes", "components");
259        ComponentDescriptor::new(component_name).into()
260    }
261}
262
263/// A generic [indicator component] array of a given length.
264///
265/// This can be useful when sending columns of indicators with
266/// `rerun::RecordingStream::send_columns`.
267///
268/// To create this type, call [`GenericIndicatorComponent::new_array`].
269///
270/// [indicator component]: [`Archetype::Indicator`]
271#[derive(Debug, Clone, Copy)]
272pub struct GenericIndicatorComponentArray<A: Archetype> {
273    len: usize,
274    _phantom: std::marker::PhantomData<A>,
275}
276
277impl<A: Archetype> crate::LoggableBatch for GenericIndicatorComponentArray<A> {
278    #[inline]
279    fn to_arrow(&self) -> SerializationResult<arrow::array::ArrayRef> {
280        Ok(Arc::new(arrow::array::NullArray::new(self.len)))
281    }
282}
283
284impl<A: Archetype> crate::ComponentBatch for GenericIndicatorComponentArray<A> {
285    #[inline]
286    fn descriptor(&self) -> Cow<'_, ComponentDescriptor> {
287        ComponentDescriptor::new(GenericIndicatorComponent::<A>::DEFAULT.name()).into()
288    }
289}
290
291// ---
292
293/// An arbitrary named [indicator component].
294///
295/// [indicator component]: [`Archetype::Indicator`]
296#[derive(Debug, Clone, Copy)]
297pub struct NamedIndicatorComponent(pub ComponentName);
298
299impl crate::LoggableBatch for NamedIndicatorComponent {
300    #[inline]
301    fn to_arrow(&self) -> SerializationResult<arrow::array::ArrayRef> {
302        Ok(Arc::new(arrow::array::NullArray::new(1)))
303    }
304}
305
306impl crate::ComponentBatch for NamedIndicatorComponent {
307    #[inline]
308    fn descriptor(&self) -> Cow<'_, ComponentDescriptor> {
309        ComponentDescriptor::new(self.0).into()
310    }
311}