Skip to content

Commit 9d7c69e

Browse files
committed
Local move: extract sink-based inner function
- Rename outer `move_files_with_progress` body into `move_files_with_progress_inner(events: &dyn OperationEventSink, ...)`. The thin outer wrapper still takes `&AppHandle`, constructs `TauriEventSink`, and calls the inner. - `move_with_rename`, `move_with_staging`, `merge_move_directory`, and `delete_sources_after_move` all switch to `&dyn OperationEventSink`. Every `app.emit(...)` and `state.emit_progress_via_app(...)` site routes through the sink. - No behavior change: identical events, identical ordering, identical error paths. Mirrors the M1 step 3 pattern from `copy.rs`.
1 parent a056eb5 commit 9d7c69e

1 file changed

Lines changed: 75 additions & 93 deletions

File tree

  • apps/desktop/src-tauri/src/file_system/write_operations

apps/desktop/src-tauri/src/file_system/write_operations/move_op.rs

Lines changed: 75 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,9 @@ use super::helpers::{is_same_filesystem, remove_dir_all_in_background, resolve_c
1111
use super::scan::{SourceItemTracker, handle_dry_run, scan_sources};
1212
use super::state::{CopyTransaction, OperationIntent, WriteOperationState, load_intent, update_operation_status};
1313
use super::types::{
14-
ConflictResolution, IoResultExt, TauriEventSink, WriteCancelledEvent, WriteCompleteEvent, WriteErrorEvent,
15-
WriteOperationConfig, WriteOperationError, WriteOperationPhase, WriteOperationType, WriteProgressEvent,
16-
WriteSourceItemDoneEvent,
14+
ConflictResolution, IoResultExt, OperationEventSink, TauriEventSink, WriteCancelledEvent, WriteCompleteEvent,
15+
WriteErrorEvent, WriteOperationConfig, WriteOperationError, WriteOperationPhase, WriteOperationType,
16+
WriteProgressEvent, WriteSourceItemDoneEvent,
1717
};
1818

1919
// ============================================================================
@@ -64,14 +64,24 @@ pub(super) fn move_files_with_progress(
6464
config: &WriteOperationConfig,
6565
) -> Result<(), WriteOperationError> {
6666
let events = TauriEventSink::new(app.clone());
67+
move_files_with_progress_inner(&events, operation_id, state, sources, destination, config)
68+
}
6769

70+
pub(super) fn move_files_with_progress_inner(
71+
events: &dyn OperationEventSink,
72+
operation_id: &str,
73+
state: &Arc<WriteOperationState>,
74+
sources: &[PathBuf],
75+
destination: &Path,
76+
config: &WriteOperationConfig,
77+
) -> Result<(), WriteOperationError> {
6878
// Handle dry-run mode
6979
if handle_dry_run(
7080
config.dry_run,
7181
sources,
7282
destination,
7383
state,
74-
&events,
84+
events,
7585
operation_id,
7686
WriteOperationType::Move,
7787
state.progress_interval,
@@ -87,24 +97,21 @@ pub(super) fn move_files_with_progress(
8797

8898
if same_fs {
8999
// Use instant rename for each source
90-
move_with_rename(app, operation_id, state, sources, destination, config)
100+
move_with_rename(events, operation_id, state, sources, destination, config)
91101
} else {
92102
// Use atomic staging pattern for cross-filesystem move
93-
move_with_staging(app, operation_id, state, sources, destination, config)
103+
move_with_staging(events, operation_id, state, sources, destination, config)
94104
}
95105
}
96106

97107
fn move_with_rename(
98-
app: &tauri::AppHandle,
108+
events: &dyn OperationEventSink,
99109
operation_id: &str,
100110
state: &Arc<WriteOperationState>,
101111
sources: &[PathBuf],
102112
destination: &Path,
103113
config: &WriteOperationConfig,
104114
) -> Result<(), WriteOperationError> {
105-
use tauri::Emitter;
106-
107-
let events = TauriEventSink::new(app.clone());
108115
let mut files_done = 0;
109116
let mut apply_to_all_resolution: Option<ConflictResolution> = None;
110117
let mut move_tx = MoveTransaction::new();
@@ -131,7 +138,7 @@ fn move_with_rename(
131138
source,
132139
&dest_path,
133140
config,
134-
&events,
141+
events,
135142
operation_id,
136143
state,
137144
&mut apply_to_all_resolution,
@@ -143,7 +150,7 @@ fn move_with_rename(
143150
source,
144151
&dest_path,
145152
config,
146-
&events,
153+
events,
147154
operation_id,
148155
state,
149156
&mut apply_to_all_resolution,
@@ -165,13 +172,10 @@ fn move_with_rename(
165172

166173
files_done += 1;
167174

168-
let _ = app.emit(
169-
"write-source-item-done",
170-
WriteSourceItemDoneEvent {
171-
operation_id: operation_id.to_string(),
172-
source_path: source.display().to_string(),
173-
},
174-
);
175+
events.emit_source_item_done(WriteSourceItemDoneEvent {
176+
operation_id: operation_id.to_string(),
177+
source_path: source.display().to_string(),
178+
});
175179
}
176180
Ok(())
177181
})();
@@ -188,15 +192,12 @@ fn move_with_rename(
188192
_ => false,
189193
};
190194

191-
let _ = app.emit(
192-
"write-cancelled",
193-
WriteCancelledEvent {
194-
operation_id: operation_id.to_string(),
195-
operation_type: WriteOperationType::Move,
196-
files_processed: files_done,
197-
rolled_back,
198-
},
199-
);
195+
events.emit_cancelled(WriteCancelledEvent {
196+
operation_id: operation_id.to_string(),
197+
operation_type: WriteOperationType::Move,
198+
files_processed: files_done,
199+
rolled_back,
200+
});
200201
return result;
201202
}
202203

@@ -206,15 +207,12 @@ fn move_with_rename(
206207
spawn_async_sync();
207208

208209
// Emit completion (instant, no progress needed)
209-
let _ = app.emit(
210-
"write-complete",
211-
WriteCompleteEvent {
212-
operation_id: operation_id.to_string(),
213-
operation_type: WriteOperationType::Move,
214-
files_processed: files_done,
215-
bytes_processed: 0, // Rename doesn't track bytes
216-
},
217-
);
210+
events.emit_complete(WriteCompleteEvent {
211+
operation_id: operation_id.to_string(),
212+
operation_type: WriteOperationType::Move,
213+
files_processed: files_done,
214+
bytes_processed: 0, // Rename doesn't track bytes
215+
});
218216

219217
Ok(())
220218
}
@@ -232,7 +230,7 @@ fn merge_move_directory(
232230
source_dir: &Path,
233231
dest_dir: &Path,
234232
config: &WriteOperationConfig,
235-
events: &dyn super::types::OperationEventSink,
233+
events: &dyn OperationEventSink,
236234
operation_id: &str,
237235
state: &Arc<WriteOperationState>,
238236
apply_to_all_resolution: &mut Option<ConflictResolution>,
@@ -309,22 +307,18 @@ fn merge_move_directory(
309307
/// Performs cross-filesystem move using atomic staging pattern.
310308
/// This ensures source files remain intact if the operation fails.
311309
fn move_with_staging(
312-
app: &tauri::AppHandle,
310+
events: &dyn OperationEventSink,
313311
operation_id: &str,
314312
state: &Arc<WriteOperationState>,
315313
sources: &[PathBuf],
316314
destination: &Path,
317315
config: &WriteOperationConfig,
318316
) -> Result<(), WriteOperationError> {
319-
use tauri::Emitter;
320-
321-
let events = TauriEventSink::new(app.clone());
322-
323317
// Phase 1: Scan (move uses default sorting - order doesn't matter much for move)
324318
let scan_result = scan_sources(
325319
sources,
326320
state,
327-
&events,
321+
events,
328322
operation_id,
329323
WriteOperationType::Move,
330324
config.sort_column,
@@ -347,8 +341,8 @@ fn move_with_staging(
347341
let mut created_dirs: HashSet<PathBuf> = HashSet::new();
348342

349343
// Emit initial copying phase event
350-
state.emit_progress_via_app(
351-
app,
344+
state.emit_progress_via_sink(
345+
events,
352346
WriteProgressEvent::new(
353347
operation_id.to_string(),
354348
WriteOperationType::Move,
@@ -395,7 +389,7 @@ fn move_with_staging(
395389
scan_result.file_count,
396390
scan_result.total_bytes,
397391
state,
398-
&events,
392+
events,
399393
operation_id,
400394
WriteOperationType::Move,
401395
&state.progress_interval,
@@ -407,13 +401,10 @@ fn move_with_staging(
407401
)?;
408402

409403
if let Some(source_path) = tracker.record(file_info) {
410-
let _ = app.emit(
411-
"write-source-item-done",
412-
WriteSourceItemDoneEvent {
413-
operation_id: operation_id.to_string(),
414-
source_path: source_path.display().to_string(),
415-
},
416-
);
404+
events.emit_source_item_done(WriteSourceItemDoneEvent {
405+
operation_id: operation_id.to_string(),
406+
source_path: source_path.display().to_string(),
407+
});
417408
}
418409
}
419410
Ok(())
@@ -422,10 +413,11 @@ fn move_with_staging(
422413
if let Err(e) = copy_result {
423414
// Cleanup staging directory in background (may block on network mounts)
424415
remove_dir_all_in_background(staging_dir.clone());
425-
let _ = app.emit(
426-
"write-error",
427-
WriteErrorEvent::new(operation_id.to_string(), WriteOperationType::Move, e.clone()),
428-
);
416+
events.emit_error(WriteErrorEvent::new(
417+
operation_id.to_string(),
418+
WriteOperationType::Move,
419+
e.clone(),
420+
));
429421
return Err(e);
430422
}
431423

@@ -448,7 +440,7 @@ fn move_with_staging(
448440
&staged_path,
449441
&final_path,
450442
config,
451-
&events,
443+
events,
452444
operation_id,
453445
state,
454446
&mut apply_to_all_resolution,
@@ -460,7 +452,7 @@ fn move_with_staging(
460452
source,
461453
&final_path,
462454
config,
463-
&events,
455+
events,
464456
operation_id,
465457
state,
466458
&mut apply_to_all_resolution,
@@ -495,15 +487,16 @@ fn move_with_staging(
495487
if let Err(e) = rename_result {
496488
// Cleanup staging directory in background (may block on network mounts)
497489
remove_dir_all_in_background(staging_dir);
498-
let _ = app.emit(
499-
"write-error",
500-
WriteErrorEvent::new(operation_id.to_string(), WriteOperationType::Move, e.clone()),
501-
);
490+
events.emit_error(WriteErrorEvent::new(
491+
operation_id.to_string(),
492+
WriteOperationType::Move,
493+
e.clone(),
494+
));
502495
return Err(e);
503496
}
504497

505498
// Phase 4: Delete source files (only after successful copy+rename)
506-
delete_sources_after_move(app, operation_id, state, sources, files_done)?;
499+
delete_sources_after_move(events, operation_id, state, sources, files_done)?;
507500

508501
// Phase 5: Remove empty staging directory
509502
let _ = fs::remove_dir(&staging_dir);
@@ -512,40 +505,32 @@ fn move_with_staging(
512505
spawn_async_sync();
513506

514507
// Emit completion
515-
let _ = app.emit(
516-
"write-complete",
517-
WriteCompleteEvent {
518-
operation_id: operation_id.to_string(),
519-
operation_type: WriteOperationType::Move,
520-
files_processed: files_done,
521-
bytes_processed: bytes_done,
522-
},
523-
);
508+
events.emit_complete(WriteCompleteEvent {
509+
operation_id: operation_id.to_string(),
510+
operation_type: WriteOperationType::Move,
511+
files_processed: files_done,
512+
bytes_processed: bytes_done,
513+
});
524514

525515
Ok(())
526516
}
527517

528518
fn delete_sources_after_move(
529-
app: &tauri::AppHandle,
519+
events: &dyn OperationEventSink,
530520
operation_id: &str,
531521
state: &Arc<WriteOperationState>,
532522
sources: &[PathBuf],
533523
files_done: usize,
534524
) -> Result<(), WriteOperationError> {
535-
use tauri::Emitter;
536-
537525
for source in sources {
538526
// Check cancellation
539527
if super::state::is_cancelled(&state.intent) {
540-
let _ = app.emit(
541-
"write-cancelled",
542-
WriteCancelledEvent {
543-
operation_id: operation_id.to_string(),
544-
operation_type: WriteOperationType::Move,
545-
files_processed: files_done,
546-
rolled_back: false, // Source deletion phase - nothing to rollback
547-
},
548-
);
528+
events.emit_cancelled(WriteCancelledEvent {
529+
operation_id: operation_id.to_string(),
530+
operation_type: WriteOperationType::Move,
531+
files_processed: files_done,
532+
rolled_back: false, // Source deletion phase - nothing to rollback
533+
});
549534
return Err(WriteOperationError::Cancelled {
550535
message: "Operation cancelled by user".to_string(),
551536
});
@@ -559,13 +544,10 @@ fn delete_sources_after_move(
559544
fs::remove_file(source).with_path(source)?;
560545
}
561546

562-
let _ = app.emit(
563-
"write-source-item-done",
564-
WriteSourceItemDoneEvent {
565-
operation_id: operation_id.to_string(),
566-
source_path: source.display().to_string(),
567-
},
568-
);
547+
events.emit_source_item_done(WriteSourceItemDoneEvent {
548+
operation_id: operation_id.to_string(),
549+
source_path: source.display().to_string(),
550+
});
569551
}
570552
}
571553

0 commit comments

Comments
 (0)