gravityfile-ops 0.4.1

File operations engine for gravityfile
Documentation

gravityfile-ops

File operations for gravityfile: copy, move, rename, create, delete, undo and archives.

This crate exists for the grav binary and its sibling crates. Its API follows their needs and can change in any minor release.

Long-running operations run on blocking threads and report through a channel: best-effort OperationProgress snapshots, interactive ConflictRequests, and one reliable completion message. Dropping the receiver cancels the operation.

Safety model

  • New content is assembled in a private staging directory beside its destination and published with a single no-clobber rename, so a failed or cancelled operation never leaves a partial item at a visible path and never silently replaces anything.
  • Overwrites the user approves move the replaced item to the system Trash (or delete it permanently, if configured) only after the replacement is complete.
  • Copies skip entries they cannot read and report each one instead of discarding everything that did copy; cross-filesystem moves keep the source unless every entry arrived.
  • Completed operations return what an undo needs as an UndoEntry. execute_undo moves and renames items back, sends copies to the Trash, and removes created files and directories only while they are still empty.
use gravityfile_ops::{CancellationToken, TransferOptions, TransferResult, start_copy};

let dir = tempfile::tempdir()?;
let source = dir.path().join("notes.txt");
let backup = dir.path().join("backup");
std::fs::write(&source, "hello")?;
std::fs::create_dir(&backup)?;

let runtime = tokio::runtime::Runtime::new()?;
let complete = runtime.block_on(async {
    let mut events = start_copy(
        vec![source],
        backup.clone(),
        TransferOptions::default(),
        CancellationToken::new(),
    );
    while let Some(event) = events.recv().await {
        match event {
            TransferResult::Progress(_) => {}
            // Dropping a request skips that item.
            TransferResult::Conflict(_) => {}
            TransferResult::Complete(complete) => return Some(complete),
        }
    }
    None
});

let complete = complete.expect("copy completes");
assert_eq!(complete.inner.succeeded, 1);
assert_eq!(std::fs::read_to_string(backup.join("notes.txt"))?, "hello");
# Ok::<(), Box<dyn std::error::Error>>(())

Licence

MIT or Apache-2.0, at your option.