re_types_core/
archetype.rs

1use crate::{ComponentDescriptor, DeserializationResult};
2
3#[expect(unused_imports, clippy::unused_trait_names)] // used in docstrings
4use crate::{Component, Loggable};
5
6// ---
7
8/// An archetype is a high-level construct that represents a set of [`Component`]s that usually
9/// play well with each other (i.e. they compose nicely).
10///
11/// Internally, it is no different than a collection of components, but working at that higher
12/// layer of abstraction opens opportunities for nicer APIs & tools that wouldn't be possible
13/// otherwise.
14///
15/// E.g. consider the `crate::archetypes::Points3D` archetype, which represents the set of
16/// components to consider when working with a 3D point cloud within Rerun.
17pub trait Archetype {
18    /// The fully-qualified name of this archetype, e.g. `rerun.archetypes.Points2D`.
19    fn name() -> ArchetypeName;
20
21    /// Readable name for displaying in UI.
22    fn display_name() -> &'static str;
23
24    // ---
25
26    /// Returns all component descriptors that _must_ be provided by the user when constructing this archetype.
27    fn required_components() -> std::borrow::Cow<'static, [ComponentDescriptor]>;
28
29    /// Returns all component descriptors that _should_ be provided by the user when constructing this archetype.
30    #[inline]
31    fn recommended_components() -> std::borrow::Cow<'static, [ComponentDescriptor]> {
32        // TODO(#10512): Maybe add the "marker" component back here?
33        std::borrow::Cow::Owned(vec![])
34    }
35
36    /// Returns all component descriptors that _may_ be provided by the user when constructing this archetype.
37    #[inline]
38    fn optional_components() -> std::borrow::Cow<'static, [ComponentDescriptor]> {
39        std::borrow::Cow::Borrowed(&[])
40    }
41
42    /// Returns all component descriptors that must, should and may be provided by the user when constructing
43    /// this archetype.
44    ///
45    /// The default implementation always does the right thing, at the cost of some runtime
46    /// allocations.
47    /// If you know all your component descriptors statically, you can override this method to get rid of the
48    /// extra allocations.
49    #[inline]
50    fn all_components() -> std::borrow::Cow<'static, [ComponentDescriptor]> {
51        [
52            Self::required_components().into_owned(),
53            Self::recommended_components().into_owned(),
54            Self::optional_components().into_owned(),
55        ]
56        .into_iter()
57        .flatten()
58        .collect::<Vec<_>>()
59        .into()
60    }
61
62    // ---
63
64    /// Given an iterator of Arrow arrays and their respective field metadata, deserializes them
65    /// into this archetype.
66    ///
67    /// Arrow arrays that are unknown to this [`Archetype`] will simply be ignored and a warning
68    /// logged to stderr.
69    #[inline]
70    fn from_arrow(
71        data: impl IntoIterator<Item = (arrow::datatypes::Field, ::arrow::array::ArrayRef)>,
72    ) -> DeserializationResult<Self>
73    where
74        Self: Sized,
75    {
76        Self::from_arrow_components(
77            data.into_iter()
78                .map(|(field, array)| (ComponentDescriptor::from(field), array)),
79        )
80    }
81
82    /// Given an iterator of Arrow arrays and their respective [`ComponentDescriptor`]s, deserializes them
83    /// into this archetype.
84    ///
85    /// Arrow arrays that are unknown to this [`Archetype`] will simply be ignored and a warning
86    /// logged to stderr.
87    #[inline]
88    fn from_arrow_components(
89        data: impl IntoIterator<Item = (ComponentDescriptor, ::arrow::array::ArrayRef)>,
90    ) -> DeserializationResult<Self>
91    where
92        Self: Sized,
93    {
94        _ = data; // NOTE: do this here to avoid breaking users' autocomplete snippets
95        Err(crate::DeserializationError::NotImplemented {
96            fqname: Self::name().to_string(),
97            backtrace: Box::new(std::backtrace::Backtrace::capture()),
98        })
99    }
100}
101
102/// Indicates that the archetype has reflection data available for it.
103pub trait ArchetypeReflectionMarker {}
104
105// ---
106
107re_string_interner::declare_new_type!(
108    /// The fully-qualified name of an [`Archetype`], e.g. `rerun.archetypes.Points3D`.
109    #[cfg_attr(feature = "serde", derive(::serde::Deserialize, ::serde::Serialize))]
110    pub struct ArchetypeName;
111);
112
113impl ArchetypeName {
114    /// Runs some asserts in debug mode to make sure the name is not weird.
115    #[inline]
116    #[track_caller]
117    pub fn sanity_check(&self) {
118        let full_name = self.0.as_str();
119        debug_assert!(
120            !full_name.starts_with("rerun.archetypes.rerun.archetypes."),
121            "DEBUG ASSERT: Found archetype with full name {full_name:?}. Maybe some bad round-tripping?"
122        );
123    }
124
125    /// Returns the fully-qualified name, e.g. `rerun.archetypes.Points3D`.
126    ///
127    /// This is the default `Display` implementation for [`ArchetypeName`].
128    #[inline]
129    pub fn full_name(&self) -> &'static str {
130        self.sanity_check();
131        self.0.as_str()
132    }
133
134    /// Returns the unqualified name, e.g. `Points3D`.
135    ///
136    /// Used for most UI elements.
137    ///
138    /// ```
139    /// # use re_types_core::ArchetypeName;
140    /// assert_eq!(ArchetypeName::from("rerun.archetypes.Points3D").short_name(), "Points3D");
141    /// ```
142    #[inline]
143    pub fn short_name(&self) -> &'static str {
144        self.sanity_check();
145        let full_name = self.0.as_str();
146        if let Some(short_name) = full_name.strip_prefix("rerun.archetypes.") {
147            short_name
148        } else if let Some(short_name) = full_name.strip_prefix("rerun.blueprint.archetypes.") {
149            short_name
150        } else if let Some(short_name) = full_name.strip_prefix("rerun.") {
151            short_name
152        } else {
153            full_name
154        }
155    }
156
157    /// Url to the rerun docs for this Rerun archetype.
158    pub fn doc_url(&self) -> Option<String> {
159        // This code should be correct as long as this url passes our link checker:
160        // https://rerun.io/docs/reference/types/archetypes/line_strips3d
161        let short_name_pascal_case = self.full_name().strip_prefix("rerun.archetypes.")?;
162        let archetype_name_snake_case = re_case::to_snake_case(short_name_pascal_case);
163        let base_url = "https://rerun.io/docs/reference/types/archetypes";
164        Some(format!("{base_url}/{archetype_name_snake_case}"))
165    }
166}
167
168impl re_byte_size::SizeBytes for ArchetypeName {
169    #[inline]
170    fn heap_size_bytes(&self) -> u64 {
171        0
172    }
173}
174
175// ---
176
177re_string_interner::declare_new_type!(
178    /// An identifier for a component, i.e. a field in an [`Archetype`].
179    #[cfg_attr(feature = "serde", derive(::serde::Deserialize, ::serde::Serialize))]
180    pub struct ComponentIdentifier;
181);
182
183impl re_byte_size::SizeBytes for ComponentIdentifier {
184    #[inline]
185    fn heap_size_bytes(&self) -> u64 {
186        0
187    }
188}