re_types_core/loggable.rs
1use nohash_hasher::IntSet;
2
3use re_byte_size::SizeBytes;
4
5use crate::{ComponentIdentifier, 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 UnorderedComponentSet = IntSet<ComponentIdentifier>;
97
98pub type ComponentSet = std::collections::BTreeSet<ComponentIdentifier>;
99
100re_string_interner::declare_new_type!(
101 /// The fully-qualified name of a [`Component`], e.g. `rerun.components.Position2D`.
102 #[cfg_attr(feature = "serde", derive(::serde::Deserialize, ::serde::Serialize))]
103 pub struct ComponentType;
104);
105
106impl ComponentType {
107 /// Runs some asserts in debug mode to make sure the name is not weird.
108 #[inline]
109 #[track_caller]
110 pub fn sanity_check(&self) {
111 let full_type = self.0.as_str();
112 debug_assert!(
113 !full_type.starts_with("rerun.components.rerun.components."),
114 "DEBUG ASSERT: Found component with full type {full_type:?}. Maybe some bad round-tripping?"
115 );
116 }
117
118 /// Returns the fully-qualified name, e.g. `rerun.components.Position2D`.
119 ///
120 /// This is the default `Display` implementation for [`ComponentType`].
121 #[inline]
122 pub fn full_name(&self) -> &'static str {
123 self.sanity_check();
124 self.0.as_str()
125 }
126
127 /// Returns the unqualified name, e.g. `Position2D`.
128 ///
129 /// Used for most UI elements.
130 ///
131 /// ```
132 /// # use re_types_core::ComponentType;
133 /// assert_eq!(ComponentType::from("rerun.components.Position2D").short_name(), "Position2D");
134 /// ```
135 #[inline]
136 pub fn short_name(&self) -> &'static str {
137 self.sanity_check();
138 let full_name = self.0.as_str();
139 if let Some(short_name) = full_name.strip_prefix("rerun.blueprint.components.") {
140 short_name
141 } else if let Some(short_name) = full_name.strip_prefix("rerun.components.") {
142 short_name
143 } else if let Some(short_name) = full_name.strip_prefix("rerun.controls.") {
144 short_name
145 } else if let Some(short_name) = full_name.strip_prefix("rerun.") {
146 short_name
147 } else {
148 full_name
149 }
150 }
151
152 /// Web URL to the Rerun documentation for this component.
153 pub fn doc_url(&self) -> Option<String> {
154 if let Some(component_type_pascal_case) = self.full_name().strip_prefix("rerun.components.")
155 {
156 // This code should be correct as long as this url passes our link checker:
157 // https://rerun.io/docs/reference/types/components/line_strip2d
158
159 let component_type_snake_case = re_case::to_snake_case(component_type_pascal_case);
160 let base_url = "https://rerun.io/docs/reference/types/components";
161 Some(format!("{base_url}/{component_type_snake_case}"))
162 } else {
163 None // A user component
164 }
165 }
166
167 /// Determine if component matches a string
168 ///
169 /// Valid matches are case invariant matches of either the full name or the short name.
170 pub fn matches(&self, other: &str) -> bool {
171 self.0.as_str() == other
172 || self.full_name().to_lowercase() == other.to_lowercase()
173 || self.short_name().to_lowercase() == other.to_lowercase()
174 }
175}
176
177// ---
178
179impl re_byte_size::SizeBytes for ComponentType {
180 #[inline]
181 fn heap_size_bytes(&self) -> u64 {
182 0
183 }
184}
185
186re_string_interner::declare_new_type!(
187 /// The fully-qualified name of a [`Datatype`], e.g. `rerun.datatypes.Vec2D`.
188 #[cfg_attr(feature = "serde", derive(::serde::Deserialize, ::serde::Serialize))]
189 pub struct DatatypeName;
190);
191
192impl DatatypeName {
193 /// Returns the fully-qualified name, e.g. `rerun.datatypes.Vec2D`.
194 ///
195 /// This is the default `Display` implementation for [`DatatypeName`].
196 #[inline]
197 pub fn full_name(&self) -> &'static str {
198 self.0.as_str()
199 }
200
201 /// Returns the unqualified name, e.g. `Vec2D`.
202 ///
203 /// Used for most UI elements.
204 ///
205 /// ```
206 /// # use re_types_core::DatatypeName;
207 /// assert_eq!(DatatypeName::from("rerun.datatypes.Vec2D").short_name(), "Vec2D");
208 /// ```
209 #[inline]
210 pub fn short_name(&self) -> &'static str {
211 let full_name = self.0.as_str();
212 if let Some(short_name) = full_name.strip_prefix("rerun.datatypes.") {
213 short_name
214 } else if let Some(short_name) = full_name.strip_prefix("rerun.") {
215 short_name
216 } else {
217 full_name
218 }
219 }
220}
221
222impl re_byte_size::SizeBytes for DatatypeName {
223 #[inline]
224 fn heap_size_bytes(&self) -> u64 {
225 0
226 }
227}