Files
web-rust-template-project/src/cli.rs
2026-02-13 15:57:29 +08:00

235 lines
7.2 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/// 命令行参数和配置管理
/// 支持优先级CLI 参数 > 环境变量 > 配置文件 > 默认值
use clap::{Parser, ValueEnum};
use std::path::PathBuf;
/// 运行环境(强类型)
#[derive(ValueEnum, Clone, Debug)]
pub enum Environment {
/// 开发环境
Development,
/// 生产环境
Production,
}
impl Environment {
/// 转换为小写字符串
pub fn as_str(&self) -> &'static str {
match self {
Environment::Development => "development",
Environment::Production => "production",
}
}
}
/// 命令行参数
#[derive(Parser, Debug)]
#[command(name = "web-rust-template")]
#[command(about = "Web Server Template", long_about = None)]
#[command(author = "Your Name <your.email@example.com>")]
#[command(version = "0.1.0")]
#[command(propagate_version = true)]
pub struct CliArgs {
/// 指定配置文件路径
///
/// 支持相对路径和绝对路径
/// 例如:-c config/production.toml
#[arg(short, long, value_name = "FILE")]
pub config: Option<PathBuf>,
/// 指定运行环境
///
/// 自动加载对应环境的配置文件(如 config/development.toml
/// 可通过环境变量 ENV 设置
#[arg(
short = 'e',
long,
value_enum,
env = "ENV",
default_value = "development"
)]
pub env: Environment,
/// 指定服务器监听端口
///
/// 覆盖配置文件中的 port 设置
/// 可通过环境变量 SERVER_PORT 设置
#[arg(short, long, global = true, env = "SERVER_PORT")]
pub port: Option<u16>,
/// 指定服务器监听地址
///
/// 覆盖配置文件中的 host 设置
/// 可通过环境变量 SERVER_HOST 设置
#[arg(long, global = true, env = "SERVER_HOST")]
pub host: Option<String>,
/// 启用调试日志
///
/// 输出详细的日志信息,包括 SQL 查询
/// 可通过环境变量 DEBUG 设置
/// 注意:与 -v 冲突,推荐使用 -v/-vv/-vvv
#[arg(long, global = true, env = "DEBUG", conflicts_with = "verbose")]
pub debug: bool,
/// 工作目录
///
/// 指定配置文件和数据库的基准目录
#[arg(short, long, global = true)]
pub work_dir: Option<PathBuf>,
/// 显示详细日志(多级 verbose
///
/// -v : info 级别日志
/// -vv : debug 级别日志(等同于 --debug
/// -vvv : trace 级别日志(最详细)
#[arg(short, long, global = true, action = clap::ArgAction::Count)]
pub verbose: u8,
}
impl CliArgs {
/// 获取是否启用调试
pub fn is_debug_enabled(&self) -> bool {
self.debug || self.verbose >= 2
}
/// 获取日志级别
pub fn get_log_level(&self) -> &'static str {
if self.debug {
return "debug";
}
match self.verbose {
0 => "info",
1 => "debug",
_ => "trace",
}
}
/// 获取环境变量的日志过滤器(工程化版本)
pub fn get_log_filter(&self) -> String {
let level = self.get_log_level();
match level {
"trace" => "web_rust_template=trace,tower_http=trace,axum=trace,sqlx=debug".into(),
"debug" => "web_rust_template=debug,tower_http=debug,axum=debug,sqlx=debug".into(),
_ => "web_rust_template=info,tower_http=info,axum=info".into(),
}
}
/// 获取配置文件路径
///
/// 优先级:
/// 1. CLI 参数 --config
/// 2. 环境变量 CONFIG
/// 3. {work_dir}/config/{env}.toml
/// 4. ./config/{env}.toml
/// 5. ./config/default.toml
///
/// 如果找不到配置文件,返回 None允许仅使用环境变量运行
pub fn resolve_config_path(&self) -> Option<PathBuf> {
use std::env;
// 1. CLI 参数优先
if let Some(ref config) = self.config {
if config.exists() {
return Some(config.clone());
}
eprintln!("⚠ 警告:指定的配置文件不存在: {}", config.display());
eprintln!(" 将仅使用环境变量运行");
return None;
}
// 2. 环境变量
if let Ok(config_path) = env::var("CONFIG") {
let config = PathBuf::from(&config_path);
if config.exists() {
return Some(config);
}
eprintln!("⚠ 警告:环境变量 CONFIG 指定的配置文件不存在: {}", config_path);
eprintln!(" 将仅使用环境变量运行");
return None;
}
// 3-6. 查找配置文件
let work_dir = self
.work_dir
.clone()
.or_else(|| env::current_dir().ok())
.unwrap_or_else(|| PathBuf::from("."));
let env_name = self.env.as_str();
// 按优先级尝试的位置
let candidates = [
// 工作目录下的环境配置
work_dir.join("config").join(format!("{}.toml", env_name)),
// 当前目录的环境配置
PathBuf::from(format!("config/{}.toml", env_name)),
// 工作目录下的默认配置
work_dir.join("config").join("default.toml"),
// 当前目录的默认配置
PathBuf::from("config/default.toml"),
];
for candidate in &candidates {
if candidate.exists() {
// 使用 println! 而非 tracing::info!
println!("✓ Found configuration file: {}", candidate.display());
return Some(candidate.clone());
}
}
// 所有候选路径都找不到配置文件,返回 None
eprintln!(" 未找到配置文件,将仅使用环境变量和默认值");
None
}
/// 获取覆盖配置
///
/// CLI 参数可以覆盖配置文件中的值(仅 Web 服务器参数)
pub fn get_overrides(&self) -> ConfigOverrides {
ConfigOverrides {
host: self.host.clone(),
port: self.port,
}
}
/// 显示启动信息(工程化版本:打印实际解析的配置)
///
/// 使用 println! 而非 tracing::info!,因为 logger 可能尚未初始化
pub fn print_startup_info(&self) {
let separator = "=".repeat(60);
println!("{}", separator);
println!("Web Rust Template Server v0.1.0");
println!("Environment: {}", self.env.as_str());
// 打印实际解析的配置路径(而非 CLI 参数)
if let Some(config_path) = self.resolve_config_path() {
println!("Config file: {}", config_path.display());
} else {
println!("Config file: None (using environment variables)");
}
if let Some(ref work_dir) = self.work_dir {
println!("Work directory: {}", work_dir.display());
}
// 打印实际的日志级别
println!("Log level: {}", self.get_log_level());
if self.is_debug_enabled() {
println!("Debug mode: ENABLED");
}
println!("{}", separator);
}
}
/// CLI 参数覆盖的配置(仅 Web 服务器参数)
#[derive(Debug, Clone)]
pub struct ConfigOverrides {
/// Web 服务器主机覆盖
pub host: Option<String>,
/// Web 服务器端口覆盖
pub port: Option<u16>,
}