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}