diff --git a/src/bin/local.rs b/src/bin/local.rs index e615e520..c054853e 100644 --- a/src/bin/local.rs +++ b/src/bin/local.rs @@ -4,19 +4,19 @@ //! or you could specify a configuration file. The format of configuration file is defined //! in mod `config`. -use clap::{App, Arg, ArgGroup}; +use clap::{Arg, clap_app}; use futures::future::{self, Either}; use log::{error, info}; use tokio::{self, runtime::Builder}; use shadowsocks::{ acl::AccessControl, - crypto::CipherType, - plugin::PluginConfig, - run_local, Config, ConfigType, + crypto::CipherType, Mode, + plugin::PluginConfig, + run_local, ServerAddr, ServerConfig, }; @@ -27,102 +27,39 @@ mod monitor; fn main() { let available_ciphers = CipherType::available_ciphers(); - let mut app = App::new("shadowsocks") - .version(shadowsocks::VERSION) - .about("A fast tunnel proxy that helps you bypass firewalls.") - .arg( - Arg::with_name("VERBOSE") - .short("v") - .multiple(true) - .help("Set the level of debug"), + let mut app = clap_app!(shadowsocks => + (version: shadowsocks::VERSION) + (about: "A fast tunnel proxy that helps you bypass firewalls.") + (@arg VERBOSE: -v ... "Set the level of debug") + (@arg UDP_ONLY: -u conflicts_with[TCP_AND_UDP] "Server mode UDP_ONLY") + (@arg TCP_AND_UDP: -U conflicts_with[UDP_ONLY] "Server mode TCP_AND_UDP") + (@arg CONFIG: -c --config +takes_value "Specify config file") + (@arg LOCAL_ADDR: -b --("local-addr") +takes_value "Local address, listen only to this address if specified") + (@arg SERVER_ADDR: -s --("server-addr") +takes_value requires[PASSWORD ENCRYPT_METHOD] "Server address") + (@arg PASSWORD: -k --password +takes_value requires[SERVER_ADDR ENCRYPT_METHOD] "Password") + (@arg PLUGIN: --plugin +takes_value "Enable SIP003 plugin") + (@arg PLUGIN_OPT: --("plugin-opts") +takes_value requires[PLUGIN] "Set SIP003 plugin options") + (@arg URL: --("server-url") +takes_value "Server address in SIP002 URL") + (@group SERVER_CONFIG => + (@attributes +required ... arg[CONFIG SERVER_ADDR URL]) ) - .arg( - Arg::with_name("UDP_ONLY") - .short("u") - .help("Server mode UDP_ONLY") - .conflicts_with("TCP_AND_UDP"), - ) - .arg( - Arg::with_name("TCP_AND_UDP") - .short("U") - .help("Server mode TCP_AND_UDP") - .conflicts_with("UDP_ONLY"), - ) - .arg( - Arg::with_name("CONFIG") - .short("c") - .long("config") - .takes_value(true) - .help("Specify config file"), - ) - .arg( - Arg::with_name("LOCAL_ADDR") - .short("b") - .long("local-addr") - .takes_value(true) - .help("Local address, listen only to this address if specified"), - ) - .arg( - Arg::with_name("SERVER_ADDR") - .short("s") - .long("server-addr") - .takes_value(true) - .help("Server address") - .requires_all(&["PASSWORD", "ENCRYPT_METHOD"]), - ) - .arg( - Arg::with_name("PASSWORD") - .short("k") - .long("password") - .takes_value(true) - .help("Password") - .requires_all(&["SERVER_ADDR", "ENCRYPT_METHOD"]), + (@group LOCAL_CONFIG => + (@attributes +required ... arg[CONFIG LOCAL_ADDR]) ) + (@arg NO_DELAY: --("no-delay") !takes_value "Set no-delay option for socket") + (@arg NOFILE: -n --nofile +takes_value "Set RLIMIT_NOFILE with both soft and hard limit (only for *nix systems)") + (@arg ACL: --acl +takes_value "Path to ACL (Access Control List)") + ); + + app = app .arg( Arg::with_name("ENCRYPT_METHOD") .short("m") .long("encrypt-method") .takes_value(true) .possible_values(&available_ciphers) - .help("Encryption method") - .requires_all(&["SERVER_ADDR", "PASSWORD"]), - ) - .arg( - Arg::with_name("PLUGIN") - .long("plugin") - .takes_value(true) - .help("Enable SIP003 plugin"), - ) - .arg( - Arg::with_name("PLUGIN_OPT") - .long("plugin-opts") - .takes_value(true) - .help("Set SIP003 plugin options") - .requires("PLUGIN"), - ) - .arg( - Arg::with_name("URL") - .long("server-url") - .takes_value(true) - .help("Server address in SIP002 URL"), - ) - .group( - ArgGroup::with_name("SERVER_CONFIG") - .args(&["CONFIG", "SERVER_ADDR", "URL"]) - .multiple(true) - .required(true), - ) - .group( - ArgGroup::with_name("LOCAL_CONFIG") - .args(&["CONFIG", "LOCAL_ADDR"]) - .multiple(true) - .required(true), - ) - .arg( - Arg::with_name("NO_DELAY") - .long("no-delay") - .takes_value(false) - .help("Set no-delay option for socket"), + .requires_all(&["SERVER_ADDR", "PASSWORD"]) + .help("Encryption method"), ) .arg( Arg::with_name("PROTOCOL") @@ -136,19 +73,6 @@ fn main() { ]) .help("Protocol that uses to communicates with clients"), ) - .arg( - Arg::with_name("NOFILE") - .short("n") - .long("nofile") - .takes_value(true) - .help("Set RLIMIT_NOFILE with both soft and hard limit (only for *nix systems)"), - ) - .arg( - Arg::with_name("ACL") - .long("acl") - .takes_value(true) - .help("Path to ACL (Access Control List)"), - ) .arg( Arg::with_name("IPV6_FIRST") .short("6") diff --git a/src/bin/manager.rs b/src/bin/manager.rs index 9bc29fa1..1a60e85e 100644 --- a/src/bin/manager.rs +++ b/src/bin/manager.rs @@ -9,7 +9,7 @@ use std::net::{IpAddr, SocketAddr}; -use clap::{App, Arg, ArgGroup}; +use clap::{Arg, clap_app}; use futures::future::{self, Either}; use log::{error, info}; use tokio::{self, runtime::Builder}; @@ -29,41 +29,24 @@ mod logging; mod monitor; fn main() { - let matches = App::new("shadowsocks") - .version(shadowsocks::VERSION) - .about("A fast tunnel proxy that helps you bypass firewalls.") - .arg( - Arg::with_name("VERBOSE") - .short("v") - .multiple(true) - .help("Set the level of debug"), - ) - .arg( - Arg::with_name("UDP_ONLY") - .short("u") - .help("Server mode UDP_ONLY") - .conflicts_with("TCP_AND_UDP"), - ) - .arg( - Arg::with_name("TCP_AND_UDP") - .short("U") - .help("Server mode TCP_AND_UDP") - .conflicts_with("UDP_ONLY"), - ) - .arg( - Arg::with_name("CONFIG") - .short("c") - .long("config") - .takes_value(true) - .help("Specify config file"), - ) - .arg( - Arg::with_name("BIND_ADDR") - .short("b") - .long("bind-addr") - .takes_value(true) - .help("Bind address, outbound socket will bind this address"), + let app = clap_app!(shadowsocks => + (version: shadowsocks::VERSION) + (about: "A fast tunnel proxy that helps you bypass firewalls.") + (@arg VERBOSE: -v ... "Set the level of debug") + (@arg UDP_ONLY: -u conflicts_with[TCP_AND_UDP] "Server mode UDP_ONLY") + (@arg TCP_AND_UDP: -U conflicts_with[UDP_ONLY] "Server mode TCP_AND_UDP") + (@arg CONFIG: -c --config +takes_value "Specify config file") + (@arg BIND_ADDR: -b --("bind-addr") +takes_value "Bind address, outbound socket will bind this address") + (@arg NO_DELAY: --("no-delay") !takes_value "Set no-delay option for socket") + (@arg MANAGER_ADDRESS: --("manager-address") +takes_value "ShadowSocks Manager (ssmgr) address, could be \"IP:Port\", \"Domain:Port\" or \"/path/to/unix.sock\"") + (@group MANAGER_CONFIG => + (@attributes +required ... arg[CONFIG MANAGER_ADDRESS]) ) + (@arg NOFILE: -n --nofile +takes_value "Set RLIMIT_NOFILE with both soft and hard limit (only for *nix systems)") + (@arg ACL: --acl +takes_value "Path to ACL (Access Control List)") + ); + + let matches = app .arg( Arg::with_name("ENCRYPT_METHOD") .short("m") @@ -72,37 +55,6 @@ fn main() { .possible_values(&CipherType::available_ciphers()) .help("Encryption method"), ) - .arg( - Arg::with_name("NO_DELAY") - .long("no-delay") - .takes_value(false) - .help("Set no-delay option for socket"), - ) - .arg( - Arg::with_name("MANAGER_ADDRESS") - .long("manager-address") - .takes_value(true) - .help("ShadowSocks Manager (ssmgr) address, could be \"IP:Port\", \"Domain:Port\" or \"/path/to/unix.sock\""), - ) - .group( - ArgGroup::with_name("MANAGER_CONFIG") - .args(&["CONFIG", "MANAGER_ADDRESS"]) - .multiple(true) - .required(true) - ) - .arg( - Arg::with_name("NOFILE") - .short("n") - .long("nofile") - .takes_value(true) - .help("Set RLIMIT_NOFILE with both soft and hard limit (only for *nix systems)"), - ) - .arg( - Arg::with_name("ACL") - .long("acl") - .takes_value(true) - .help("Path to ACL (Access Control List)"), - ) .arg( Arg::with_name("IPV6_FIRST") .short("6") diff --git a/src/bin/redir.rs b/src/bin/redir.rs index 5623736f..eb8ddb23 100644 --- a/src/bin/redir.rs +++ b/src/bin/redir.rs @@ -4,7 +4,7 @@ //! or you could specify a configuration file. The format of configuration file is defined //! in mod `config`. -use clap::{App, Arg, ArgGroup}; +use clap::{Arg, clap_app}; use futures::future::{self, Either}; use log::{error, info}; use tokio::{self, runtime::Builder}; @@ -29,57 +29,31 @@ mod monitor; fn main() { let available_ciphers = CipherType::available_ciphers(); - let mut app = App::new("shadowsocks") - .version(shadowsocks::VERSION) - .about("A fast tunnel proxy that helps you bypass firewalls.") - .arg( - Arg::with_name("VERBOSE") - .short("v") - .multiple(true) - .help("Set the level of debug"), + let mut app = clap_app!(shadowsocks => + (version: shadowsocks::VERSION) + (about: "A fast tunnel proxy that helps you bypass firewalls.") + (@arg VERBOSE: -v ... "Set the level of debug") + (@arg UDP_ONLY: -u conflicts_with[TCP_AND_UDP] "Server mode UDP_ONLY") + (@arg TCP_AND_UDP: -U conflicts_with[UDP_ONLY] "Server mode TCP_AND_UDP") + (@arg CONFIG: -c --config +takes_value "Specify config file") + (@arg LOCAL_ADDR: -b --("local-addr") +takes_value "Local address, listen only to this address if specified") + (@arg SERVER_ADDR: -s --("server-addr") +takes_value requires[PASSWORD ENCRYPT_METHOD] "Server address") + (@arg PASSWORD: -k --password +takes_value requires[SERVER_ADDR ENCRYPT_METHOD] "Password") + (@arg PLUGIN: --plugin +takes_value "Enable SIP003 plugin") + (@arg PLUGIN_OPT: --("plugin-opts") +takes_value requires[PLUGIN] "Set SIP003 plugin options") + (@arg URL: --("server-url") +takes_value "Server address in SIP002 URL") + (@group SERVER_CONFIG => + (@attributes +required ... arg[CONFIG SERVER_ADDR URL]) ) - .arg( - Arg::with_name("UDP_ONLY") - .short("u") - .help("Server mode UDP_ONLY") - .conflicts_with("TCP_AND_UDP"), - ) - .arg( - Arg::with_name("TCP_AND_UDP") - .short("U") - .help("Server mode TCP_AND_UDP") - .conflicts_with("UDP_ONLY"), - ) - .arg( - Arg::with_name("CONFIG") - .short("c") - .long("config") - .takes_value(true) - .help("Specify config file"), - ) - .arg( - Arg::with_name("LOCAL_ADDR") - .short("b") - .long("local-addr") - .takes_value(true) - .help("Local address, listen only to this address if specified"), - ) - .arg( - Arg::with_name("SERVER_ADDR") - .short("s") - .long("server-addr") - .takes_value(true) - .help("Server address") - .requires_all(&["PASSWORD", "ENCRYPT_METHOD"]), - ) - .arg( - Arg::with_name("PASSWORD") - .short("k") - .long("password") - .takes_value(true) - .help("Password") - .requires_all(&["SERVER_ADDR", "ENCRYPT_METHOD"]), + (@group LOCAL_CONFIG => + (@attributes +required ... arg[CONFIG LOCAL_ADDR]) ) + (@arg NO_DELAY: --("no-delay") !takes_value "Set no-delay option for socket") + (@arg NOFILE: -n --nofile +takes_value "Set RLIMIT_NOFILE with both soft and hard limit (only for *nix systems)") + (@arg ACL: --acl +takes_value "Path to ACL (Access Control List)") + ); + + app = app .arg( Arg::with_name("ENCRYPT_METHOD") .short("m") @@ -89,56 +63,6 @@ fn main() { .help("Encryption method") .requires_all(&["SERVER_ADDR", "PASSWORD"]), ) - .arg( - Arg::with_name("PLUGIN") - .long("plugin") - .takes_value(true) - .help("Enable SIP003 plugin"), - ) - .arg( - Arg::with_name("PLUGIN_OPT") - .long("plugin-opts") - .takes_value(true) - .help("Set SIP003 plugin options") - .requires("PLUGIN"), - ) - .arg( - Arg::with_name("URL") - .long("server-url") - .takes_value(true) - .help("Server address in SIP002 URL"), - ) - .group( - ArgGroup::with_name("SERVER_CONFIG") - .args(&["CONFIG", "SERVER_ADDR", "URL"]) - .multiple(true) - .required(true), - ) - .group( - ArgGroup::with_name("LOCAL_CONFIG") - .args(&["CONFIG", "LOCAL_ADDR"]) - .multiple(true) - .required(true), - ) - .arg( - Arg::with_name("NO_DELAY") - .long("no-delay") - .takes_value(false) - .help("Set no-delay option for socket"), - ) - .arg( - Arg::with_name("NOFILE") - .short("n") - .long("nofile") - .takes_value(true) - .help("Set RLIMIT_NOFILE with both soft and hard limit (only for *nix systems)"), - ) - .arg( - Arg::with_name("ACL") - .long("acl") - .takes_value(true) - .help("Path to ACL (Access Control List)"), - ) .arg( Arg::with_name("IPV6_FIRST") .short("6") diff --git a/src/bin/server.rs b/src/bin/server.rs index 0ab0de25..d6bc37ec 100644 --- a/src/bin/server.rs +++ b/src/bin/server.rs @@ -9,7 +9,7 @@ use std::net::{IpAddr, SocketAddr}; -use clap::{App, Arg, ArgGroup}; +use clap::{Arg, clap_app}; use futures::future::{self, Either}; use log::{error, info}; use tokio::{self, runtime::Builder}; @@ -33,57 +33,28 @@ mod monitor; fn main() { let available_ciphers = CipherType::available_ciphers(); - let matches = App::new("shadowsocks") - .version(shadowsocks::VERSION) - .about("A fast tunnel proxy that helps you bypass firewalls.") - .arg( - Arg::with_name("VERBOSE") - .short("v") - .multiple(true) - .help("Set the level of debug"), - ) - .arg( - Arg::with_name("UDP_ONLY") - .short("u") - .help("Server mode UDP_ONLY") - .conflicts_with("TCP_AND_UDP"), - ) - .arg( - Arg::with_name("TCP_AND_UDP") - .short("U") - .help("Server mode TCP_AND_UDP") - .conflicts_with("UDP_ONLY"), - ) - .arg( - Arg::with_name("CONFIG") - .short("c") - .long("config") - .takes_value(true) - .help("Specify config file"), - ) - .arg( - Arg::with_name("BIND_ADDR") - .short("b") - .long("bind-addr") - .takes_value(true) - .help("Bind address, outbound socket will bind this address"), - ) - .arg( - Arg::with_name("SERVER_ADDR") - .short("s") - .long("server-addr") - .takes_value(true) - .help("Server address") - .requires_all(&["PASSWORD", "ENCRYPT_METHOD"]), - ) - .arg( - Arg::with_name("PASSWORD") - .short("k") - .long("password") - .takes_value(true) - .help("Password") - .requires_all(&["SERVER_ADDR", "ENCRYPT_METHOD"]), + let app = clap_app!(shadowsocks => + (version: shadowsocks::VERSION) + (about: "A fast tunnel proxy that helps you bypass firewalls.") + (@arg VERBOSE: -v ... "Set the level of debug") + (@arg UDP_ONLY: -u conflicts_with[TCP_AND_UDP] "Server mode UDP_ONLY") + (@arg TCP_AND_UDP: -U conflicts_with[UDP_ONLY] "Server mode TCP_AND_UDP") + (@arg CONFIG: -c --config +takes_value "Specify config file") + (@arg BIND_ADDR: -b --("bind-addr") +takes_value "Bind address, outbound socket will bind this address") + (@arg SERVER_ADDR: -s --("server-addr") +takes_value requires[PASSWORD ENCRYPT_METHOD] "Server address") + (@arg PASSWORD: -k --password +takes_value requires[SERVER_ADDR ENCRYPT_METHOD] "Password") + (@arg PLUGIN: --plugin +takes_value "Enable SIP003 plugin") + (@arg PLUGIN_OPT: --("plugin-opts") +takes_value requires[PLUGIN] "Set SIP003 plugin options") + (@group SERVER_CONFIG => + (@attributes +required ... arg[CONFIG SERVER_ADDR URL]) ) + (@arg MANAGER_ADDRESS: --("manager-address") +takes_value "ShadowSocks Manager (ssmgr) address, could be \"IP:Port\", \"Domain:Port\" or \"/path/to/unix.sock\"") + (@arg NO_DELAY: --("no-delay") !takes_value "Set no-delay option for socket") + (@arg NOFILE: -n --nofile +takes_value "Set RLIMIT_NOFILE with both soft and hard limit (only for *nix systems)") + (@arg ACL: --acl +takes_value "Path to ACL (Access Control List)") + ); + + let matches = app .arg( Arg::with_name("ENCRYPT_METHOD") .short("m") @@ -93,50 +64,6 @@ fn main() { .help("Encryption method") .requires_all(&["SERVER_ADDR", "PASSWORD"]), ) - .arg( - Arg::with_name("PLUGIN") - .long("plugin") - .takes_value(true) - .help("Enable SIP003 plugin"), - ) - .arg( - Arg::with_name("PLUGIN_OPT") - .long("plugin-opts") - .takes_value(true) - .help("Set SIP003 plugin options") - .requires("PLUGIN"), - ) - .group( - ArgGroup::with_name("SERVER_CONFIG") - .args(&["CONFIG", "SERVER_ADDR"]) - .multiple(true) - .required(true), - ) - .arg( - Arg::with_name("MANAGER_ADDRESS") - .long("manager-address") - .takes_value(true) - .help("ShadowSocks Manager (ssmgr) address, could be \"IP:Port\", \"Domain:Port\" or \"/path/to/unix.sock\""), - ) - .arg( - Arg::with_name("NO_DELAY") - .long("no-delay") - .takes_value(false) - .help("Set no-delay option for socket"), - ) - .arg( - Arg::with_name("NOFILE") - .short("n") - .long("nofile") - .takes_value(true) - .help("Set RLIMIT_NOFILE with both soft and hard limit (only for *nix systems)"), - ) - .arg( - Arg::with_name("ACL") - .long("acl") - .takes_value(true) - .help("Path to ACL (Access Control List)"), - ) .arg( Arg::with_name("IPV6_FIRST") .short("6") diff --git a/src/bin/ssurl.rs b/src/bin/ssurl.rs index 4490a846..d6bf0170 100644 --- a/src/bin/ssurl.rs +++ b/src/bin/ssurl.rs @@ -3,12 +3,11 @@ //! SS-URI = "ss://" userinfo "@" hostname ":" port [ "/" ] [ "?" plugin ] [ "#" tag ] //! userinfo = websafe-base64-encode-utf8(method ":" password) -use clap::{App, Arg}; +use clap::clap_app; use qrcode::{types::Color, QrCode}; use shadowsocks::{ - config::{Config, ConfigType, ServerConfig}, - VERSION, + config::{Config, ConfigType, ServerConfig} }; const BLACK: &str = "\x1b[40m \x1b[0m"; @@ -70,30 +69,14 @@ fn decode(encoded: &str, need_qrcode: bool) { } fn main() { - let app = App::new("ssurl") - .about("Encode and decode ShadowSocks URL") - .version(VERSION) - .arg( - Arg::with_name("ENCODE") - .short("e") - .long("encode") - .takes_value(true) - .help("Encode the server configuration in the provided JSON file"), - ) - .arg( - Arg::with_name("DECODE") - .short("d") - .long("decode") - .takes_value(true) - .help("Decode the server configuration from the provide ShadowSocks URL"), - ) - .arg( - Arg::with_name("QRCODE") - .short("c") - .long("qrcode") - .takes_value(false) - .help("Generate the QRCode with the provided configuration"), - ); + let app = clap_app!(ssurl => + (version: shadowsocks::VERSION) + (about: "Encode and decode ShadowSocks URL") + (@arg ENCODE: -e --encode +takes_value "Encode the server configuration in the provided JSON file") + (@arg DECODE: -d --decode +takes_value "Decode the server configuration from the provide ShadowSocks URL") + (@arg QRCODE: -c --qrcode !takes_value "Generate the QRCode with the provided configuration") + ); + let matches = app.get_matches(); let need_qrcode = matches.is_present("QRCODE"); diff --git a/src/bin/tunnel.rs b/src/bin/tunnel.rs index a0dcf8b0..a89b2522 100644 --- a/src/bin/tunnel.rs +++ b/src/bin/tunnel.rs @@ -4,7 +4,7 @@ //! or you could specify a configuration file. The format of configuration file is defined //! in mod `config`. -use clap::{App, Arg, ArgGroup}; +use clap::{Arg, clap_app}; use futures::future::{self, Either}; use log::{error, info}; use tokio::{self, runtime::Builder}; @@ -27,65 +27,32 @@ mod monitor; fn main() { let available_ciphers = CipherType::available_ciphers(); - let matches = App::new("shadowsocks") - .version(shadowsocks::VERSION) - .about("A fast tunnel proxy that helps you bypass firewalls.") - .arg( - Arg::with_name("VERBOSE") - .short("v") - .multiple(true) - .help("Set the level of debug"), + let app = clap_app!(shadowsocks => + (version: shadowsocks::VERSION) + (about: "A fast tunnel proxy that helps you bypass firewalls.") + (@arg VERBOSE: -v ... "Set the level of debug") + (@arg UDP_ONLY: -u conflicts_with[TCP_AND_UDP] "Server mode UDP_ONLY") + (@arg TCP_AND_UDP: -U conflicts_with[UDP_ONLY] "Server mode TCP_AND_UDP") + (@arg CONFIG: -c --config +takes_value "Specify config file") + (@arg LOCAL_ADDR: -b --("local-addr") +takes_value "Local address, listen only to this address if specified") + (@arg FORWARD_ADDR: -f --("foward-addr") +takes_value +required "Forward address, forward to this address") + (@arg SERVER_ADDR: -s --("server-addr") +takes_value requires[PASSWORD ENCRYPT_METHOD] "Server address") + (@arg PASSWORD: -k --password +takes_value requires[SERVER_ADDR ENCRYPT_METHOD] "Password") + (@arg PLUGIN: --plugin +takes_value "Enable SIP003 plugin") + (@arg PLUGIN_OPT: --("plugin-opts") +takes_value requires[PLUGIN] "Set SIP003 plugin options") + (@arg URL: --("server-url") +takes_value "Server address in SIP002 URL") + (@group SERVER_CONFIG => + (@attributes +required ... arg[CONFIG SERVER_ADDR URL]) ) - .arg( - Arg::with_name("UDP_ONLY") - .short("u") - .help("Server mode UDP_ONLY") - .conflicts_with("TCP_AND_UDP"), - ) - .arg( - Arg::with_name("TCP_AND_UDP") - .short("U") - .help("Server mode TCP_AND_UDP") - .conflicts_with("UDP_ONLY"), - ) - .arg( - Arg::with_name("CONFIG") - .short("c") - .long("config") - .takes_value(true) - .help("Specify config file"), - ) - .arg( - Arg::with_name("LOCAL_ADDR") - .short("b") - .long("local-addr") - .takes_value(true) - .help("Local address, listen only to this address if specified"), - ) - .arg( - Arg::with_name("FORWARD_ADDR") - .short("f") - .long("foward-addr") - .takes_value(true) - .required(true) - .help("Forward address, forward to this address"), - ) - .arg( - Arg::with_name("SERVER_ADDR") - .short("s") - .long("server-addr") - .takes_value(true) - .help("Server address") - .requires_all(&["PASSWORD", "ENCRYPT_METHOD"]), - ) - .arg( - Arg::with_name("PASSWORD") - .short("k") - .long("password") - .takes_value(true) - .help("Password") - .requires_all(&["SERVER_ADDR", "ENCRYPT_METHOD"]), + (@group LOCAL_CONFIG => + (@attributes +required ... arg[CONFIG LOCAL_ADDR]) ) + (@arg NO_DELAY: --("no-delay") !takes_value "Set no-delay option for socket") + (@arg NOFILE: -n --nofile +takes_value "Set RLIMIT_NOFILE with both soft and hard limit (only for *nix systems)") + ); + + + let matches = app .arg( Arg::with_name("ENCRYPT_METHOD") .short("m") @@ -95,50 +62,6 @@ fn main() { .help("Encryption method") .requires_all(&["SERVER_ADDR", "PASSWORD"]), ) - .arg( - Arg::with_name("PLUGIN") - .long("plugin") - .takes_value(true) - .help("Enable SIP003 plugin"), - ) - .arg( - Arg::with_name("PLUGIN_OPT") - .long("plugin-opts") - .takes_value(true) - .help("Set SIP003 plugin options") - .requires("PLUGIN"), - ) - .arg( - Arg::with_name("URL") - .long("server-url") - .takes_value(true) - .help("Server address in SIP002 URL"), - ) - .group( - ArgGroup::with_name("SERVER_CONFIG") - .args(&["CONFIG", "SERVER_ADDR", "URL"]) - .multiple(true) - .required(true), - ) - .group( - ArgGroup::with_name("LOCAL_CONFIG") - .args(&["CONFIG", "LOCAL_ADDR"]) - .multiple(true) - .required(true), - ) - .arg( - Arg::with_name("NO_DELAY") - .long("no-delay") - .takes_value(false) - .help("Set no-delay option for socket"), - ) - .arg( - Arg::with_name("NOFILE") - .short("n") - .long("nofile") - .takes_value(true) - .help("Set RLIMIT_NOFILE with both soft and hard limit (only for *nix systems)"), - ) .arg( Arg::with_name("IPV6_FIRST") .short("6")