re_viewer_context/component_fallbacks.rs
1use arrow::array::{ArrayRef, NullArray};
2
3use re_types::{ComponentDescriptor, ComponentType};
4
5use crate::QueryContext;
6
7/// Result for a fallback request to a provider.
8pub enum ComponentFallbackProviderResult {
9 /// A fallback value was successfully provided.
10 Value(ArrayRef),
11
12 /// The fallback provider is not able to handle the given component.
13 ///
14 /// This is not treated as an error and should be handled by looking up a placeholder value.
15 ComponentNotHandled,
16
17 /// Arrow serialization failed.
18 ///
19 /// Unlike [`ComponentFallbackProviderResult::ComponentNotHandled`], this is treated as an unexpected error.
20 SerializationError(re_types::SerializationError),
21}
22
23impl<T: re_types::ComponentBatch> From<T> for ComponentFallbackProviderResult {
24 fn from(batch: T) -> Self {
25 match batch.to_arrow() {
26 Ok(value) => Self::Value(value),
27 Err(err) => Self::SerializationError(err),
28 }
29 }
30}
31
32/// Error type for a fallback request.
33#[derive(thiserror::Error, Debug)]
34pub enum ComponentFallbackError {
35 /// Not directly returned by the fallback provider, but useful when serializing a fallback value.
36 #[error("Fallback value turned up to be empty when we expected a value.")]
37 UnexpectedEmptyFallback,
38}
39
40/// Provides fallback values for components, implemented typically by [`crate::ViewClass`] and [`crate::VisualizerSystem`].
41///
42/// Fallbacks can be based on arbitrarily complex & context sensitive heuristics.
43pub trait ComponentFallbackProvider {
44 /// Tries to provide a fallback value for a given component.
45 ///
46 /// If the provider can't handle the component or simply want to use a placeholder value,
47 /// it should return [`ComponentFallbackProviderResult::ComponentNotHandled`].
48 ///
49 /// Fallbacks can be based on arbitrarily complex & context sensitive heuristics.
50 fn try_provide_fallback(
51 &self,
52 ctx: &QueryContext<'_>,
53 component: ComponentType,
54 ) -> ComponentFallbackProviderResult;
55
56 /// Provides a fallback value for a given component, first trying the provider and
57 /// then falling back to the placeholder value registered in the viewer context.
58 fn fallback_for(
59 &self,
60 ctx: &QueryContext<'_>,
61 component_descr: &ComponentDescriptor,
62 ) -> ArrayRef {
63 let Some(component_type) = component_descr.component_type else {
64 re_log::warn!(
65 "Requested fallback for component descr {component_descr} without component type"
66 );
67 return std::sync::Arc::new(NullArray::new(0));
68 };
69
70 match self.try_provide_fallback(ctx, component_type) {
71 ComponentFallbackProviderResult::Value(value) => {
72 return value;
73 }
74 ComponentFallbackProviderResult::SerializationError(err) => {
75 // We still want to provide the base fallback value so we can move on,
76 // but arrow serialization should never fail.
77 // Giving out _both_ the error and the fallback value gets messy,
78 // so given that this should be a rare bug, we log it and return the fallback value as success.
79 re_log::error_once!(
80 "Arrow serialization failed trying to provide a fallback for {component_type}. Using base fallback instead: {err}"
81 );
82 }
83 ComponentFallbackProviderResult::ComponentNotHandled => {}
84 }
85
86 ctx.viewer_ctx().placeholder_for(component_type)
87 }
88}
89
90/// Provides a fallback value for a given component with known type.
91///
92/// Use the [`crate::impl_component_fallback_provider`] macro to build a [`ComponentFallbackProvider`]
93/// out of several strongly typed [`TypedComponentFallbackProvider`]s.
94pub trait TypedComponentFallbackProvider<C: re_types::Component> {
95 fn fallback_for(&self, ctx: &QueryContext<'_>) -> C;
96}
97
98/// Implements the [`ComponentFallbackProvider`] trait for a given type, using a number of [`TypedComponentFallbackProvider`].
99///
100/// Usage examples:
101/// ```ignore
102/// impl_component_fallback_provider!(MySystem => []); // Empty fallback provider
103/// impl_component_fallback_provider!(MySystem => [Color, Text]); // Fallback provider handling the Color and Text components.
104/// ```
105#[macro_export]
106macro_rules! impl_component_fallback_provider {
107 ($type:ty => [$($component:ty),*]) => {
108 impl $crate::ComponentFallbackProvider for $type {
109 fn try_provide_fallback(
110 &self,
111 _ctx: &$crate::QueryContext<'_>,
112 _component_type: re_types::ComponentType,
113 ) -> $crate::ComponentFallbackProviderResult {
114 $(
115 if _component_type == <$component as re_types::Component>::name() {
116 return $crate::TypedComponentFallbackProvider::<$component>::fallback_for(self, _ctx).into();
117 }
118 )*
119 $crate::ComponentFallbackProviderResult::ComponentNotHandled
120 }
121 }
122 };
123}