use crate::cipher::Cipher;
use clap::{Parser, Subcommand};
use std::{net::SocketAddr, path::PathBuf, sync::Arc};
use tracing::Level;
#[derive(Debug, Parser)]
#[command(author, version, about, long_about = None)]
pub struct Args {
#[arg(required = true, long, short)]
pub listen: SocketAddr,
#[arg(required = true, long, short)]
pub remote: SocketAddr,
#[arg(long, value_parser = ["udp", "tcp", "uot"])]
pub protocol: String,
#[arg(long)]
pub deadline: Option<u64>,
#[arg(long, default_value_t = 20)]
pub timeout: u64,
#[arg(long, value_parser = parse_encryption, default_value = "")]
pub encryption: Arc<Cipher>,
#[arg(long, value_parser = parse_handshake)]
pub custom_handshake: Option<Arc<CustomHandshake>>,
#[arg(long)]
pub daemonize: bool,
#[arg(long, default_value_t = Level::ERROR)]
pub log_level: Level,
#[arg(long)]
pub log_path: Option<PathBuf>,
#[arg(long, default_value_t = 4096)]
pub mtu: usize,
#[command(subcommand)]
pub command: Commands,
}
#[derive(Debug, Subcommand)]
pub enum Commands {
Client,
Server,
}
#[derive(Debug)]
pub struct CustomHandshake {
pub request: Box<[u8]>,
pub response: Box<[u8]>,
}
fn parse_encryption(value: &str) -> Result<Arc<Cipher>, String> {
Cipher::try_from(value).map(Arc::new)
}
fn parse_handshake(value: &str) -> Result<Arc<CustomHandshake>, String> {
let values: Vec<&str> = value.split(',').collect();
if values.len() < 2 {
return Err("you should provide both request and response file paths".into());
}
let request = std::fs::read(values[0])
.map_err(|err| format!("{err}"))?
.into_boxed_slice();
let response = std::fs::read(values[1])
.map_err(|err| format!("{err}"))?
.into_boxed_slice();
Ok(Arc::new(CustomHandshake { request, response }))
}