use super::InventoryRecordError;
use crate::model::artifacts::ArtifactChecksumRecord;
use serde::{Deserialize, Deserializer, Serialize};
pub const MAX_INVENTORY_ROLE_BYTES: usize = 256;
#[derive(Clone, Debug)]
pub struct InventoryTargetRequest {
pub canister_id: String,
pub parent_canister_id: Option<String>,
pub role: Option<String>,
pub module_hash: Option<String>,
}
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
#[serde(try_from = "TargetFields")]
pub struct InventoryTargetRecord {
canister_id: String,
parent_canister_id: Option<String>,
role: Option<String>,
module_hash: Option<String>,
}
#[derive(Deserialize)]
#[serde(deny_unknown_fields)]
struct TargetFields {
canister_id: String,
#[serde(deserialize_with = "required_text")]
parent_canister_id: Option<String>,
#[serde(deserialize_with = "required_text")]
role: Option<String>,
#[serde(deserialize_with = "required_text")]
module_hash: Option<String>,
}
fn required_text<'de, D: Deserializer<'de>>(deserializer: D) -> Result<Option<String>, D::Error> {
Option::deserialize(deserializer)
}
impl TryFrom<TargetFields> for InventoryTargetRecord {
type Error = InventoryRecordError;
fn try_from(fields: TargetFields) -> Result<Self, Self::Error> {
Self::new(&InventoryTargetRequest {
canister_id: fields.canister_id,
parent_canister_id: fields.parent_canister_id,
role: fields.role,
module_hash: fields.module_hash,
})
}
}
impl InventoryTargetRecord {
pub fn new(request: &InventoryTargetRequest) -> Result<Self, InventoryRecordError> {
if request
.role
.as_ref()
.is_some_and(|role| role.len() > MAX_INVENTORY_ROLE_BYTES)
{
return Err(InventoryRecordError::RoleTooLarge);
}
Ok(Self {
canister_id: crate::model::principal::canonical_text(&request.canister_id)
.ok_or(InventoryRecordError::InvalidPrincipal("canister_id"))?,
parent_canister_id: request
.parent_canister_id
.as_deref()
.map(|value| {
crate::model::principal::canonical_text(value)
.ok_or(InventoryRecordError::InvalidPrincipal("parent_canister_id"))
})
.transpose()?,
role: request.role.clone(),
module_hash: request
.module_hash
.as_deref()
.map(|value| {
ArtifactChecksumRecord::from_hash(value).map(|hash| hash.hash().to_owned())
})
.transpose()?,
})
}
#[must_use]
pub fn canister_id(&self) -> &str {
&self.canister_id
}
#[must_use]
pub fn parent_canister_id(&self) -> Option<&str> {
self.parent_canister_id.as_deref()
}
#[must_use]
pub fn role(&self) -> Option<&str> {
self.role.as_deref()
}
#[must_use]
pub fn module_hash(&self) -> Option<&str> {
self.module_hash.as_deref()
}
}