use super::{
Capabilities, CapabilityRegistry, ConsumedTracker, Error, ResolvedLocalEntry,
ResolvedSharedEntry,
};
use otel_arrow_dfe_config::ExtensionId;
use std::any::TypeId;
use std::collections::{HashMap, HashSet};
pub(crate) fn resolve_bindings(
bindings: &HashMap<otel_arrow_dfe_config::CapabilityId, ExtensionId>,
registry: &CapabilityRegistry,
known_extensions: &HashSet<ExtensionId>,
tracker: &mut ConsumedTracker,
) -> Result<Capabilities, Error> {
let known_caps: HashMap<&str, &crate::capability::KnownCapability> =
crate::capability::KNOWN_CAPABILITIES
.iter()
.map(|kc| (kc.name, kc))
.collect();
let mut local_entries: HashMap<TypeId, ResolvedLocalEntry> = HashMap::new();
let mut shared_entries: HashMap<TypeId, ResolvedSharedEntry> = HashMap::new();
let mut sorted_bindings: Vec<(&otel_arrow_dfe_config::CapabilityId, &ExtensionId)> =
bindings.iter().collect();
sorted_bindings.sort_unstable_by(|(a, _), (b, _)| a.as_ref().cmp(b.as_ref()));
for (cap_name, ext_name) in sorted_bindings {
let cap_name_str: &str = cap_name.as_ref();
let ext_name_str: &str = ext_name.as_ref();
if !known_extensions.contains(ext_name_str) {
return Err(Error::ConfigError(Box::new(
otel_arrow_dfe_config::error::Error::InvalidUserConfig {
error: format!(
"capability binding '{cap_name_str}': no extension named '{ext_name_str}' exists",
),
},
)));
}
let known_cap = known_caps.get(cap_name_str).ok_or_else(|| {
let mut known_names: Vec<&str> = known_caps.keys().copied().collect();
known_names.sort_unstable();
Error::ConfigError(Box::new(
otel_arrow_dfe_config::error::Error::InvalidUserConfig {
error: format!(
"unknown capability '{cap_name_str}'. Known capabilities: {known_names:?}",
),
},
))
})?;
let cap_type_id = (known_cap.type_id)();
let has_native_local = registry.has_native_local(&cap_type_id);
let has_shared = registry.has_shared(&cap_type_id);
if !has_native_local && !has_shared {
return Err(Error::ConfigError(Box::new(
otel_arrow_dfe_config::error::Error::InvalidUserConfig {
error: format!("capability '{cap_name_str}': no loaded extension provides it",),
},
)));
}
let local_entry = registry.get_local(&cap_type_id, ext_name_str);
let shared_entry = registry.get_shared(&cap_type_id, ext_name_str);
if local_entry.is_none() && shared_entry.is_none() {
return Err(Error::ConfigError(Box::new(
otel_arrow_dfe_config::error::Error::InvalidUserConfig {
error: format!(
"capability '{cap_name_str}': extension '{ext_name_str}' does not provide it",
),
},
)));
}
if let Some(local_entry) = local_entry {
let tracker_consumed = tracker.ensure_local_consumer_slot(
cap_type_id,
known_cap.name,
local_entry.extension_id.clone(),
);
let prior = local_entries.insert(
cap_type_id,
ResolvedLocalEntry {
extension_id: local_entry.extension_id.clone(),
produce: std::cell::Cell::new(Some(local_entry.produce.clone_box())),
tracker_consumed,
},
);
debug_assert!(
prior.is_none(),
"resolve_bindings: duplicate local entry for capability '{cap_name_str}' \
- the config layer should prevent two bindings with the same capability name",
);
}
if let Some(shared_entry) = shared_entry {
let tracker_consumed = tracker.ensure_shared_consumer_slot(
cap_type_id,
known_cap.name,
shared_entry.extension_id.clone(),
);
let prior = shared_entries.insert(
cap_type_id,
ResolvedSharedEntry {
extension_id: shared_entry.extension_id.clone(),
produce: std::cell::Cell::new(Some(shared_entry.produce.clone_box())),
tracker_consumed,
adapt_as_local: shared_entry.adapt_as_local,
},
);
debug_assert!(
prior.is_none(),
"resolve_bindings: duplicate shared entry for capability '{cap_name_str}'",
);
}
}
Ok(Capabilities::new(local_entries, shared_entries))
}