use futures_util::future::BoxFuture;
use super::{
validate_bootstrap_contract_state, BootstrapBinding, BootstrapContractRef, ServerError,
ServiceResourceBindings,
};
pub trait BootstrapBindingInfo: Clone + Send + Sync {
fn bootstrap_binding(&self) -> BootstrapBinding;
fn resource_bindings(&self) -> ServiceResourceBindings {
ServiceResourceBindings::default()
}
}
impl BootstrapBindingInfo for BootstrapBinding {
fn bootstrap_binding(&self) -> BootstrapBinding {
self.clone()
}
}
pub trait CoreBootstrapPort: Send + Sync {
type Binding: BootstrapBindingInfo;
fn fetch_catalog_contracts<'a>(
&'a self,
) -> BoxFuture<'a, Result<Vec<BootstrapContractRef>, ServerError>>;
fn fetch_binding<'a>(
&'a self,
expected: &'a BootstrapContractRef,
) -> BoxFuture<'a, Result<Option<Self::Binding>, ServerError>>;
}
pub async fn resolve_bootstrap_binding<C>(
service_name: &str,
expected: &BootstrapContractRef,
core: &C,
) -> Result<C::Binding, ServerError>
where
C: CoreBootstrapPort,
{
let catalog = core.fetch_catalog_contracts().await?;
let is_active = catalog
.iter()
.any(|contract| contract.id == expected.id && contract.digest == expected.digest);
if !is_active {
return Err(ServerError::BootstrapInactiveContract {
service_name: service_name.to_string(),
contract_id: expected.id.clone(),
contract_digest: expected.digest.clone(),
});
}
let binding = core.fetch_binding(expected).await?;
let validated_binding = binding
.as_ref()
.map(BootstrapBindingInfo::bootstrap_binding);
validate_bootstrap_contract_state(
service_name,
expected,
&catalog,
validated_binding.as_ref(),
)?;
binding.ok_or_else(|| ServerError::BootstrapMissingBinding {
service_name: service_name.to_string(),
contract_id: expected.id.clone(),
contract_digest: expected.digest.clone(),
})
}