use crate::descriptor::{ConnectorDescriptor, ConnectorKind, ControlCapabilities, RawBoundary};
use crate::open::{OpenConnection, PendingRawConnection};
use crate::traits::Connector;
use monoloop_contracts::{ConnectorError, ConnectorErrorKind};
use std::collections::HashMap;
use std::sync::Arc;
#[derive(Clone, Debug)]
pub enum ProxyRoute {
EndpointPrefix,
Fixed(String),
}
pub struct ConnectorProxyBuilder {
routes: HashMap<String, Arc<dyn Connector>>,
default_backend: Option<String>,
route: ProxyRoute,
}
impl ConnectorProxyBuilder {
pub fn new() -> Self {
Self {
routes: HashMap::new(),
default_backend: None,
route: ProxyRoute::EndpointPrefix,
}
}
pub fn register(mut self, name: impl Into<String>, connector: Arc<dyn Connector>) -> Self {
self.routes.insert(name.into(), connector);
self
}
pub fn default_backend(mut self, name: impl Into<String>) -> Self {
self.default_backend = Some(name.into());
self
}
pub fn route(mut self, route: ProxyRoute) -> Self {
self.route = route;
self
}
pub fn build(self) -> Result<ConnectorProxy, ConnectorError> {
if self.routes.is_empty() {
return Err(ConnectorError::configuration_invalid(
"connector proxy requires at least one backend",
));
}
if let ProxyRoute::Fixed(ref name) = self.route {
if !self.routes.contains_key(name) {
return Err(ConnectorError::configuration_invalid(
"fixed proxy route names unknown backend",
));
}
}
if let Some(ref name) = self.default_backend {
if !self.routes.contains_key(name) {
return Err(ConnectorError::configuration_invalid(
"default backend not registered",
));
}
}
Ok(ConnectorProxy {
descriptor: ConnectorDescriptor {
connector_kind: ConnectorKind::Other("proxy".into()),
implementation_id: "monoloop.connector_proxy".into(),
implementation_version: env!("CARGO_PKG_VERSION").into(),
transport_kind: "proxy".into(),
supported_dialects: vec!["delegated".into()],
raw_boundary: RawBoundary::InProcess,
control_capabilities: ControlCapabilities::default(),
},
routes: self.routes,
default_backend: self.default_backend,
route: self.route,
})
}
}
impl Default for ConnectorProxyBuilder {
fn default() -> Self {
Self::new()
}
}
pub struct ConnectorProxy {
descriptor: ConnectorDescriptor,
routes: HashMap<String, Arc<dyn Connector>>,
default_backend: Option<String>,
route: ProxyRoute,
}
impl ConnectorProxy {
pub fn builder() -> ConnectorProxyBuilder {
ConnectorProxyBuilder::new()
}
fn resolve(&self, request: &OpenConnection) -> Result<(String, String), ConnectorError> {
match &self.route {
ProxyRoute::Fixed(name) => Ok((name.clone(), request.endpoint_ref.clone())),
ProxyRoute::EndpointPrefix => {
if let Some((name, rest)) = request.endpoint_ref.split_once(':') {
if !name.contains('/') && self.routes.contains_key(name) {
return Ok((name.to_string(), rest.to_string()));
}
}
if let Some(ref default) = self.default_backend {
return Ok((default.clone(), request.endpoint_ref.clone()));
}
Err(ConnectorError::configuration_invalid(
"no backend route in endpoint_ref and no default_backend",
))
}
}
}
}
impl Connector for ConnectorProxy {
fn descriptor(&self) -> &ConnectorDescriptor {
&self.descriptor
}
fn begin_open(&self, mut request: OpenConnection) -> PendingRawConnection {
match self.resolve(&request) {
Ok((backend_name, endpoint)) => {
let Some(backend) = self.routes.get(&backend_name) else {
return failed_pending(
request.connection_id.clone(),
ConnectorError::new(
ConnectorErrorKind::ConfigurationInvalid,
"unknown connector backend",
)
.with_connection_id(request.connection_id.as_str()),
);
};
request.endpoint_ref = endpoint;
backend.begin_open(request)
}
Err(err) => failed_pending(
request.connection_id.clone(),
err.with_connection_id(request.connection_id.as_str()),
),
}
}
}
fn failed_pending(
connection_id: monoloop_contracts::ConnectionId,
err: ConnectorError,
) -> PendingRawConnection {
use crate::control::{ConnectionControlHandle, ControlState};
let state = ControlState::new();
let control = ConnectionControlHandle::new(state);
let opened = Box::pin(async move { Err(err) });
PendingRawConnection {
connection_id,
control,
opened,
}
}