use std::{
collections::{HashMap, HashSet},
sync::Arc,
};
use crate::{
ErrorKind,
property::Property,
sync::{
declare::StoragePair,
mapping::ResolvedMapping,
mode::Mode,
operation::PropertyOp,
ordering::DeletionBarrier,
plan::PlanError,
status::{MappingUid, StatusDatabase},
},
};
pub(crate) async fn load_and_create_property_operations(
mapping: &Arc<ResolvedMapping>,
mapping_uid: Option<MappingUid>,
pair: &StoragePair,
status: Option<&StatusDatabase>,
deletion_completion: Option<&DeletionBarrier>,
mode: &dyn Mode,
) -> Result<Vec<PropertyOp>, PlanError> {
let (props_a, props_b) = match tokio::try_join!(
pair.storage_a().list_properties(mapping.a().href()),
pair.storage_b().list_properties(mapping.b().href()),
) {
Ok((a, b)) => (a, b),
Err(error) => {
return if let ErrorKind::DoesNotExist | ErrorKind::Unsupported = error.kind {
Ok(Vec::new())
} else {
Err(error.into())
};
}
};
let props_status = match (status, mapping_uid) {
(Some(s), Some(u)) => s.list_properties_for_collection(u)?,
_ => Vec::with_capacity(0),
};
let Some(mapping_uid) = mapping_uid else {
return Ok(Vec::new());
};
let mut values_a: HashMap<Property, String> =
props_a.into_iter().map(|p| (p.property, p.value)).collect();
let mut values_b: HashMap<Property, String> =
props_b.into_iter().map(|p| (p.property, p.value)).collect();
let mut previous_values: HashMap<String, String> = props_status
.into_iter()
.map(|p| (p.property, p.value))
.collect();
let all_props: HashSet<Property> = values_a.keys().chain(values_b.keys()).copied().collect();
let operations: Vec<PropertyOp> = all_props
.into_iter()
.filter_map(|property: Property| {
let from_a = values_a.remove(&property);
let from_b = values_b.remove(&property);
let from_status = previous_values.remove(property.name());
mode.decide_property_action(
property,
from_a,
from_b,
from_status,
mapping,
mapping_uid,
deletion_completion,
)
})
.collect();
Ok(operations)
}