ombrac_server/config/
cli.rs

1use std::net::SocketAddr;
2use std::path::PathBuf;
3
4use clap::Parser;
5use clap::builder::Styles;
6use clap::builder::styling::{AnsiColor, Style};
7
8use ombrac_transport::quic::Congestion;
9
10use crate::config::{TlsMode, TransportConfig};
11
12/// Command-line arguments for the ombrac server
13#[derive(Parser, Debug)]
14#[command(version, about, long_about = None, styles = styles())]
15pub struct Args {
16    /// Path to the JSON configuration file
17    #[clap(long, short = 'c', value_name = "FILE")]
18    pub config: Option<PathBuf>,
19
20    /// Protocol Secret
21    #[clap(
22        long,
23        short = 'k',
24        help_heading = "Required",
25        value_name = "STR",
26        required_unless_present = "config"
27    )]
28    pub secret: Option<String>,
29
30    /// Address to bind the server
31    #[clap(
32        long,
33        short = 'l',
34        help_heading = "Required",
35        value_name = "ADDR",
36        required_unless_present = "config"
37    )]
38    pub listen: Option<SocketAddr>,
39
40    #[clap(flatten)]
41    pub transport: CliTransportConfig,
42
43    #[cfg(feature = "tracing")]
44    #[clap(flatten)]
45    pub logging: CliLoggingConfig,
46}
47
48/// CLI-specific transport configuration
49#[derive(Parser, Debug, Clone)]
50pub struct CliTransportConfig {
51    /// Set the TLS mode for the connection
52    #[clap(long, value_enum, help_heading = "Transport")]
53    pub tls_mode: Option<TlsMode>,
54
55    /// Path to the Certificate Authority (CA) certificate file
56    /// in 'TLS' mode, if not provided, the system's default root certificates are used
57    #[clap(long, help_heading = "Transport", value_name = "FILE")]
58    pub ca_cert: Option<PathBuf>,
59
60    /// Path to the server's TLS certificate
61    #[clap(long, help_heading = "Transport", value_name = "FILE")]
62    pub tls_cert: Option<PathBuf>,
63
64    /// Path to the server's TLS private key
65    #[clap(long, help_heading = "Transport", value_name = "FILE")]
66    pub tls_key: Option<PathBuf>,
67
68    /// Enable 0-RTT for faster connection establishment
69    #[clap(long, help_heading = "Transport", value_name = "BOOL")]
70    pub zero_rtt: Option<bool>,
71
72    /// Application-Layer protocol negotiation (ALPN) protocols [default: h3]
73    #[clap(
74        long,
75        help_heading = "Transport",
76        value_name = "PROTOCOLS",
77        value_delimiter = ','
78    )]
79    pub alpn_protocols: Option<Vec<Vec<u8>>>,
80
81    /// Congestion control algorithm to use (e.g. bbr, cubic, newreno) [default: bbr]
82    #[clap(long, help_heading = "Transport", value_name = "ALGORITHM")]
83    pub congestion: Option<Congestion>,
84
85    /// Initial congestion window size in bytes
86    #[clap(long, help_heading = "Transport", value_name = "NUM")]
87    pub cwnd_init: Option<u64>,
88
89    /// Maximum idle time (in milliseconds) before closing the connection [default: 30000]
90    #[clap(long, help_heading = "Transport", value_name = "TIME")]
91    pub idle_timeout: Option<u64>,
92
93    /// Keep-alive interval (in milliseconds) [default: 8000]
94    #[clap(long, help_heading = "Transport", value_name = "TIME")]
95    pub keep_alive: Option<u64>,
96
97    /// Maximum number of bidirectional streams that can be open simultaneously [default: 1000]
98    #[clap(long, help_heading = "Transport", value_name = "NUM")]
99    pub max_streams: Option<u64>,
100}
101
102/// CLI-specific logging configuration
103#[cfg(feature = "tracing")]
104#[derive(Parser, Debug, Clone)]
105pub struct CliLoggingConfig {
106    /// Logging level (e.g., INFO, WARN, ERROR) [default: INFO]
107    #[clap(long, help_heading = "Logging", value_name = "LEVEL")]
108    pub log_level: Option<String>,
109}
110
111impl CliTransportConfig {
112    /// Convert CLI transport config to internal TransportConfig
113    pub fn into_transport_config(self) -> TransportConfig {
114        TransportConfig {
115            tls_mode: self.tls_mode,
116            ca_cert: self.ca_cert,
117            tls_cert: self.tls_cert,
118            tls_key: self.tls_key,
119            zero_rtt: self.zero_rtt,
120            alpn_protocols: self.alpn_protocols,
121            congestion: self.congestion,
122            cwnd_init: self.cwnd_init,
123            idle_timeout: self.idle_timeout,
124            keep_alive: self.keep_alive,
125            max_streams: self.max_streams,
126        }
127    }
128}
129
130#[cfg(feature = "tracing")]
131impl CliLoggingConfig {
132    /// Convert CLI logging config to internal LoggingConfig
133    pub fn into_logging_config(self) -> crate::config::LoggingConfig {
134        crate::config::LoggingConfig {
135            log_level: self.log_level,
136        }
137    }
138}
139
140/// Represents CLI arguments as a partial configuration
141#[derive(Debug)]
142pub struct CliConfig {
143    pub secret: Option<String>,
144    pub listen: Option<SocketAddr>,
145    pub transport: TransportConfig,
146    #[cfg(feature = "tracing")]
147    pub logging: crate::config::LoggingConfig,
148}
149
150impl Args {
151    /// Parse CLI arguments and convert to CliConfig
152    pub fn parse_to_config() -> CliConfig {
153        let args = Self::parse();
154        CliConfig {
155            secret: args.secret,
156            listen: args.listen,
157            transport: args.transport.into_transport_config(),
158            #[cfg(feature = "tracing")]
159            logging: args.logging.into_logging_config(),
160        }
161    }
162
163    /// Get the config file path if specified
164    pub fn config_path() -> Option<PathBuf> {
165        Self::parse().config
166    }
167}
168
169fn styles() -> Styles {
170    Styles::styled()
171        .header(Style::new().bold().fg_color(Some(AnsiColor::Green.into())))
172        .usage(Style::new().bold().fg_color(Some(AnsiColor::Green.into())))
173        .literal(Style::new().bold().fg_color(Some(AnsiColor::Cyan.into())))
174        .placeholder(Style::new().fg_color(Some(AnsiColor::Cyan.into())))
175        .valid(Style::new().bold().fg_color(Some(AnsiColor::Cyan.into())))
176        .invalid(Style::new().bold().fg_color(Some(AnsiColor::Yellow.into())))
177        .error(Style::new().bold().fg_color(Some(AnsiColor::Red.into())))
178}