#[cfg(test)]
mod tests;
use crate::icp::{IcpJsonResponseError, decode_json_result_response};
use canic_core::{
cdk::utils::hash::hex_bytes,
dto::topology::{SubnetRegistryEntry, SubnetRegistryResponse},
};
use thiserror::Error as ThisError;
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct RegistryEntry {
pub pid: String,
pub role: Option<String>,
pub parent_pid: Option<String>,
pub module_hash: Option<String>,
}
#[derive(Debug, ThisError)]
pub enum RegistryParseError {
#[error("registry entry principal mismatch: entry={entry_pid} record={record_pid}")]
PrincipalMismatch {
entry_pid: String,
record_pid: String,
},
#[error(transparent)]
Response(#[from] IcpJsonResponseError),
#[error("registry entry role mismatch for {pid}: entry={entry_role} record={record_role}")]
RoleMismatch {
pid: String,
entry_role: String,
record_role: String,
},
}
pub fn parse_registry_entries(
registry_json: &str,
) -> Result<Vec<RegistryEntry>, RegistryParseError> {
let response = decode_json_result_response::<SubnetRegistryResponse>(registry_json)?;
registry_entries_from_response(response)
}
pub(crate) fn registry_entries_from_response(
response: SubnetRegistryResponse,
) -> Result<Vec<RegistryEntry>, RegistryParseError> {
response.0.into_iter().map(registry_entry).collect()
}
fn registry_entry(entry: SubnetRegistryEntry) -> Result<RegistryEntry, RegistryParseError> {
let pid = entry.pid.to_text();
let record_pid = entry.record.pid.to_text();
if pid != record_pid {
return Err(RegistryParseError::PrincipalMismatch {
entry_pid: pid,
record_pid,
});
}
let role = entry.role.into_string();
let record_role = entry.record.role.into_string();
if role != record_role {
return Err(RegistryParseError::RoleMismatch {
pid,
entry_role: role,
record_role,
});
}
Ok(RegistryEntry {
pid,
role: Some(role),
parent_pid: entry.record.parent_pid.map(|pid| pid.to_text()),
module_hash: entry.record.module_hash.map(hex_bytes),
})
}