Skip to content

Commit b80a580

Browse files
committed
feat(common): 提取公共环境变量加载模块
- 创建独立的 file_classification_common crate 用于共享环境变量加载逻辑 - 在 CLI 和 Web API 项目中移除重复的环境变量加载代码 - 引入 dotenvy 依赖到公共模块,统一管理环境配置加载 - 更新 Cargo.toml 和 Cargo.lock 以反映新的依赖结构 - 重构 Web API 静态文件服务,支持从嵌入资源和物理目录双重加 - 添加静态资源目录查找逻辑,提高程序运行时的路径兼容性 - 实现嵌入资源 fallback 机制,确保在缺少物理文件时仍能正常运行 - 移除各项目中冗余的默认环境文件创建逻辑,统一由公共模块处理
1 parent 669b3b0 commit b80a580

13 files changed

Lines changed: 699 additions & 126 deletions

File tree

Cargo.lock

Lines changed: 10 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[workspace]
22
resolver = "2"
3-
members = ["file_classification_*"]
3+
members = ["file_classification_*", "common"]
44

55
[profile.release]
66
codegen-units = 1

common/Cargo.toml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
[package]
2+
name = "file_classification_common"
3+
version = "0.1.0"
4+
edition = "2024"
5+
6+
[dependencies]
7+
dotenvy = { workspace = true }
8+
9+
[dev-dependencies]
10+
tempfile = "3.23"

common/src/env_loader.rs

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
//! 环境变量加载工具
2+
//!
3+
//! 提供灵活的环境变量文件加载机制,支持自定义文件名和多重回退策略
4+
5+
use std::env;
6+
use std::fs;
7+
use std::path::Path;
8+
9+
/// 加载环境变量文件
10+
///
11+
/// 加载顺序:
12+
/// 1. 通过 ENV_FILE 环境变量指定的文件
13+
/// 2. 回退到 .file_classification_env
14+
/// 3. 如果以上都不存在,则创建带有默认配置的 .file_classification_env
15+
///
16+
/// # Returns
17+
/// 返回加载的环境变量文件路径,如果使用默认配置则返回 None
18+
pub fn load_env_file() -> Result<Option<String>, Box<dyn std::error::Error>> {
19+
// 首先检查是否通过环境变量指定了env文件
20+
let env_file = env::var("ENV_FILE").unwrap_or_else(|_| ".file_classification_env".to_string());
21+
22+
// 尝试加载指定的env文件
23+
if Path::new(&env_file).exists() {
24+
dotenvy::dotenv_override().ok();
25+
dotenvy::from_filename_override(&env_file)?;
26+
Ok(Some(env_file))
27+
} else if env_file != ".file_classification_env" && Path::new(".file_classification_env").exists() {
28+
// 如果指定了自定义env文件但不存在,回退到.file_classification_env
29+
dotenvy::dotenv_override().ok();
30+
dotenvy::from_filename_override(".file_classification_env")?;
31+
Ok(Some(".file_classification_env".to_string()))
32+
} else {
33+
// 如果文件都不存在,创建默认的.file_classification_env
34+
create_default_env_file()?;
35+
dotenvy::dotenv_override().ok();
36+
dotenvy::from_filename_override(".file_classification_env")?;
37+
Ok(None)
38+
}
39+
}
40+
41+
/// 创建默认的环境变量配置文件
42+
fn create_default_env_file() -> Result<(), Box<dyn std::error::Error>> {
43+
let default_content = r#"# File Classification 系统配置文件
44+
# 数据库配置
45+
DATABASE_URL=file_classification.db
46+
DATABASE_TYPE=sqlite
47+
48+
# Web API 配置
49+
BIND_ADDRESS=127.0.0.1
50+
BIND_PORT=8082
51+
52+
# 日志配置
53+
RUST_LOG=info
54+
RUST_LOG_FILE=debug
55+
56+
# CORS 配置
57+
CORS_ENABLED=true
58+
CORS_ORIGIN=http://localhost:8082
59+
"#;
60+
61+
fs::write(".file_classification_env", default_content)?;
62+
Ok(())
63+
}
64+
65+
#[cfg(test)]
66+
mod tests {
67+
use super::*;
68+
use std::fs;
69+
use std::env;
70+
71+
#[test]
72+
fn test_create_default_env_file() {
73+
// 创建临时目录进行测试
74+
let test_dir = tempfile::tempdir().unwrap();
75+
let original_dir = env::current_dir().unwrap();
76+
77+
// 切换到临时目录
78+
env::set_current_dir(test_dir.path()).unwrap();
79+
80+
// 测试创建默认env文件
81+
assert!(create_default_env_file().is_ok());
82+
assert!(Path::new(".file_classification_env").exists());
83+
84+
// 检查文件内容
85+
let content = fs::read_to_string(".file_classification_env").unwrap();
86+
assert!(content.contains("DATABASE_URL=file_classification.db"));
87+
assert!(content.contains("DATABASE_TYPE=sqlite"));
88+
89+
// 恢复原始目录
90+
env::set_current_dir(original_dir).unwrap();
91+
}
92+
}

common/src/lib.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
//! 公共工具库
2+
//!
3+
//! 包含项目中多个模块共享的工具函数和类型定义
4+
5+
/// 环境变量加载工具模块
6+
pub mod env_loader;

file_classification_cli/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,12 @@ version = "0.2.0"
44
edition = "2024"
55

66
[dependencies]
7+
file_classification_common = { path = "../common" }
78
file_classification_core = { path = "../file_classification_core" }
89
clap = { version = "4.5", features = ["derive"] }
910
dialoguer = "0.12.0"
1011
rustyline = "17.0.2"
1112
shlex = "1.3.0"
12-
dotenvy = { workspace = true }
1313

1414
[[bin]]
1515
name = "file_classification_cli"

file_classification_cli/src/main.rs

Lines changed: 5 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,16 @@
22
// 主入口文件,负责解析CLI参数并分派到相应处理函数
33

44
use std::error::Error;
5-
use std::env;
6-
use std::fs;
7-
use std::path::Path;
85

96
use clap::Parser;
107

118
use crate::cli::Cli;
129
use crate::context::Context;
1310
use crate::handlers::handle_command;
1411
use crate::repl::run_repl;
15-
use file_classification_core::utils;
12+
use file_classification_core::utils::database;
13+
// 引入环境变量加载工具
14+
use file_classification_common::env_loader::load_env_file;
1615

1716
mod cli;
1817
mod context;
@@ -22,59 +21,6 @@ mod interactive;
2221
mod parsers;
2322
mod repl;
2423

25-
/// 加载环境变量文件
26-
///
27-
/// 加载顺序:
28-
/// 1. 通过 ENV_FILE 环境变量指定的文件
29-
/// 2. 回退到 .file_classification_env
30-
/// 3. 如果以上都不存在,则创建带有默认配置的 .file_classification_env
31-
fn load_env_file() -> Result<Option<String>, Box<dyn std::error::Error>> {
32-
// 首先检查是否通过环境变量指定了env文件
33-
let env_file = env::var("ENV_FILE").unwrap_or_else(|_| ".file_classification_env".to_string());
34-
35-
// 尝试加载指定的env文件
36-
if Path::new(&env_file).exists() {
37-
dotenvy::dotenv_override().ok();
38-
dotenvy::from_filename_override(&env_file)?;
39-
Ok(Some(env_file))
40-
} else if env_file != ".file_classification_env" && Path::new(".file_classification_env").exists() {
41-
// 如果指定了自定义env文件但不存在,回退到.file_classification_env
42-
dotenvy::dotenv_override().ok();
43-
dotenvy::from_filename_override(".file_classification_env")?;
44-
Ok(Some(".file_classification_env".to_string()))
45-
} else {
46-
// 如果文件都不存在,创建默认的.file_classification_env
47-
create_default_env_file()?;
48-
dotenvy::dotenv_override().ok();
49-
dotenvy::from_filename_override(".file_classification_env")?;
50-
Ok(None)
51-
}
52-
}
53-
54-
/// 创建默认的环境变量配置文件
55-
fn create_default_env_file() -> Result<(), Box<dyn std::error::Error>> {
56-
let default_content = r#"# File Classification 系统配置文件
57-
# 数据库配置
58-
DATABASE_URL=file_classification.db
59-
DATABASE_TYPE=sqlite
60-
61-
# Web API 配置
62-
BIND_ADDRESS=127.0.0.1
63-
BIND_PORT=8082
64-
65-
# 日志配置
66-
RUST_LOG=info
67-
RUST_LOG_FILE=debug
68-
69-
# CORS 配置
70-
CORS_ENABLED=true
71-
CORS_ORIGIN=http://localhost:8082
72-
"#;
73-
74-
fs::write(".file_classification_env", default_content)?;
75-
Ok(())
76-
}
77-
7824
/// 主函数:解析命令行参数,初始化数据库连接和上下文,并处理命令
7925
fn main() -> Result<(), Box<dyn Error>> {
8026
let cli = Cli::parse();
@@ -88,10 +34,10 @@ fn main() -> Result<(), Box<dyn Error>> {
8834
let database_url = std::env::var("DATABASE_URL").expect("DATABASE_URL must be set");
8935
let database_type = std::env::var("DATABASE_TYPE").expect("DATABASE_TYPE must be set");
9036

91-
let mut conn = utils::database::establish_connection(&database_url, &database_type);
37+
let mut conn = database::establish_connection(&database_url, &database_type);
9238

9339
// 运行待处理的数据库迁移
94-
if let Err(e) = utils::database::run_pending_migrations(&mut conn) {
40+
if let Err(e) = database::run_pending_migrations(&mut conn) {
9541
eprintln!("数据库迁移失败: {}", e);
9642
return Err(e);
9743
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
//! CLI 工具模块
2+
3+
/// 加载环境变量文件
4+
///
5+
/// 加载顺序:
6+
/// 1. 通过 ENV_FILE 环境变量指定的文件
7+
/// 2. 回退到 .file_classification_env
8+
/// 3. 如果以上都不存在,则创建带有默认配置的 .file_classification_env
9+
///
10+
/// # Returns
11+
/// 返回加载的环境变量文件路径,如果使用默认配置则返回 None
12+
pub fn load_env_file() -> Result<Option<String>, Box<dyn std::error::Error>> {
13+
// 首先检查是否通过环境变量指定了env文件
14+
let env_file = std::env::var("ENV_FILE").unwrap_or_else(|_| ".file_classification_env".to_string());
15+
16+
// 尝试加载指定的env文件
17+
if std::path::Path::new(&env_file).exists() {
18+
dotenvy::dotenv_override().ok();
19+
dotenvy::from_filename_override(&env_file)?;
20+
Ok(Some(env_file))
21+
} else if env_file != ".file_classification_env" && std::path::Path::new(".file_classification_env").exists() {
22+
// 如果指定了自定义env文件但不存在,回退到.file_classification_env
23+
dotenvy::dotenv_override().ok();
24+
dotenvy::from_filename_override(".file_classification_env")?;
25+
Ok(Some(".file_classification_env".to_string()))
26+
} else {
27+
// 如果文件都不存在,创建默认的.file_classification_env
28+
create_default_env_file()?;
29+
dotenvy::dotenv_override().ok();
30+
dotenvy::from_filename_override(".file_classification_env")?;
31+
Ok(None)
32+
}
33+
}
34+
35+
/// 创建默认的环境变量配置文件
36+
fn create_default_env_file() -> Result<(), Box<dyn std::error::Error>> {
37+
let default_content = r#"# File Classification 系统配置文件
38+
# 数据库配置
39+
DATABASE_URL=file_classification.db
40+
DATABASE_TYPE=sqlite
41+
42+
# Web API 配置
43+
BIND_ADDRESS=127.0.0.1
44+
BIND_PORT=8082
45+
46+
# 日志配置
47+
RUST_LOG=info
48+
RUST_LOG_FILE=debug
49+
50+
# CORS 配置
51+
CORS_ENABLED=true
52+
CORS_ORIGIN=http://localhost:8082
53+
"#;
54+
55+
std::fs::write(".file_classification_env", default_content)?;
56+
Ok(())
57+
}

file_classification_webapi/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,12 @@ version = "0.2.0"
44
edition = "2024"
55

66
[dependencies]
7+
file_classification_common = { path = "../common" }
78
file_classification_core = { path = "../file_classification_core", features = ["swagger"] }
89
chrono = { workspace = true }
910
serde = { workspace = true }
1011
#diesel = { workspace = true, features = ["r2d2"] }
1112
diesel = { workspace = true }
12-
dotenvy = { workspace = true }
1313
# web related deps
1414
actix-web = "4.11"
1515
actix-cors = "0.7"

0 commit comments

Comments
 (0)