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(); }