use crate::{
cdk::types::Principal,
infra::ic::{IcInfraError, call::Call, known::NNS_REGISTRY_CANISTER},
log,
log::Topic,
};
use candid::CandidType;
use serde::Deserialize;
use thiserror::Error as ThisError;
#[derive(CandidType, Debug, Deserialize)]
pub struct GetSubnetForCanisterRequest {
pub principal: Principal,
}
#[derive(CandidType, Debug, Deserialize)]
pub struct GetSubnetForCanisterPayload {
pub subnet_id: Option<Principal>,
}
#[derive(Debug, ThisError)]
pub enum NnsRegistryInfraError {
#[error("NNS registry rejected the request: {reason}")]
Rejected { reason: String },
}
pub struct NnsRegistryInfra;
impl NnsRegistryInfra {
pub async fn get_subnet_for_canister(
pid: Principal,
) -> Result<Option<Principal>, IcInfraError> {
let request = GetSubnetForCanisterRequest { principal: pid };
let result = Call::unbounded_wait(*NNS_REGISTRY_CANISTER, "get_subnet_for_canister")
.with_arg(request)?
.execute()
.await?
.candid::<Result<GetSubnetForCanisterPayload, String>>()?;
match result {
Ok(payload) => Ok(payload.subnet_id),
Err(msg) => {
log!(
Topic::Topology,
Warn,
"NNS registry rejected get_subnet_for_canister: {msg}"
);
Err(NnsRegistryInfraError::Rejected { reason: msg }.into())
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn registry_rejection_preserves_its_typed_infra_cause() {
let error: IcInfraError = NnsRegistryInfraError::Rejected {
reason: "rejected".to_string(),
}
.into();
assert!(matches!(
error,
IcInfraError::NnsRegistryInfra(
NnsRegistryInfraError::Rejected { reason }
) if reason == "rejected"
));
}
}