diff --git a/clash/src/main.rs b/clash/src/main.rs index ce27910da..b2d6fbd34 100644 --- a/clash/src/main.rs +++ b/clash/src/main.rs @@ -2,7 +2,10 @@ extern crate clash_lib as clash; use clap::Parser; use clash::TokioRuntime; -use std::path::{Path, PathBuf}; +use std::{ + path::{Path, PathBuf}, + process::exit, +}; #[derive(Parser)] #[clap(author, version, about, long_about = None)] @@ -13,11 +16,21 @@ struct Cli { #[clap( short, long, + visible_short_aliases = ['f'], // -f is used by clash, it is a compatibility option value_parser, value_name = "FILE", - default_value = "config.yaml" + default_value = "config.yaml", + help = "Specify configuration file" )] config: PathBuf, + #[clap( + short = 't', + long, + value_parser, + default_value = "false", + help = "Test configuration and exit" + )] + test_config: bool, } fn main() { @@ -31,9 +44,21 @@ fn main() { .to_string(); if !Path::new(&file).exists() { + // TODO: offer a internal default config, to compatible with clash behavior panic!("config file not found: {}", file); } - + if cli.test_config { + match clash::Config::File(file.clone()).try_parse() { + Ok(_) => { + println!("configuration file {} test is successful", file); + exit(0); + } + Err(e) => { + eprintln!(" configuration file {} test failed: {}", file, e); + exit(1); + } + } + } clash::start(clash::Options { config: clash::Config::File(file), cwd: cli.directory.map(|x| x.to_string_lossy().to_string()), diff --git a/clash_lib/src/lib.rs b/clash_lib/src/lib.rs index 237eaa2bb..fa4a13246 100644 --- a/clash_lib/src/lib.rs +++ b/clash_lib/src/lib.rs @@ -78,6 +78,17 @@ pub enum Config { Str(String), } +impl Config { + pub fn try_parse(self) -> Result { + match self { + Config::Def(c) => c.try_into(), + Config::Internal(c) => Ok(c), + Config::File(file) => TryInto::::try_into(PathBuf::from(file))?.try_into(), + Config::Str(s) => s.parse::()?.try_into(), + } + } +} + pub struct GlobalState { log_level: LogLevel, inbound_listener_handle: Option>>, @@ -124,12 +135,7 @@ async fn start_async(opts: Options) -> Result<(), Error> { RUNTIME_CONTROLLER.set(std::sync::RwLock::new(RuntimeController { shutdown_tx })); - let config: InternalConfig = match opts.config { - Config::Def(c) => c.try_into()?, - Config::Internal(c) => c, - Config::File(file) => TryInto::::try_into(PathBuf::from(file))?.try_into()?, - Config::Str(s) => s.parse::()?.try_into()?, - }; + let config: InternalConfig = opts.config.try_parse()?; let cwd = opts.cwd.unwrap_or_else(|| ".".to_string()); let cwd = std::path::Path::new(&cwd);