use crate::{
db::{
Db,
relation::{
StrongRelationTargetInfo, build_relation_target_raw_key,
for_each_relation_target_value, strong_relation_target_from_kind,
},
},
error::InternalError,
model::entity::EntityModel,
traits::{EntityKind, EntityValue},
value::Value,
};
#[must_use]
pub(in crate::db) fn model_has_strong_relation_targets(model: &'static EntityModel) -> bool {
model
.fields()
.iter()
.any(|field| strong_relation_target_from_kind(&field.kind).is_some())
}
pub(in crate::db) fn validate_save_strong_relations<E>(
db: &Db<E::Canister>,
entity: &E,
) -> Result<(), InternalError>
where
E: EntityKind + EntityValue,
{
for (field_index, field) in E::MODEL.fields.iter().enumerate() {
let Some(relation) = strong_relation_target_from_kind(&field.kind) else {
continue;
};
let value = entity.get_value_by_index(field_index).ok_or_else(|| {
InternalError::executor_invariant(format!(
"entity field missing: {} field={}",
E::PATH,
field.name
))
})?;
for_each_relation_target_value(&value, |item| {
validate_save_relation_value::<E>(db, field.name, relation, item)
})?;
}
Ok(())
}
fn validate_save_relation_value<E>(
db: &Db<E::Canister>,
field_name: &str,
relation: StrongRelationTargetInfo,
value: &Value,
) -> Result<(), InternalError>
where
E: EntityKind + EntityValue,
{
let raw_key = build_relation_target_raw_key(
relation.target_entity_tag,
relation.target_entity_name,
value,
)
.map_err(|err| {
InternalError::relation_target_raw_key_error(
err,
E::PATH,
field_name,
relation.target_path,
relation.target_entity_name,
value,
"strong relation key not storage-compatible",
"strong relation target name invalid",
)
})?;
let store = db
.with_store_registry(|reg| reg.try_get_store(relation.target_store_path))
.map_err(|err| {
InternalError::strong_relation_target_store_missing(
E::PATH,
field_name,
relation.target_path,
relation.target_store_path,
value,
err,
)
})?;
let exists = store.with_data(|s| s.contains_key(&raw_key));
if !exists {
return Err(InternalError::strong_relation_target_missing(
E::PATH,
field_name,
relation.target_path,
value,
));
}
Ok(())
}