app: add sidecar sync commands (export, detect, import, resolve, full export/import)
This commit is contained in:
66
crates/application/src/sidecar/commands/resolve_conflict.rs
Normal file
66
crates/application/src/sidecar/commands/resolve_conflict.rs
Normal file
@@ -0,0 +1,66 @@
|
||||
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)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user