re_types_core/
as_components.rs

1use crate::{SerializationResult, SerializedComponentBatch};
2
3/// Describes the interface for interpreting an object as a bundle of [`Component`]s.
4///
5/// ## Custom bundles
6///
7/// While, in most cases, component bundles are code generated from our [IDL definitions],
8/// it is possible to manually extend existing bundles, or even implement fully custom ones.
9///
10/// All [`AsComponents`] methods are optional to implement, with the exception of
11/// [`AsComponents::as_serialized_batches`], which describes how the bundle can be interpreted
12/// as a set of [`SerializedComponentBatch`]es: serialized component data.
13///
14/// Have a look at our [Custom Data Loader] example to learn more about handwritten bundles.
15///
16/// [IDL definitions]: https://github.com/rerun-io/rerun/tree/latest/crates/store/re_types/definitions/rerun
17/// [Custom Data Loader]: https://github.com/rerun-io/rerun/blob/latest/examples/rust/custom_data_loader
18/// [`Component`]: [crate::Component]
19pub trait AsComponents {
20    /// Exposes the object's contents as a set of [`SerializedComponentBatch`]es.
21    ///
22    /// This is the main mechanism for easily extending builtin archetypes or even writing
23    /// fully custom ones.
24    /// Have a look at our [Custom Data Loader] example to learn more about extending archetypes.
25    ///
26    /// Implementers of [`AsComponents`] get one last chance to override the tags in the
27    /// [`ComponentDescriptor`], see [`SerializedComponentBatch::with_descriptor_override`].
28    ///
29    /// [Custom Data Loader]: https://github.com/rerun-io/rerun/blob/latest/docs/snippets/all/tutorials/custom_data.rs
30    /// [`ComponentDescriptor`]: [crate::ComponentDescriptor]
31    //
32    // NOTE: Don't bother returning a CoW here: we need to dynamically discard optional components
33    // depending on their presence (or lack thereof) at runtime anyway.
34    fn as_serialized_batches(&self) -> Vec<SerializedComponentBatch>;
35
36    // ---
37
38    /// Serializes all non-null [`Component`]s of this bundle into Arrow arrays.
39    ///
40    /// The default implementation will simply serialize the result of [`Self::as_serialized_batches`]
41    /// as-is, which is what you want in 99.9% of cases.
42    ///
43    /// [`Component`]: [crate::Component]
44    #[inline]
45    fn to_arrow(
46        &self,
47    ) -> SerializationResult<Vec<(::arrow::datatypes::Field, ::arrow::array::ArrayRef)>> {
48        self.as_serialized_batches()
49            .into_iter()
50            .map(|comp_batch| Ok((arrow::datatypes::Field::from(&comp_batch), comp_batch.array)))
51            .collect()
52    }
53}
54
55#[expect(dead_code)]
56fn assert_object_safe() {
57    let _: &dyn AsComponents;
58}
59
60impl AsComponents for SerializedComponentBatch {
61    #[inline]
62    fn as_serialized_batches(&self) -> Vec<SerializedComponentBatch> {
63        vec![self.clone()]
64    }
65}
66
67impl<AS: AsComponents, const N: usize> AsComponents for [AS; N] {
68    #[inline]
69    fn as_serialized_batches(&self) -> Vec<SerializedComponentBatch> {
70        self.iter()
71            .flat_map(|as_components| as_components.as_serialized_batches())
72            .collect()
73    }
74}
75
76impl<const N: usize> AsComponents for [&dyn AsComponents; N] {
77    #[inline]
78    fn as_serialized_batches(&self) -> Vec<SerializedComponentBatch> {
79        self.iter()
80            .flat_map(|as_components| as_components.as_serialized_batches())
81            .collect()
82    }
83}
84
85impl<const N: usize> AsComponents for [Box<dyn AsComponents>; N] {
86    #[inline]
87    fn as_serialized_batches(&self) -> Vec<SerializedComponentBatch> {
88        self.iter()
89            .flat_map(|as_components| as_components.as_serialized_batches())
90            .collect()
91    }
92}
93
94impl<AS: AsComponents> AsComponents for Vec<AS> {
95    #[inline]
96    fn as_serialized_batches(&self) -> Vec<SerializedComponentBatch> {
97        self.iter()
98            .flat_map(|as_components| as_components.as_serialized_batches())
99            .collect()
100    }
101}
102
103impl AsComponents for Vec<&dyn AsComponents> {
104    #[inline]
105    fn as_serialized_batches(&self) -> Vec<SerializedComponentBatch> {
106        self.iter()
107            .flat_map(|as_components| as_components.as_serialized_batches())
108            .collect()
109    }
110}
111
112impl AsComponents for Vec<Box<dyn AsComponents>> {
113    #[inline]
114    fn as_serialized_batches(&self) -> Vec<SerializedComponentBatch> {
115        self.iter()
116            .flat_map(|as_components| as_components.as_serialized_batches())
117            .collect()
118    }
119}
120
121// ---
122
123// NOTE: These needs to not be tests in order for doc-tests to work.
124
125/// ```compile_fail
126/// let comp = re_types_core::components::ClearIsRecursive::default();
127/// let _ = (&comp as &dyn re_types_core::AsComponents).as_serialized_batches();
128/// ```
129#[expect(dead_code)]
130#[expect(rustdoc::private_doc_tests)] // doc-tests are the only way to assert failed compilation
131fn single_ascomponents() {}
132
133/// ```compile_fail
134/// let comp = re_types_core::components::ClearIsRecursive::default();
135/// let _ = (&[comp] as &dyn re_types_core::AsComponents).as_serialized_batches();
136/// ```
137#[expect(dead_code)]
138#[expect(rustdoc::private_doc_tests)] // doc-tests are the only way to assert failed compilation
139fn single_ascomponents_wrapped() {
140    // This is non-sense (and more importantly: dangerous): a single component shouldn't be able to
141    // autocast straight to a collection of batches.
142}
143
144/// ```compile_fail
145/// let comp = re_types_core::components::ClearIsRecursive::default();
146/// let _ = (&[comp, comp, comp] as &dyn re_types_core::AsComponents).as_serialized_batches();
147/// ```
148#[expect(dead_code)]
149#[expect(rustdoc::private_doc_tests)] // doc-tests are the only way to assert failed compilation
150fn single_ascomponents_wrapped_many() {
151    // This is non-sense (and more importantly: dangerous): a single component shouldn't be able to
152    // autocast straight to a collection of batches.
153}
154
155/// ```compile_fail
156/// let comp = re_types_core::components::ClearIsRecursive::default();
157/// let comps = vec![comp, comp, comp];
158/// let _ = (&comps as &dyn re_types_core::AsComponents).as_serialized_batches();
159/// ```
160#[expect(dead_code)]
161#[expect(rustdoc::private_doc_tests)] // doc-tests are the only way to assert failed compilation
162fn many_ascomponents() {}
163
164/// ```compile_fail
165/// let comp = re_types_core::components::ClearIsRecursive::default();
166/// let comps = vec![comp, comp, comp];
167/// let _ = (&[comps] as &dyn re_types_core::AsComponents).as_serialized_batches();
168/// ```
169#[expect(dead_code)]
170#[expect(rustdoc::private_doc_tests)] // doc-tests are the only way to assert failed compilation
171fn many_ascomponents_wrapped() {}
172
173/// ```compile_fail
174/// let comp = re_types_core::components::ClearIsRecursive::default();
175/// let comps = vec![comp, comp, comp];
176/// let _ = (&[comps] as &dyn re_types_core::ComponentBatch).to_arrow();
177/// ```
178#[expect(dead_code)]
179#[expect(rustdoc::private_doc_tests)] // doc-tests are the only way to assert failed compilation
180fn many_componentbatch_wrapped() {}
181
182/// ```compile_fail
183/// let comp = re_types_core::components::ClearIsRecursive::default();
184/// let comps = vec![comp, comp, comp];
185/// let _ = (&[comps.clone(), comps.clone(), comps.clone()] as &dyn re_types_core::AsComponents).as_serialized_batches();
186/// ```
187#[expect(dead_code)]
188#[expect(rustdoc::private_doc_tests)] // doc-tests are the only way to assert failed compilation
189fn many_ascomponents_wrapped_many() {}
190
191/// ```compile_fail
192/// let comp = re_types_core::components::ClearIsRecursive::default();
193/// let comps = vec![comp, comp, comp];
194/// let _ = (&[comps.clone(), comps.clone(), comps.clone()] as &dyn re_types_core::ComponentBatch).to_arrow();
195/// ```
196#[expect(dead_code)]
197#[expect(rustdoc::private_doc_tests)] // doc-tests are the only way to assert failed compilation
198fn many_componentbatch_wrapped_many() {}
199
200#[cfg(test)]
201mod tests {
202    use std::sync::Arc;
203
204    use arrow::array::{
205        Array as ArrowArray, PrimitiveArray as ArrowPrimitiveArray, types::UInt32Type,
206    };
207    use itertools::Itertools as _;
208    use similar_asserts::assert_eq;
209
210    use crate::{Component as _, ComponentDescriptor};
211
212    #[derive(Clone, Copy, Debug, PartialEq, Eq, bytemuck::Pod, bytemuck::Zeroable)]
213    #[repr(transparent)]
214    pub struct MyColor(pub u32);
215
216    impl MyColor {
217        fn descriptor() -> ComponentDescriptor {
218            ComponentDescriptor {
219                archetype: Some("test".into()),
220                component: "color".into(),
221                component_type: Some(Self::name()),
222            }
223        }
224    }
225
226    crate::macros::impl_into_cow!(MyColor);
227
228    impl re_byte_size::SizeBytes for MyColor {
229        #[inline]
230        fn heap_size_bytes(&self) -> u64 {
231            let Self(_) = self;
232            0
233        }
234    }
235
236    impl crate::Loggable for MyColor {
237        fn arrow_datatype() -> arrow::datatypes::DataType {
238            arrow::datatypes::DataType::UInt32
239        }
240
241        fn to_arrow_opt<'a>(
242            data: impl IntoIterator<Item = Option<impl Into<std::borrow::Cow<'a, Self>>>>,
243        ) -> crate::SerializationResult<arrow::array::ArrayRef>
244        where
245            Self: 'a,
246        {
247            use crate::datatypes::UInt32;
248            UInt32::to_arrow_opt(
249                data.into_iter()
250                    .map(|opt| opt.map(Into::into).map(|c| UInt32(c.0))),
251            )
252        }
253
254        fn from_arrow_opt(
255            data: &dyn arrow::array::Array,
256        ) -> crate::DeserializationResult<Vec<Option<Self>>> {
257            use crate::datatypes::UInt32;
258            Ok(UInt32::from_arrow_opt(data)?
259                .into_iter()
260                .map(|opt| opt.map(|v| Self(v.0)))
261                .collect())
262        }
263    }
264
265    impl crate::Component for MyColor {
266        fn name() -> crate::ComponentType {
267            "example.MyColor".into()
268        }
269    }
270
271    fn data() -> (MyColor, MyColor, MyColor, Vec<MyColor>) {
272        let red = MyColor(0xDD0000FF);
273        let green = MyColor(0x00DD00FF);
274        let blue = MyColor(0x0000DDFF);
275        let colors = vec![red, green, blue];
276        (red, green, blue, colors)
277    }
278
279    #[test]
280    fn single_ascomponents_howto() {
281        let (red, _, _, _) = data();
282
283        let got = {
284            let red = &red as &dyn crate::ComponentBatch;
285            vec![red.try_serialized(MyColor::descriptor()).unwrap().array]
286        };
287        let expected = vec![
288            Arc::new(ArrowPrimitiveArray::<UInt32Type>::from(vec![red.0])) as Arc<dyn ArrowArray>,
289        ];
290        assert_eq!(&expected, &got);
291    }
292
293    #[test]
294    fn single_componentbatch() -> anyhow::Result<()> {
295        let (red, _, _, _) = data();
296
297        // A single component should autocast to a batch with a single instance.
298        let got = (&red as &dyn crate::ComponentBatch).to_arrow()?;
299        let expected =
300            Arc::new(ArrowPrimitiveArray::<UInt32Type>::from(vec![red.0])) as Arc<dyn ArrowArray>;
301        similar_asserts::assert_eq!(&expected, &got);
302
303        Ok(())
304    }
305
306    #[test]
307    fn single_ascomponents_wrapped_howto() {
308        let (red, _, _, _) = data();
309
310        let got = {
311            let red = &red as &dyn crate::ComponentBatch;
312            vec![red.try_serialized(MyColor::descriptor()).unwrap().array]
313        };
314        let expected = vec![
315            Arc::new(ArrowPrimitiveArray::<UInt32Type>::from(vec![red.0])) as Arc<dyn ArrowArray>,
316        ];
317        assert_eq!(&expected, &got);
318    }
319
320    #[test]
321    fn single_componentbatch_wrapped() -> anyhow::Result<()> {
322        let (red, _, _, _) = data();
323
324        // Nothing out of the ordinary here, a slice of components is indeed a batch.
325        let got = (&[red] as &dyn crate::ComponentBatch).to_arrow()?;
326        let expected =
327            Arc::new(ArrowPrimitiveArray::<UInt32Type>::from(vec![red.0])) as Arc<dyn ArrowArray>;
328        similar_asserts::assert_eq!(&expected, &got);
329
330        Ok(())
331    }
332
333    #[test]
334    fn single_ascomponents_wrapped_many_howto() {
335        let (red, green, blue, _) = data();
336
337        let got = {
338            let red = &red as &dyn crate::ComponentBatch;
339            let green = &green as &dyn crate::ComponentBatch;
340            let blue = &blue as &dyn crate::ComponentBatch;
341            [
342                red.try_serialized(MyColor::descriptor()).unwrap(),
343                green.try_serialized(MyColor::descriptor()).unwrap(),
344                blue.try_serialized(MyColor::descriptor()).unwrap(),
345            ]
346            .into_iter()
347            .map(|batch| batch.array)
348            .collect_vec()
349        };
350        let expected = vec![
351            Arc::new(ArrowPrimitiveArray::<UInt32Type>::from(vec![red.0])) as Arc<dyn ArrowArray>,
352            Arc::new(ArrowPrimitiveArray::<UInt32Type>::from(vec![green.0])) as Arc<dyn ArrowArray>,
353            Arc::new(ArrowPrimitiveArray::<UInt32Type>::from(vec![blue.0])) as Arc<dyn ArrowArray>,
354        ];
355        assert_eq!(&expected, &got);
356    }
357
358    #[test]
359    fn single_componentbatch_wrapped_many() -> anyhow::Result<()> {
360        let (red, green, blue, _) = data();
361
362        // Nothing out of the ordinary here, a slice of components is indeed a batch.
363        let got = (&[red, green, blue] as &dyn crate::ComponentBatch).to_arrow()?;
364        let expected = Arc::new(ArrowPrimitiveArray::<UInt32Type>::from(vec![
365            red.0, green.0, blue.0,
366        ])) as Arc<dyn ArrowArray>;
367        similar_asserts::assert_eq!(&expected, &got);
368
369        Ok(())
370    }
371
372    #[test]
373    fn many_componentbatch() -> anyhow::Result<()> {
374        let (red, green, blue, colors) = data();
375
376        // Nothing out of the ordinary here, a batch is indeed a batch.
377        let got = (&colors as &dyn crate::ComponentBatch).to_arrow()?;
378        let expected = Arc::new(ArrowPrimitiveArray::<UInt32Type>::from(vec![
379            red.0, green.0, blue.0,
380        ])) as Arc<dyn ArrowArray>;
381        similar_asserts::assert_eq!(&expected, &got);
382
383        Ok(())
384    }
385
386    #[test]
387    fn many_ascomponents_wrapped_howto() {
388        let (red, green, blue, colors) = data();
389
390        let got = {
391            let colors = &colors as &dyn crate::ComponentBatch;
392            vec![colors.try_serialized(MyColor::descriptor()).unwrap().array]
393        };
394        let expected = vec![Arc::new(ArrowPrimitiveArray::<UInt32Type>::from(vec![
395            red.0, green.0, blue.0,
396        ])) as Arc<dyn ArrowArray>];
397        assert_eq!(&expected, &got);
398    }
399
400    #[test]
401    fn many_ascomponents_wrapped_many_howto() {
402        let (red, green, blue, colors) = data();
403
404        // Nothing out of the ordinary here, a collection of batches is indeed a collection of batches.
405        let got = {
406            let colors = &colors as &dyn crate::ComponentBatch;
407            vec![
408                colors.try_serialized(MyColor::descriptor()).unwrap().array,
409                colors.try_serialized(MyColor::descriptor()).unwrap().array,
410                colors.try_serialized(MyColor::descriptor()).unwrap().array,
411            ]
412        };
413        let expected = vec![
414            Arc::new(ArrowPrimitiveArray::<UInt32Type>::from(vec![
415                red.0, green.0, blue.0,
416            ])) as Arc<dyn ArrowArray>,
417            Arc::new(ArrowPrimitiveArray::<UInt32Type>::from(vec![
418                red.0, green.0, blue.0,
419            ])) as Arc<dyn ArrowArray>,
420            Arc::new(ArrowPrimitiveArray::<UInt32Type>::from(vec![
421                red.0, green.0, blue.0,
422            ])) as Arc<dyn ArrowArray>,
423        ];
424        assert_eq!(&expected, &got);
425    }
426}