use std::collections::HashMap;
use std::sync::Arc;
use tonic::transport::Endpoint;
use crate::chain_handler::{ChainHandler, ChainHandlerBuildError, ChainHandlerBuilder};
use crate::proto::delivery_v1;
use crate::proto::delivery_v1::delivery_service_client::DeliveryServiceClient;
use crate::{Domain, TridentClient};
#[derive(Debug, thiserror::Error)]
pub enum TridentClientBuilderError {
#[error("Failed to connect to delivery service: {0}")]
Transport(#[from] tonic::transport::Error),
#[error("Grpc: {0}")]
Grpc(#[from] tonic::Status),
#[error("Missing deposit_contract for domain {0}")]
MissingDepositContract(Domain),
#[error("Missing delivery_contract for domain {0}")]
MissingDeliveryContract(Domain),
#[error(transparent)]
ChainHandlerBuild(#[from] ChainHandlerBuildError),
#[error("No deployment returned for registered domain {domain}")]
UnregisteredDeployment { domain: Domain },
}
pub struct TridentClientBuilder {
endpoint: Endpoint,
pending: HashMap<Domain, Box<dyn ChainHandlerBuilder>>,
expiry_margins: HashMap<Domain, u64>,
}
impl TridentClientBuilder {
pub fn new(endpoint: Endpoint) -> Self {
Self {
endpoint,
pending: HashMap::new(),
expiry_margins: HashMap::new(),
}
}
pub(crate) fn with_chain<B>(mut self, domain: Domain, builder: B) -> Self
where
B: ChainHandlerBuilder + 'static,
{
self.pending.insert(domain, Box::new(builder));
self
}
pub fn with_expiry_margin(mut self, domain: Domain, margin: u64) -> Self {
self.expiry_margins.insert(domain, margin);
self
}
pub async fn connect(self) -> Result<TridentClient, TridentClientBuilderError> {
let channel = self.endpoint.connect().await?;
let mut delivery = DeliveryServiceClient::new(channel);
let deployments = delivery
.list_deployments(delivery_v1::ListDeploymentsRequest {})
.await?
.into_inner();
let mut pending = self.pending;
let mut chains: HashMap<Domain, Arc<dyn ChainHandler>> = HashMap::new();
for deployment in deployments.deployments {
let Some(builder) = pending.remove(&deployment.domain) else {
continue;
};
let deposit_contract = deployment.deposit_contract.ok_or(
TridentClientBuilderError::MissingDepositContract(deployment.domain),
)?;
let delivery_contract = deployment.delivery_contract.ok_or(
TridentClientBuilderError::MissingDeliveryContract(deployment.domain),
)?;
let handler = builder.build(delivery_contract, deposit_contract).await?;
chains.insert(deployment.domain, Arc::from(handler));
}
if let Some(&domain) = pending.keys().next() {
return Err(TridentClientBuilderError::UnregisteredDeployment { domain });
}
Ok(TridentClient {
chains,
delivery,
expiry_margins: self.expiry_margins,
})
}
}