merge: 合并热重载功能分支
合并 feature-ms-v0.0.1-SNAPSHOOT-20260319 的热重载实现: - 使用 notify-debouncer-mini 监听 mocks 目录变化 - AppState 使用 RwLock<MockRouter> 支持并发读写 - 200ms 防抖避免编辑器保存时的多次触发 保留当前分支的功能: - 文件上传功能 (upload.rs) - 请求体 body 匹配支持 - 完整的测试覆盖 (43个测试) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
56
src/main.rs
56
src/main.rs
@@ -1,7 +1,10 @@
|
||||
use std::net::SocketAddr;
|
||||
use std::path::Path;
|
||||
use std::sync::Arc;
|
||||
use std::sync::{Arc, RwLock};
|
||||
use std::time::Duration;
|
||||
use axum::{routing::{any, post}, Router};
|
||||
use notify_debouncer_mini::{new_debouncer, notify::*};
|
||||
|
||||
use mock_server::loader::MockLoader;
|
||||
use mock_server::router::MockRouter;
|
||||
use mock_server::handler::{mock_handler, AppState};
|
||||
@@ -9,34 +12,58 @@ use mock_server::upload::upload_handler;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
// 1. 初始化日志(建议添加,方便观察加载情况)
|
||||
// 需要在 Cargo.toml 添加 tracing-subscriber = "0.3"
|
||||
tracing_subscriber::fmt::init();
|
||||
|
||||
// 2. 递归加载所有的 YAML 配置文件
|
||||
// 1. 确保 mocks 目录存在
|
||||
let mocks_dir = Path::new("./mocks");
|
||||
if !mocks_dir.exists() {
|
||||
println!("Warning: 'mocks/' directory not found. Creating it...");
|
||||
std::fs::create_dir_all(mocks_dir).unwrap();
|
||||
}
|
||||
|
||||
println!("Scanning mocks directory...");
|
||||
let index = MockLoader::load_all_from_dir(mocks_dir);
|
||||
|
||||
// 3. 确保 storage 目录存在
|
||||
// 2. 确保 storage 目录存在
|
||||
let storage_dir = Path::new("./storage");
|
||||
if !storage_dir.exists() {
|
||||
println!("Creating storage directory...");
|
||||
std::fs::create_dir_all(storage_dir).unwrap();
|
||||
}
|
||||
|
||||
// 4. 构建路由引擎并包装为共享状态
|
||||
let router_engine = MockRouter::new(index);
|
||||
// 3. 初始加载 Mock 配置
|
||||
println!("Scanning mocks directory...");
|
||||
let index = MockLoader::load_all_from_dir(mocks_dir);
|
||||
|
||||
// 4. 构建共享状态(使用 RwLock 支持热重载)
|
||||
let shared_state = Arc::new(AppState {
|
||||
router: router_engine,
|
||||
router: RwLock::new(MockRouter::new(index)),
|
||||
});
|
||||
|
||||
// 5. 配置 Axum 路由
|
||||
// 5. 设置热加载监听器
|
||||
let state_for_watcher = shared_state.clone();
|
||||
let watch_path = mocks_dir.to_path_buf();
|
||||
let (tx, rx) = std::sync::mpsc::channel();
|
||||
|
||||
// 200ms 防抖,防止编辑器保存文件时产生多次干扰
|
||||
let mut debouncer = new_debouncer(Duration::from_millis(200), tx).unwrap();
|
||||
debouncer.watcher().watch(&watch_path, RecursiveMode::Recursive).unwrap();
|
||||
|
||||
// 启动异步任务监听文件变动
|
||||
tokio::spawn(async move {
|
||||
while let Ok(res) = rx.recv() {
|
||||
match res {
|
||||
Ok(_) => {
|
||||
println!("🔄 Detecting changes in mocks/, reloading...");
|
||||
let new_index = MockLoader::load_all_from_dir(&watch_path);
|
||||
// 获取写锁 (Write Lock) 更新索引
|
||||
let mut writer = state_for_watcher.router.write().expect("Failed to acquire write lock");
|
||||
*writer = MockRouter::new(new_index);
|
||||
println!("✅ Mocks reloaded successfully.");
|
||||
}
|
||||
Err(e) => eprintln!("Watcher error: {:?}", e),
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// 6. 配置 Axum 路由
|
||||
// 文件上传路由:POST /api/upload
|
||||
// 其他所有请求由 mock_handler 处理
|
||||
let app = Router::new()
|
||||
@@ -44,12 +71,13 @@ async fn main() {
|
||||
.fallback(any(mock_handler))
|
||||
.with_state(shared_state);
|
||||
|
||||
// 6. 启动服务
|
||||
// 7. 启动服务
|
||||
let addr = SocketAddr::from(([127, 0, 0, 1], 8080));
|
||||
println!("🚀 Rust Mock Server is running on http://{}", addr);
|
||||
println!("📁 File upload endpoint: POST http://{}/api/upload", addr);
|
||||
println!("🔄 Hot reload enabled for mocks/ directory");
|
||||
println!("Ready to handle requests based on your YAML definitions.");
|
||||
|
||||
let listener = tokio::net::TcpListener::bind(addr).await.unwrap();
|
||||
axum::serve(listener, app).await.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user