合并 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>
84 lines
3.0 KiB
Rust
84 lines
3.0 KiB
Rust
use std::net::SocketAddr;
|
||
use std::path::Path;
|
||
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};
|
||
use mock_server::upload::upload_handler;
|
||
|
||
#[tokio::main]
|
||
async fn main() {
|
||
tracing_subscriber::fmt::init();
|
||
|
||
// 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();
|
||
}
|
||
|
||
// 2. 确保 storage 目录存在
|
||
let storage_dir = Path::new("./storage");
|
||
if !storage_dir.exists() {
|
||
println!("Creating storage directory...");
|
||
std::fs::create_dir_all(storage_dir).unwrap();
|
||
}
|
||
|
||
// 3. 初始加载 Mock 配置
|
||
println!("Scanning mocks directory...");
|
||
let index = MockLoader::load_all_from_dir(mocks_dir);
|
||
|
||
// 4. 构建共享状态(使用 RwLock 支持热重载)
|
||
let shared_state = Arc::new(AppState {
|
||
router: RwLock::new(MockRouter::new(index)),
|
||
});
|
||
|
||
// 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()
|
||
.route("/api/upload", post(upload_handler))
|
||
.fallback(any(mock_handler))
|
||
.with_state(shared_state);
|
||
|
||
// 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();
|
||
}
|