use anyhow::Result;
use stderrlog::LogLevelNum;
use transfer::{listen::ListenArgs, send::SendArgs};
mod util;
use util::*;
pub mod compression;
#[cfg(feature = "ssh")]
pub mod ssh;
pub mod transfer;
pub const BIN_NAME: &str = "qft";
#[cfg(feature = "evaluate-compression")]
pub mod evaluate_compression;
pub mod get_free_port;
#[cfg(feature = "mdns")]
pub mod mdns;
pub mod misc;
#[derive(Debug, Parser)]
#[command(name = "Quick File Transfer", version, styles = misc::cli_styles())]
#[command(bin_name = BIN_NAME)]
pub struct Config {
#[clap(subcommand)]
pub command: Option<Command>,
#[arg(short, long, action = ArgAction::Count, default_value_t = 0, global = true)]
pub verbose: u8,
#[arg(short, long, action = ArgAction::SetTrue, conflicts_with("verbose"), global = true, env = "QFT_QUIET")]
pub quiet: bool,
#[arg(
long,
require_equals = true,
value_name = "WHEN",
default_value_t = clap::ColorChoice::Auto,
default_missing_value = "always",
value_enum,
global = true
)]
pub color: clap::ColorChoice,
#[arg(
long = "completions",
value_hint = clap::ValueHint::Other,
value_name = "SHELL"
)]
pub completions: Option<clap_complete::Shell>,
}
impl Config {
pub fn init() -> Result<Self> {
let cfg = Self::parse();
let log_level: LogLevelNum = match cfg.verbose {
0 => LogLevelNum::Info,
1 => LogLevelNum::Debug,
255 => LogLevelNum::Off,
_ => LogLevelNum::Trace,
};
let log_color_when: stderrlog::ColorChoice = match cfg.color {
clap::ColorChoice::Auto => stderrlog::ColorChoice::Auto,
clap::ColorChoice::Always => stderrlog::ColorChoice::Always,
clap::ColorChoice::Never => stderrlog::ColorChoice::Never,
};
set_tracing(&log_level);
stderrlog::new()
.verbosity(log_level)
.quiet(cfg.quiet)
.color(log_color_when)
.init()?;
Ok(cfg)
}
pub fn generate_completion_script(shell: clap_complete::Shell) {
use clap::CommandFactory;
clap_complete::generate(
shell,
&mut Config::command(),
BIN_NAME,
&mut std::io::stdout(),
);
}
}
#[allow(clippy::large_enum_variant)]
#[derive(Debug, Subcommand)]
pub enum Command {
Listen(ListenArgs),
Send(SendArgs),
#[cfg(feature = "mdns")]
Mdns(mdns::MdnsArgs),
#[cfg(feature = "evaluate-compression")]
EvaluateCompression(evaluate_compression::EvaluateCompressionArgs),
GetFreePort(get_free_port::GetFreePortArgs),
#[cfg(feature = "ssh")]
#[command(long_about("SCP-like transfer to a remote target that might not have qft actively listening.\n\
Authentication uses SSH (key based auth only) and while the transfer occurs over TCP, UNENCRYPTED!.\n\
Just like the rest of QTF, this is not suitable for transforring sensitive information."))]
Ssh(ssh::SendSshArgs),
}
fn set_tracing(_trace_level: &LogLevelNum) {
#[cfg(debug_assertions)]
set_dev_tracing(_trace_level);
#[cfg(not(debug_assertions))]
set_prod_tracing();
}
#[cfg(not(debug_assertions))]
fn set_prod_tracing() {
let subscriber = tracing_subscriber::FmtSubscriber::builder()
.with_writer(std::io::stderr)
.with_max_level(tracing::Level::ERROR)
.with_file(false)
.without_time()
.compact()
.finish();
tracing::subscriber::set_global_default(subscriber).expect("setting default subscriber failed");
}
#[cfg(debug_assertions)]
fn set_dev_tracing(trace_level: &LogLevelNum) {
use tracing::Level;
let log_level: Level = match trace_level {
LogLevelNum::Info => Level::INFO,
LogLevelNum::Debug => Level::DEBUG,
LogLevelNum::Trace => Level::TRACE,
LogLevelNum::Off => Level::ERROR,
_ => Level::ERROR,
};
let subscriber = tracing_subscriber::FmtSubscriber::builder()
.with_writer(std::io::stderr)
.with_max_level(log_level)
.with_line_number(true)
.with_thread_names(true)
.finish();
tracing::subscriber::set_global_default(subscriber).expect("setting default subscriber failed");
}