use std::path::PathBuf;
use clap::Parser;
use crate::{config::ByteSize, logging::LogLevel, progress::ProgressMode};
#[derive(Debug, Parser)]
#[command(version, about = "socat-inspired relay")]
pub struct Cli {
#[arg(
short,
long,
value_name = "PATH",
conflicts_with = "no_config",
help = "Configuration file to use."
)]
pub config: Option<PathBuf>,
#[arg(long, help = "Disable configuration file merging.")]
pub no_config: bool,
#[arg(long, help = "Render the final configuration as TOML.")]
pub dump_config: bool,
#[arg(
short = 'b',
long,
value_name = "SIZE",
help = "Bytes per copy, e.g. 65536 or 256KiB. One buffer per direction per connection."
)]
pub buffer_size: Option<ByteSize>,
#[arg(
short = 'f',
long = "from",
value_name = "ADDR",
help = "Source endpoint. Fills the first positional slot."
)]
pub source: Option<String>,
#[arg(
short = 't',
long = "to",
value_name = "ADDR",
help = "Sink endpoint. Fills the last positional slot."
)]
pub sink: Option<String>,
#[arg(
short = 'p',
long = "plugin",
value_name = "SPEC",
help = "Pipeline entry: NAME[:DIRECTION][,key=value...]. Repeatable, applied in order."
)]
pub plugins: Vec<String>,
#[arg(
long,
help = "Ignore plugins declared in the configuration file.",
conflicts_with = "no_config"
)]
pub no_plugins: bool,
#[arg(long, help = "List the plugins compiled into this binary and exit.")]
pub list_plugins: bool,
#[cfg(feature = "schema")]
#[arg(long, help = "Print the config file JSON schema to stdout and exit.")]
pub dump_schema: bool,
#[arg(
short = 'P',
long,
value_name = "WHEN",
value_enum,
num_args = 0..=1,
require_equals = true,
default_missing_value = "auto",
help = "Draw a progress line on stderr. Bare, or 'auto', draws \
only when stderr is a terminal; 'always' draws regardless."
)]
pub progress: Option<ProgressMode>,
#[arg(short, long, action = clap::ArgAction::Count, conflicts_with = "log_level", help = "Simple verbosity level.")]
pub verbose: u8,
#[arg(
long,
value_enum,
value_name = "LEVEL",
help = "Explicit verbosity level."
)]
pub log_level: Option<LogLevel>,
#[arg(
value_name = "SPEC",
help = "SOURCE [PLUGIN ...] SINK. The outer specs are endpoints; \
anything between them is a pipeline entry. Slots already \
filled by --from/--to are skipped."
)]
pub positional: Vec<String>,
}
#[derive(Debug, Default)]
pub struct Layout {
pub source: Option<String>,
pub sink: Option<String>,
pub plugins: Vec<String>,
}
impl Cli {
pub fn layout(&self) -> Layout {
let mut rest: Vec<String> = self.positional.clone();
let mut source = self.source.clone();
let mut sink = self.sink.clone();
if source.is_none() && !rest.is_empty() {
source = Some(rest.remove(0));
}
if sink.is_none() && !rest.is_empty() {
sink = rest.pop();
}
Layout {
source,
sink,
plugins: rest,
}
}
pub fn verbose_level(&self) -> Option<LogLevel> {
match self.verbose {
0 => None,
1 => Some(LogLevel::Debug),
_ => Some(LogLevel::Trace),
}
}
}