67 lines
2.5 KiB
Rust
67 lines
2.5 KiB
Rust
use std::sync::Arc;
|
|
use domain::{
|
|
catalog::entities::{AssetMetadata, MetadataSource},
|
|
catalog::services::resolve_metadata,
|
|
entities::{ConflictPolicy, SidecarRecord, SyncStatus},
|
|
errors::DomainError,
|
|
ports::{AssetMetadataRepository, SidecarRepository, SidecarWriterPort},
|
|
value_objects::SystemId,
|
|
};
|
|
use crate::sidecar::hash_helper::hash_structured_data;
|
|
|
|
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
|
pub struct ResolveConflictCommand {
|
|
pub asset_id: SystemId,
|
|
pub policy: ConflictPolicy,
|
|
}
|
|
|
|
pub struct ResolveConflictHandler {
|
|
sidecar_repo: Arc<dyn SidecarRepository>,
|
|
writer: Arc<dyn SidecarWriterPort>,
|
|
metadata_repo: Arc<dyn AssetMetadataRepository>,
|
|
}
|
|
|
|
impl ResolveConflictHandler {
|
|
pub fn new(
|
|
sidecar_repo: Arc<dyn SidecarRepository>,
|
|
writer: Arc<dyn SidecarWriterPort>,
|
|
metadata_repo: Arc<dyn AssetMetadataRepository>,
|
|
) -> Self {
|
|
Self { sidecar_repo, writer, metadata_repo }
|
|
}
|
|
|
|
pub async fn execute(&self, cmd: ResolveConflictCommand) -> Result<SidecarRecord, DomainError> {
|
|
let mut record = self.sidecar_repo.find_by_asset(&cmd.asset_id).await?
|
|
.ok_or_else(|| DomainError::NotFound(format!("Sidecar record for {} not found", cmd.asset_id)))?;
|
|
|
|
if record.sync_status != SyncStatus::Conflict {
|
|
return Err(DomainError::Validation(
|
|
format!("Sidecar is not in conflict (status: {:?})", record.sync_status),
|
|
));
|
|
}
|
|
|
|
match cmd.policy {
|
|
ConflictPolicy::DbWins => {
|
|
let layers = self.metadata_repo.find_by_asset(&cmd.asset_id).await?;
|
|
let resolved = resolve_metadata(&layers);
|
|
self.writer.write_sidecar(&resolved, &record.sidecar_storage_path).await?;
|
|
let hash = hash_structured_data(&resolved);
|
|
record.mark_synced(hash);
|
|
}
|
|
ConflictPolicy::FileWins => {
|
|
let data = self.writer.read_sidecar(&record.sidecar_storage_path).await?;
|
|
let metadata = AssetMetadata::new(cmd.asset_id, MetadataSource::ExifExtracted, data.clone());
|
|
self.metadata_repo.save(&metadata).await?;
|
|
let hash = hash_structured_data(&data);
|
|
record.mark_synced(hash);
|
|
}
|
|
ConflictPolicy::RequireUserDecision => {
|
|
return Err(DomainError::Validation("Manual resolution required".to_string()));
|
|
}
|
|
}
|
|
|
|
self.sidecar_repo.save(&record).await?;
|
|
Ok(record)
|
|
}
|
|
}
|