Files
k-photos/crates/application/src/sidecar/commands/resolve_conflict.rs

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)
}
}