mirror of
https://github.com/narrrl/proton-drive-linux.git
synced 2026-11-03 16:41:26 +01:00
feat(cli,fuse,core): copy files and folders on Proton Drive
`pdfs copy PATH... NEW_PARENT` sends a new control request, Copy, which copies a file with its content or a folder with its subtree on Proton Drive through proton-drive-rs's copy_node. Nothing is downloaded or uploaded. A name already taken in the destination gets the next free " (n)" suffix through get_available_name. A copy cannot wait in the queue, because Drive copies what it holds. So it is refused offline, and while the mount still owes Drive part of the node: a local placeholder, a queued op, unsaved writes, or, for a folder, any queued op at all. Local-first does not change this. DriveApi gains copy_node and get_available_name, and the fake drive copies subtrees, so a FUSE sim test covers the copy, the suffix, the folder subtree and both refusals. The activity feed logs a new Copy kind, translated in every catalog. Needs the proton-drive-rs release that adds copy_node; until then, build with a [patch.crates-io] pointing at proton-sdk-rs.
This commit is contained in:
1 parent
a73ed34421
commit
c1d9538669
20 files changed
+1073
-600
No files matched your search
@@ -356,6 +356,18 @@ enum Command {
|
||||
#[arg(required = true, num_args = 2.., value_name = "PATH")]
|
||||
paths: Vec<PathBuf>,
|
||||
},
|
||||
/// Copy files or folders into another folder in My files via the running
|
||||
/// daemon.
|
||||
///
|
||||
/// The copy happens on Proton Drive, so nothing is downloaded or uploaded,
|
||||
/// unlike `cp` inside the mount. A name already taken in the destination
|
||||
/// gets a " (1)" suffix. The last path is the destination folder.
|
||||
Copy {
|
||||
/// Files/folders to copy, then the destination folder: inside My files,
|
||||
/// or relative to the mountpoint.
|
||||
#[arg(required = true, num_args = 2.., value_name = "PATH")]
|
||||
paths: Vec<PathBuf>,
|
||||
},
|
||||
/// Trash a file or folder via the running daemon.
|
||||
Rm {
|
||||
/// File/folder path, inside My files or an on-demand synced folder, or
|
||||
@@ -919,6 +931,10 @@ fn main() -> Result<()> {
|
||||
let new_parent = paths.pop().expect("clap requires at least two paths");
|
||||
cmd_move(paths, new_parent)
|
||||
}
|
||||
Command::Copy { mut paths } => {
|
||||
let new_parent = paths.pop().expect("clap requires at least two paths");
|
||||
cmd_copy(paths, new_parent)
|
||||
}
|
||||
Command::Rm { path } => cmd_rm(path),
|
||||
Command::Mkdir { parent, name } => cmd_mkdir(parent, name),
|
||||
Command::Upload { sources, parent } => cmd_upload(sources, parent),
|
||||
@@ -1765,6 +1781,7 @@ fn activity_verb(kind: pdfs_core::control::ActivityKind) -> &'static str {
|
||||
Sync => "sync",
|
||||
Rename => "rename",
|
||||
Move => "move",
|
||||
Copy => "copy",
|
||||
CreateFolder => "mkdir",
|
||||
Trash => "trash",
|
||||
Restore => "restore",
|
||||
@@ -2920,6 +2937,35 @@ fn cmd_move(sources: Vec<PathBuf>, new_parent: PathBuf) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn cmd_copy(sources: Vec<PathBuf>, new_parent: PathBuf) -> Result<()> {
|
||||
let new_parent = path_arg(&new_parent)?;
|
||||
let mut failed = 0;
|
||||
for source in &sources {
|
||||
let outcome = path_arg(source).and_then(|path| {
|
||||
match control_request(CtlRequest::Copy {
|
||||
path,
|
||||
new_parent: new_parent.clone(),
|
||||
})? {
|
||||
CtlResponse::Ok { message } => Ok(message),
|
||||
CtlResponse::Error { message, kind } => bail!("{}", cli_error(kind, &message)),
|
||||
other => bail!("unexpected response: {other:?}"),
|
||||
}
|
||||
});
|
||||
match outcome {
|
||||
Ok(message) => println!("{message}"),
|
||||
Err(e) if sources.len() == 1 => return Err(e),
|
||||
Err(e) => {
|
||||
eprintln!("{}: {e:#}", source.display());
|
||||
failed += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
if failed > 0 {
|
||||
bail!("{failed} of {} could not be copied", sources.len());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn cmd_rm(path: PathBuf) -> Result<()> {
|
||||
match control_request(CtlRequest::Delete {
|
||||
path: path_arg(&path)?,
|
||||
|
||||
Reference in new issue
Block a user