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