use std::sync::Arc;
use std::time::Duration;
use axum::http::HeaderMap;
use brain_db_sdk::BrainClient;
use crate::config::EdgeConfig;
use crate::error::ApiError;
use crate::pool::{BrainPool, BrainPoolConfig};
use crate::port::{
BearerResolver, CredentialResolver, MeterEvent, MeteringSink, NoopMeter, Outcome,
ResolvedCredential,
};
#[derive(Clone)]
pub struct EdgeState {
inner: Arc<Inner>,
}
impl std::fmt::Debug for EdgeState {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("EdgeState")
.field("pool", &self.inner.pool)
.finish_non_exhaustive()
}
}
struct Inner {
pool: BrainPool,
resolver: Arc<dyn CredentialResolver>,
meter: Arc<dyn MeteringSink>,
}
impl EdgeState {
#[must_use]
pub fn new(config: EdgeConfig) -> Self {
Self::with_ports(config, Arc::new(BearerResolver), Arc::new(NoopMeter))
}
#[must_use]
pub fn with_ports(
config: EdgeConfig,
resolver: Arc<dyn CredentialResolver>,
meter: Arc<dyn MeteringSink>,
) -> Self {
let pool = BrainPool::new(BrainPoolConfig {
brain_addr: config.brain_addr,
pool_size: config.pool_size,
max_credentials: config.max_credentials,
idle_ttl: Duration::from_secs(config.idle_ttl_secs),
});
Self {
inner: Arc::new(Inner {
pool,
resolver,
meter,
}),
}
}
pub async fn resolve(&self, headers: &HeaderMap) -> Result<ResolvedCredential, ApiError> {
self.inner.resolver.resolve(headers).await
}
pub fn record(&self, op: &str, ident: &ResolvedCredential, outcome: Outcome) {
self.inner.meter.record(&MeterEvent {
tenant: ident.tenant.as_deref(),
op,
outcome,
});
}
pub async fn client_for(&self, credential: &str) -> Result<Arc<BrainClient>, ApiError> {
self.inner
.pool
.client_for(credential)
.await
.map_err(|e| ApiError::from_brain(&e))
}
#[must_use]
pub fn pool(&self) -> &BrainPool {
&self.inner.pool
}
#[must_use]
pub fn meter(&self) -> Arc<dyn MeteringSink> {
Arc::clone(&self.inner.meter)
}
}