re_types_core/
loggable.rs

1use nohash_hasher::IntSet;
2
3use re_byte_size::SizeBytes;
4
5use crate::{ComponentDescriptor, DeserializationResult, SerializationResult};
6
7#[expect(unused_imports, clippy::unused_trait_names)] // used in docstrings
8use crate::{Archetype, ComponentBatch};
9
10// ---
11
12/// A [`Loggable`] represents a single instance in an array of loggable data.
13///
14/// Internally, Arrow, and by extension Rerun, only deal with arrays of data.
15/// We refer to individual entries in these arrays as instances.
16///
17/// A [`Loggable`] has no semantics (such as a name, for example): it's just data.
18/// If you want to encode semantics, then you're looking for a [`Component`], which extends [`Loggable`].
19///
20/// Implementing the [`Loggable`] trait automatically derives the [`ComponentBatch`] implementation,
21/// which makes it possible to work with lists' worth of data in a generic fashion.
22pub trait Loggable: 'static + Send + Sync + Clone + Sized + SizeBytes {
23    /// The underlying [`arrow::datatypes::DataType`], excluding datatype extensions.
24    fn arrow_datatype() -> arrow::datatypes::DataType;
25
26    // Returns an empty Arrow array that matches this `Loggable`'s underlying datatype.
27    #[inline]
28    fn arrow_empty() -> arrow::array::ArrayRef {
29        arrow::array::new_empty_array(&Self::arrow_datatype())
30    }
31
32    /// Given an iterator of owned or reference values to the current [`Loggable`], serializes
33    /// them into an Arrow array.
34    ///
35    /// When using Rerun's builtin components & datatypes, this can only fail if the data
36    /// exceeds the maximum number of entries in an Arrow array (2^31 for standard arrays,
37    /// 2^63 for large arrays).
38    #[inline]
39    fn to_arrow<'a>(
40        data: impl IntoIterator<Item = impl Into<std::borrow::Cow<'a, Self>>>,
41    ) -> SerializationResult<arrow::array::ArrayRef>
42    where
43        Self: 'a,
44    {
45        Self::to_arrow_opt(data.into_iter().map(|v| Some(v)))
46    }
47
48    /// Given an iterator of options of owned or reference values to the current
49    /// [`Loggable`], serializes them into an Arrow array.
50    ///
51    /// When using Rerun's builtin components & datatypes, this can only fail if the data
52    /// exceeds the maximum number of entries in an Arrow array (2^31 for standard arrays,
53    /// 2^63 for large arrays).
54    fn to_arrow_opt<'a>(
55        data: impl IntoIterator<Item = Option<impl Into<std::borrow::Cow<'a, Self>>>>,
56    ) -> SerializationResult<arrow::array::ArrayRef>
57    where
58        Self: 'a;
59
60    /// Given an Arrow array, deserializes it into a collection of [`Loggable`]s.
61    #[inline]
62    fn from_arrow(data: &dyn arrow::array::Array) -> DeserializationResult<Vec<Self>> {
63        Self::from_arrow_opt(data)?
64            .into_iter()
65            .map(|opt| opt.ok_or_else(crate::DeserializationError::missing_data))
66            .collect::<DeserializationResult<Vec<_>>>()
67    }
68
69    /// Given an Arrow array, deserializes it into a collection of optional [`Loggable`]s.
70    #[inline]
71    fn from_arrow_opt(
72        data: &dyn arrow::array::Array,
73    ) -> crate::DeserializationResult<Vec<Option<Self>>> {
74        Self::from_arrow(data).map(|v| v.into_iter().map(Some).collect())
75    }
76
77    /// Verifies that the given Arrow array can be deserialized into a collection of [`Self`]s.
78    ///
79    /// Calls [`Self::from_arrow`] and returns an error if it fails.
80    fn verify_arrow_array(data: &dyn arrow::array::Array) -> crate::DeserializationResult<()> {
81        Self::from_arrow(data).map(|_| ())
82    }
83}
84
85/// A [`Component`] describes semantic data that can be used by any number of [`Archetype`]s.
86///
87/// Implementing the [`Component`] trait automatically derives the [`ComponentBatch`] implementation,
88/// which makes it possible to work with lists' worth of data in a generic fashion.
89pub trait Component: Loggable {
90    /// The fully-qualified type of this component, e.g. `rerun.components.Position2D`.
91    fn name() -> ComponentType;
92}
93
94// ---
95
96pub type UnorderedComponentDescriptorSet = IntSet<ComponentDescriptor>;
97
98// TODO(#10460): Can we replace this with `BTreeSet<ComponentIdentifier>` here?
99pub type ComponentDescriptorSet = std::collections::BTreeSet<ComponentDescriptor>;
100
101re_string_interner::declare_new_type!(
102    /// The fully-qualified name of a [`Component`], e.g. `rerun.components.Position2D`.
103    #[cfg_attr(feature = "serde", derive(::serde::Deserialize, ::serde::Serialize))]
104    pub struct ComponentType;
105);
106
107impl ComponentType {
108    /// Runs some asserts in debug mode to make sure the name is not weird.
109    #[inline]
110    #[track_caller]
111    pub fn sanity_check(&self) {
112        let full_type = self.0.as_str();
113        debug_assert!(
114            !full_type.starts_with("rerun.components.rerun.components."),
115            "DEBUG ASSERT: Found component with full type {full_type:?}. Maybe some bad round-tripping?"
116        );
117    }
118
119    /// Returns the fully-qualified name, e.g. `rerun.components.Position2D`.
120    ///
121    /// This is the default `Display` implementation for [`ComponentType`].
122    #[inline]
123    pub fn full_name(&self) -> &'static str {
124        self.sanity_check();
125        self.0.as_str()
126    }
127
128    /// Returns the unqualified name, e.g. `Position2D`.
129    ///
130    /// Used for most UI elements.
131    ///
132    /// ```
133    /// # use re_types_core::ComponentType;
134    /// assert_eq!(ComponentType::from("rerun.components.Position2D").short_name(), "Position2D");
135    /// ```
136    #[inline]
137    pub fn short_name(&self) -> &'static str {
138        self.sanity_check();
139        let full_name = self.0.as_str();
140        if let Some(short_name) = full_name.strip_prefix("rerun.blueprint.components.") {
141            short_name
142        } else if let Some(short_name) = full_name.strip_prefix("rerun.components.") {
143            short_name
144        } else if let Some(short_name) = full_name.strip_prefix("rerun.controls.") {
145            short_name
146        } else if let Some(short_name) = full_name.strip_prefix("rerun.") {
147            short_name
148        } else {
149            full_name
150        }
151    }
152
153    /// Web URL to the Rerun documentation for this component.
154    pub fn doc_url(&self) -> Option<String> {
155        if let Some(component_type_pascal_case) = self.full_name().strip_prefix("rerun.components.")
156        {
157            // This code should be correct as long as this url passes our link checker:
158            // https://rerun.io/docs/reference/types/components/line_strip2d
159
160            let component_type_snake_case = re_case::to_snake_case(component_type_pascal_case);
161            let base_url = "https://rerun.io/docs/reference/types/components";
162            Some(format!("{base_url}/{component_type_snake_case}"))
163        } else {
164            None // A user component
165        }
166    }
167
168    /// Determine if component matches a string
169    ///
170    /// Valid matches are case invariant matches of either the full name or the short name.
171    pub fn matches(&self, other: &str) -> bool {
172        self.0.as_str() == other
173            || self.full_name().to_lowercase() == other.to_lowercase()
174            || self.short_name().to_lowercase() == other.to_lowercase()
175    }
176}
177
178// ---
179
180impl re_byte_size::SizeBytes for ComponentType {
181    #[inline]
182    fn heap_size_bytes(&self) -> u64 {
183        0
184    }
185}
186
187re_string_interner::declare_new_type!(
188    /// The fully-qualified name of a [`Datatype`], e.g. `rerun.datatypes.Vec2D`.
189    #[cfg_attr(feature = "serde", derive(::serde::Deserialize, ::serde::Serialize))]
190    pub struct DatatypeName;
191);
192
193impl DatatypeName {
194    /// Returns the fully-qualified name, e.g. `rerun.datatypes.Vec2D`.
195    ///
196    /// This is the default `Display` implementation for [`DatatypeName`].
197    #[inline]
198    pub fn full_name(&self) -> &'static str {
199        self.0.as_str()
200    }
201
202    /// Returns the unqualified name, e.g. `Vec2D`.
203    ///
204    /// Used for most UI elements.
205    ///
206    /// ```
207    /// # use re_types_core::DatatypeName;
208    /// assert_eq!(DatatypeName::from("rerun.datatypes.Vec2D").short_name(), "Vec2D");
209    /// ```
210    #[inline]
211    pub fn short_name(&self) -> &'static str {
212        let full_name = self.0.as_str();
213        if let Some(short_name) = full_name.strip_prefix("rerun.datatypes.") {
214            short_name
215        } else if let Some(short_name) = full_name.strip_prefix("rerun.") {
216            short_name
217        } else {
218            full_name
219        }
220    }
221}
222
223impl re_byte_size::SizeBytes for DatatypeName {
224    #[inline]
225    fn heap_size_bytes(&self) -> u64 {
226        0
227    }
228}