1use std::collections::HashMap;
2use std::sync::Arc;
3
4use tonic::transport::Endpoint;
5
6use crate::chain_handler::{ChainHandler, ChainHandlerBuildError, ChainHandlerBuilder};
7use crate::proto::delivery_v1;
8use crate::proto::delivery_v1::delivery_service_client::DeliveryServiceClient;
9use crate::{Domain, TridentClient};
10
11#[derive(Debug, thiserror::Error)]
18pub enum TridentClientBuilderError {
19 #[error("Failed to connect to delivery service: {0}")]
21 Transport(#[from] tonic::transport::Error),
22
23 #[error("Grpc: {0}")]
25 Grpc(#[from] tonic::Status),
26
27 #[error("Missing deposit_contract for domain {0}")]
30 MissingDepositContract(Domain),
31
32 #[error("Missing delivery_contract for domain {0}")]
35 MissingDeliveryContract(Domain),
36
37 #[error(transparent)]
40 ChainHandlerBuild(#[from] ChainHandlerBuildError),
41
42 #[error("No deployment returned for registered domain {domain}")]
45 UnregisteredDeployment { domain: Domain },
46}
47
48pub struct TridentClientBuilder {
54 endpoint: Endpoint,
55 pending: HashMap<Domain, Box<dyn ChainHandlerBuilder>>,
56 expiry_margins: HashMap<Domain, u64>,
57}
58
59impl TridentClientBuilder {
60 pub fn new(endpoint: Endpoint) -> Self {
63 Self {
64 endpoint,
65 pending: HashMap::new(),
66 expiry_margins: HashMap::new(),
67 }
68 }
69
70 pub(crate) fn with_chain<B>(mut self, domain: Domain, builder: B) -> Self
75 where
76 B: ChainHandlerBuilder + 'static,
77 {
78 self.pending.insert(domain, Box::new(builder));
79 self
80 }
81
82 pub fn with_expiry_margin(mut self, domain: Domain, margin: u64) -> Self {
87 self.expiry_margins.insert(domain, margin);
88 self
89 }
90
91 pub async fn connect(self) -> Result<TridentClient, TridentClientBuilderError> {
100 let channel = self.endpoint.connect().await?;
101 let mut delivery = DeliveryServiceClient::new(channel);
102 let deployments = delivery
103 .list_deployments(delivery_v1::ListDeploymentsRequest {})
104 .await?
105 .into_inner();
106
107 let mut pending = self.pending;
108 let mut chains: HashMap<Domain, Arc<dyn ChainHandler>> = HashMap::new();
109
110 for deployment in deployments.deployments {
111 let Some(builder) = pending.remove(&deployment.domain) else {
112 continue;
113 };
114 let deposit_contract = deployment.deposit_contract.ok_or(
115 TridentClientBuilderError::MissingDepositContract(deployment.domain),
116 )?;
117 let delivery_contract = deployment.delivery_contract.ok_or(
118 TridentClientBuilderError::MissingDeliveryContract(deployment.domain),
119 )?;
120
121 let handler = builder.build(delivery_contract, deposit_contract).await?;
122 chains.insert(deployment.domain, Arc::from(handler));
123 }
124
125 if let Some(&domain) = pending.keys().next() {
126 return Err(TridentClientBuilderError::UnregisteredDeployment { domain });
127 }
128
129 Ok(TridentClient {
130 chains,
131 delivery,
132 expiry_margins: self.expiry_margins,
133 })
134 }
135}