use std::borrow::Cow;
use ahash::HashMap;
use arrow::array::{ArrayRef, NullArray};
use nohash_hasher::IntMap;
use re_sdk_types::{Component, ComponentType, SerializationError, ViewClassIdentifier};
use re_types_core::ComponentIdentifier;
use crate::QueryContext;
pub fn typed_fallback_for<C: Component>(
query_context: &QueryContext<'_>,
component: ComponentIdentifier,
) -> C {
let array = query_context
.viewer_ctx()
.component_fallback_registry()
.fallback_for(
&re_types_core::ComponentDescriptor::partial(component).with_component_type(C::name()),
query_context,
);
let Some(v) = C::from_arrow(&array)
.ok()
.and_then(|v| v.into_iter().next())
else {
panic!("Invalid fallback provider for `{component}`, failed deserializing result.",);
};
v
}
#[derive(thiserror::Error, Debug)]
pub enum ComponentFallbackError {
#[error("Fallback value turned up to be empty when we expected a value.")]
UnexpectedEmptyFallback,
}
type ComponentFallbackProviderFn =
Box<dyn Fn(&QueryContext<'_>) -> Result<ArrayRef, SerializationError> + Send + Sync + 'static>;
#[derive(Default)]
pub struct FallbackProviderRegistry {
view_component_fallback_providers:
HashMap<(ViewClassIdentifier, ComponentIdentifier), ComponentFallbackProviderFn>,
component_fallback_providers: IntMap<ComponentIdentifier, ComponentFallbackProviderFn>,
type_fallback_providers: IntMap<ComponentType, ComponentFallbackProviderFn>,
}
impl FallbackProviderRegistry {
pub fn register_dyn_type_fallback_provider(
&mut self,
component: ComponentType,
provider: ComponentFallbackProviderFn,
) {
if self
.type_fallback_providers
.insert(component, provider)
.is_some()
{
re_log::warn!(
"There was already a component fallback provider registered for {component}"
);
}
}
pub fn register_type_fallback_provider<C: re_sdk_types::Component>(
&mut self,
f: impl Fn(&QueryContext<'_>) -> C + Send + Sync + 'static,
) {
self.register_dyn_type_fallback_provider(
C::name(),
Box::new(move |query_context| {
let value = f(query_context);
C::to_arrow([Cow::Owned(value)])
}),
);
}
pub fn register_dyn_component_fallback_provider(
&mut self,
component: ComponentIdentifier,
provider: ComponentFallbackProviderFn,
) {
if self
.component_fallback_providers
.insert(component, provider)
.is_some()
{
re_log::warn!(
"There was already a component fallback provider registered for {component}"
);
}
}
pub fn register_component_fallback_provider<C: re_sdk_types::Component>(
&mut self,
component: ComponentIdentifier,
provider: impl Fn(&QueryContext<'_>) -> C + Send + Sync + 'static,
) {
self.register_dyn_component_fallback_provider(
component,
Box::new(move |query_context| {
let value = provider(query_context);
C::to_arrow([Cow::Owned(value)])
}),
);
}
pub fn register_dyn_view_fallback_provider(
&mut self,
view: ViewClassIdentifier,
component: ComponentIdentifier,
provider: ComponentFallbackProviderFn,
) {
if self
.view_component_fallback_providers
.insert((view, component), provider)
.is_some()
{
re_log::warn!(
"There was already a view component fallback provider registered for {component} in {view}"
);
}
}
pub fn register_view_fallback_provider<C: re_sdk_types::Component>(
&mut self,
view: ViewClassIdentifier,
component: ComponentIdentifier,
provider: impl Fn(&QueryContext<'_>) -> C + Send + Sync + 'static,
) {
self.register_dyn_view_fallback_provider(
view,
component,
Box::new(move |query_context| {
let value = provider(query_context);
C::to_arrow([Cow::Owned(value)])
}),
);
}
pub fn register_view_array_fallback_provider<
C: re_sdk_types::Component,
I: IntoIterator<Item = C>,
>(
&mut self,
view: ViewClassIdentifier,
component: ComponentIdentifier,
provider: impl Fn(&QueryContext<'_>) -> I + Send + Sync + 'static,
) {
self.register_dyn_view_fallback_provider(
view,
component,
Box::new(move |query_context| {
let values = provider(query_context);
C::to_arrow(values.into_iter().map(Cow::Owned))
}),
);
}
fn get_fallback_function<'a>(
&'a self,
component: ComponentIdentifier,
component_type: Option<ComponentType>,
ctx: &QueryContext<'_>,
) -> Option<&'a ComponentFallbackProviderFn> {
if let Some(f) = self
.view_component_fallback_providers
.get(&(ctx.view_ctx.view_class_identifier, component))
{
return Some(f);
}
if let Some(f) = self.component_fallback_providers.get(&component) {
return Some(f);
}
if let Some(ty) = component_type
&& let Some(f) = self.type_fallback_providers.get(&ty)
{
return Some(f);
}
None
}
pub fn fallback_for(
&self,
descr: &re_types_core::ComponentDescriptor,
ctx: &QueryContext<'_>,
) -> ArrayRef {
let component = descr.component;
let component_type = descr.component_type;
re_tracing::profile_function!(component);
let res = self
.get_fallback_function(component, component_type, ctx)
.map(|f| f(ctx));
match res {
Some(Ok(array)) => return array,
Some(Err(err)) => {
re_log::error_once!(
"Arrow serialization failed trying to provide a fallback for {component}. Using base fallback instead: {err}"
);
}
None => {}
}
if let Some(ty) = component_type {
placeholder_for(ctx, component, ty)
} else {
re_log::warn_once!(
"Requested fallback for component {component} without component type"
);
std::sync::Arc::new(NullArray::new(0))
}
}
}
fn placeholder_for(
ctx: &QueryContext<'_>,
component_identifier: ComponentIdentifier,
component: re_chunk::ComponentType,
) -> ArrayRef {
let viewer_ctx = ctx.viewer_ctx();
let datatype = if let Some(reflection) = viewer_ctx.reflection().components.get(&component) {
if let Some(placeholder) = reflection.custom_placeholder.as_ref() {
return placeholder.clone();
}
reflection.datatype.clone()
} else {
let entity_path = ctx.target_entity_path;
viewer_ctx.recording_engine()
.schema().lookup_component_type(entity_path, component_identifier)
.or_else(|| viewer_ctx.blueprint_engine().schema().lookup_component_type(entity_path, component_identifier))
.map(|(_component_type, datatype)| datatype)
.unwrap_or_else(|| {
re_log::error_once!("Could not find datatype for component {component}. Using null array as placeholder.");
arrow::datatypes::DataType::Null})
};
re_sdk_types::reflection::generic_placeholder_for_datatype(&datatype)
}