A review against the Python client, which shares this client's config and login cache, found the transfers holding whole files in memory and several commands answering differently from their Python counterparts. - Stream uploads and downloads instead of buffering the whole file, so memory no longer scales with file size times concurrency. Uploads still declare a Content-Length rather than going out chunked. - Write a download beside its destination and move it into place once complete, and refuse a destination that cannot be written, so a failed transfer leaves what was already there untouched. - Report the server's own error detail instead of the bare status. - List locked sub-folders in `ls --json --recursive` using the given password and skip only those that cannot be unlocked, instead of failing the whole listing. - Name each folder's laboratory in JSON output and sort its entries, matching the Python client's schema and order. - Ask the server who is logged in for `whoami`, rather than trusting the cached name. - Collapse repeated separators and refuse `..` in remote paths. - Fall back to the API when a laboratory is missing from the cache, so a newly added one no longer needs a fresh login. - Verify the login cache digest on read, as the Python client does. - Accept `-e` as the short form of `--exclude`. - Satisfy clippy and rustfmt across the crate.
115 lines
2.7 KiB
Rust
115 lines
2.7 KiB
Rust
use clap::{Parser, Subcommand};
|
|
|
|
use crate::commands::config_subcommand::*;
|
|
|
|
#[derive(Parser)]
|
|
#[command(name = "mdrs")]
|
|
#[command(about = "MDRS Rust CLI client", long_about = None)]
|
|
pub struct Cli {
|
|
#[command(subcommand)]
|
|
pub command: Commands,
|
|
}
|
|
|
|
#[derive(Subcommand)]
|
|
pub enum Commands {
|
|
/// Config management (create, update, list, delete)
|
|
#[command(subcommand)]
|
|
Config(ConfigSubcommand),
|
|
Login {
|
|
#[arg(short, long)]
|
|
username: Option<String>,
|
|
#[arg(short, long)]
|
|
password: Option<String>,
|
|
remote: String,
|
|
},
|
|
/// Logout and remove cached credentials for a remote
|
|
Logout {
|
|
remote: String,
|
|
},
|
|
Upload {
|
|
#[arg(short, long)]
|
|
recursive: bool,
|
|
#[arg(short = 's', long)]
|
|
skip_if_exists: bool,
|
|
local_path: String,
|
|
remote_path: String,
|
|
},
|
|
Download {
|
|
#[arg(short, long)]
|
|
recursive: bool,
|
|
#[arg(short = 's', long)]
|
|
skip_if_exists: bool,
|
|
#[arg(short = 'p', long)]
|
|
password: Option<String>,
|
|
#[arg(short = 'e', long)]
|
|
exclude: Vec<String>,
|
|
remote_path: String,
|
|
local_path: String,
|
|
},
|
|
Ls {
|
|
remote_path: String,
|
|
#[arg(short = 'p', long)]
|
|
password: Option<String>,
|
|
#[arg(short = 'J', long = "json")]
|
|
json: bool,
|
|
#[arg(short = 'r', long)]
|
|
recursive: bool,
|
|
#[arg(short = 'q', long)]
|
|
quiet: bool,
|
|
},
|
|
Whoami {
|
|
remote: String,
|
|
},
|
|
Labs {
|
|
remote: String,
|
|
},
|
|
Chacl {
|
|
/// Access level key: private, public, pw_open, cbs_open, 5kikan_open,
|
|
/// cbs_or_pw_open, 5kikan_or_pw_open, storage
|
|
access_level_key: String,
|
|
#[arg(short, long)]
|
|
recursive: bool,
|
|
#[arg(short = 'p', long)]
|
|
password: Option<String>,
|
|
remote_path: String,
|
|
},
|
|
Metadata {
|
|
#[arg(short = 'p', long)]
|
|
password: Option<String>,
|
|
remote_path: String,
|
|
},
|
|
Mkdir {
|
|
remote_path: String,
|
|
},
|
|
Rm {
|
|
#[arg(short, long)]
|
|
recursive: bool,
|
|
remote_path: String,
|
|
},
|
|
Mv {
|
|
src_path: String,
|
|
dest_path: String,
|
|
},
|
|
Cp {
|
|
#[arg(short, long)]
|
|
recursive: bool,
|
|
src_path: String,
|
|
dest_path: String,
|
|
},
|
|
/// Show metadata for a remote file
|
|
FileMetadata {
|
|
#[arg(short = 'p', long)]
|
|
password: Option<String>,
|
|
remote_path: String,
|
|
},
|
|
/// Show the version of this tool
|
|
Version,
|
|
/// Update this binary to the latest release
|
|
#[command(name = "selfupdate")]
|
|
SelfUpdate {
|
|
/// Skip the confirmation prompt
|
|
#[arg(short = 'y', long)]
|
|
yes: bool,
|
|
},
|
|
}
|