use std::collections::HashSet;
use std::sync::Arc;
use crate::core::auth::{Identity, IdentityProvider};
use crate::protocol::connection::{split_single_stream, CallConnection};
use crate::protocol::dispatch::Dispatcher;
use crate::registry::discovery::install_bootstrap_discovery;
use crate::registry::registration::{HandlerRegistration, OperationRegistry};
use super::operations::ChannelCore;
use super::relay::ChannelRelay;
use super::{adapter::InstallChannelZero, policy::ChannelLifecyclePolicy};
#[derive(Clone, Default)]
pub struct HubLegImports {
marked: Vec<HandlerRegistration>,
plain: Vec<HandlerRegistration>,
}
impl HubLegImports {
pub fn from_bundles(bundles: Vec<HandlerRegistration>) -> Self {
let mut imports = Self::default();
for bundle in bundles {
if bundle.spec.channel_open.is_some() {
imports.marked.push(bundle);
} else {
imports.plain.push(bundle);
}
}
imports
}
pub fn marked(&self) -> &[HandlerRegistration] {
&self.marked
}
pub fn plain(&self) -> &[HandlerRegistration] {
&self.plain
}
#[must_use]
pub fn filtered(self, keep: impl Fn(&str) -> bool) -> Self {
Self {
marked: self
.marked
.into_iter()
.filter(|b| keep(&b.spec.name))
.collect(),
plain: self
.plain
.into_iter()
.filter(|b| keep(&b.spec.name))
.collect(),
}
}
#[must_use]
pub fn only(self, names: &[&str]) -> Self {
let allowed: HashSet<&str> = names.iter().copied().collect();
self.filtered(|name| allowed.contains(name))
}
}
pub struct HubLegTemplate {
relay: Arc<ChannelRelay>,
imports: HubLegImports,
policy: Arc<dyn ChannelLifecyclePolicy>,
identity_provider: Arc<dyn IdentityProvider>,
identity: Option<Identity>,
}
impl HubLegTemplate {
pub fn new(relay: ChannelRelay, imports: HubLegImports) -> Self {
Self {
relay: Arc::new(relay),
imports,
policy: super::policy::default_policy(),
identity_provider: Arc::new(crate::core::auth::NoopIdentityProvider),
identity: None,
}
}
pub fn with_policy(mut self, policy: Arc<dyn ChannelLifecyclePolicy>) -> Self {
self.policy = policy;
self
}
pub fn with_identity_provider(mut self, provider: Arc<dyn IdentityProvider>) -> Self {
self.identity_provider = provider;
self
}
pub fn with_identity(mut self, identity: Identity) -> Self {
self.identity = Some(identity);
self
}
pub fn install_hook(&self) -> InstallChannelZero {
let relay = Arc::clone(&self.relay);
let imports = self.imports.clone();
let policy = Arc::clone(&self.policy);
let identity_provider = Arc::clone(&self.identity_provider);
let identity = self.identity.clone();
Arc::new(move |consumer_manager, channel0_conn, auth| {
let relay = Arc::clone(&relay);
let imports = imports.clone();
let policy = Arc::clone(&policy);
let identity_provider = Arc::clone(&identity_provider);
let identity = identity.clone();
tokio::spawn(async move {
if let Some(identity) = identity {
let _ = channel0_conn.set_identity(identity);
}
let Ok(channel0_bidi) = channel0_conn.accept_bi().await else {
return;
};
let (writer, reader) = split_single_stream(channel0_bidi);
let registry = Arc::new(OperationRegistry::new());
let operations = super::operations::ChannelOperations::new(
consumer_manager.clone(),
Arc::clone(&policy),
);
if let Err(e) = operations.register_on(®istry) {
tracing::warn!(error = %e, "hub-leg template: generic channel ops failed to register");
return;
}
let consumer_core = ChannelCore::new(consumer_manager, Arc::clone(&policy));
for bundle in &imports.plain {
if crate::registry::discovery::BOOTSTRAP_DISCOVERY_OPS
.contains(&bundle.spec.name.as_str())
{
continue;
}
if let Err(e) = registry.register(bundle.clone()) {
tracing::warn!(error = %e, "hub-leg template: plain bundle registration failed");
return;
}
}
let install_auth = auth.clone();
for bundle in &imports.marked {
if let Err(e) = relay.register_relay_openable(
&consumer_core,
®istry,
bundle.spec.clone(),
install_auth.clone(),
) {
tracing::warn!(error = %e, "hub-leg template: relay openable registration failed");
return;
}
}
if let Err(e) = install_bootstrap_discovery(®istry) {
tracing::warn!(error = %e, "hub-leg template: bootstrap discovery install failed");
return;
}
let call_connection = Arc::new(CallConnection::new_single_stream(
channel0_conn,
Arc::clone(&writer),
));
Dispatcher::new(registry, identity_provider)
.run_loop_single_stream(call_connection, reader, writer)
.await;
})
})
}
}
#[cfg(test)]
#[path = "hub_leg_tests.rs"]
mod tests;
#[cfg(test)]
#[path = "gate2_tests.rs"]
mod gate2_tests;