109 lines
3.5 KiB
Rust
109 lines
3.5 KiB
Rust
use clap::Parser;
|
|
|
|
/// scp-like tool for copying (possibly still-growing) pcap capture files.
|
|
#[derive(Parser, Debug)]
|
|
#[command(name = "scpcap")]
|
|
pub struct ClientCli {
|
|
/// Source: local path or [user@]host:path. If the name ends in the
|
|
/// partial extension, it's treated as still growing.
|
|
pub source: String,
|
|
|
|
/// Destination: local path or [user@]host:path.
|
|
pub dest: String,
|
|
|
|
/// Compress in flight; destination is written as a streaming .pcap.zst.
|
|
/// Only "zstd" is supported.
|
|
#[arg(long, value_name = "CODEC", num_args = 0..=1, default_missing_value = "zstd", require_equals = true)]
|
|
pub compress: Option<String>,
|
|
|
|
/// Target chunk size for compression, e.g. "256k", "1m".
|
|
#[arg(long, default_value = "256k")]
|
|
pub chunk: String,
|
|
|
|
/// zstd compression level (lower = faster; default tuned for throughput
|
|
/// to stay competitive with `scp -C`).
|
|
#[arg(long, default_value_t = crate::zstd_frame::DEFAULT_LEVEL)]
|
|
pub level: i32,
|
|
|
|
/// Suffix identifying a still-growing file.
|
|
#[arg(long, default_value = "partial")]
|
|
pub extension: String,
|
|
}
|
|
|
|
/// Remote helper mode, ssh-spawned by the client (like `rsync --server`).
|
|
/// Not meant to be invoked directly by a user.
|
|
#[derive(Parser, Debug)]
|
|
#[command(name = "scpcap --server")]
|
|
pub struct ServerCli {
|
|
#[command(flatten)]
|
|
pub role: ServerRoleFlags,
|
|
|
|
/// Source path to read (--send mode).
|
|
#[arg(long)]
|
|
pub source: Option<String>,
|
|
|
|
/// Source's final (non-partial) path, for rename detection (--send mode).
|
|
#[arg(long)]
|
|
pub final_source: Option<String>,
|
|
|
|
/// Whether the source is a still-growing (partial) file (--send mode).
|
|
#[arg(long)]
|
|
pub partial: bool,
|
|
|
|
/// Compress in flight (--send mode). No adjacent positional args in
|
|
/// --server mode, so (unlike ClientCli's --compress) there's no
|
|
/// ambiguity requiring `--compress=zstd` -- `--compress zstd` is fine.
|
|
#[arg(long, value_name = "CODEC", num_args = 0..=1, default_missing_value = "zstd")]
|
|
pub compress: Option<String>,
|
|
|
|
/// Target chunk size for compression (--send mode).
|
|
#[arg(long, default_value = "256k")]
|
|
pub chunk: String,
|
|
|
|
/// zstd compression level (--send mode).
|
|
#[arg(long, default_value_t = crate::zstd_frame::DEFAULT_LEVEL)]
|
|
pub level: i32,
|
|
|
|
/// Destination final path (--recv mode).
|
|
#[arg(long)]
|
|
pub dest_final: Option<String>,
|
|
|
|
/// Destination temp path, written while in flight (--recv mode).
|
|
#[arg(long)]
|
|
pub dest_temp: Option<String>,
|
|
}
|
|
|
|
/// The clap-facing shape of the role choice: two mutually exclusive,
|
|
/// individually-long-named flags, matching rsync's `--server --send`/
|
|
/// `--server --recv` convention. Clap's derive has no direct way to map two
|
|
/// separate boolean flags onto one enum field, so this stays a flag pair at
|
|
/// the parsing boundary and converts to `ServerRole` via `role()` below.
|
|
#[derive(clap::Args, Debug)]
|
|
#[group(required = true, multiple = false)]
|
|
pub struct ServerRoleFlags {
|
|
#[arg(long)]
|
|
pub send: bool,
|
|
#[arg(long)]
|
|
pub recv: bool,
|
|
}
|
|
|
|
/// The role a `--server` process plays, for the rest of the code to match on
|
|
/// instead of checking two booleans.
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
pub enum ServerRole {
|
|
Send,
|
|
Recv,
|
|
}
|
|
|
|
impl ServerCli {
|
|
/// The `--group(required, exclusive)` on `ServerRoleFlags` guarantees
|
|
/// exactly one of `send`/`recv` is set by the time parsing succeeds.
|
|
pub fn role(&self) -> ServerRole {
|
|
if self.role.send {
|
|
ServerRole::Send
|
|
} else {
|
|
ServerRole::Recv
|
|
}
|
|
}
|
|
}
|