Skip to main content

nargo_document/server/
mod.rs

1use crate::{config::Config, generator::Generator};
2use notify::{Event, EventKind, RecommendedWatcher, RecursiveMode, Watcher};
3use std::{path::Path, sync::mpsc::channel};
4use warp::{http::Uri, Filter};
5
6/// 开发服务器,用于实时预览文档
7pub struct DevServer {
8    /// 配置信息
9    config: Config,
10    /// 文档生成器
11    generator: Generator,
12    /// 服务器端口
13    port: u16,
14    /// 输出目录
15    output_dir: String,
16}
17
18impl DevServer {
19    /// 创建新的开发服务器
20    ///
21    /// # 参数
22    /// * `config` - 文档配置信息
23    /// * `port` - 服务器端口
24    /// * `output_dir` - 输出目录
25    ///
26    /// # 返回值
27    /// 返回一个新的开发服务器实例
28    pub fn new(config: Config, port: u16, output_dir: &str) -> Self {
29        Self { config: config.clone(), generator: Generator::new(config), port, output_dir: output_dir.to_string() }
30    }
31
32    /// 启动开发服务器
33    ///
34    /// # 返回值
35    /// 返回 Result,成功时为 (),失败时为错误信息
36    pub async fn start(&mut self) -> Result<(), Box<dyn std::error::Error>> {
37        println!("Starting development server on port {}", self.port);
38
39        // 首先生成静态文件
40        self.generator.generate(".", &self.output_dir)?;
41
42        // 设置文件监听
43        self.setup_file_watcher()?;
44
45        // 设置 HTTP 服务器
46        let routes = self.setup_routes();
47
48        // 启动服务器
49        warp::serve(routes).run(([127, 0, 0, 1], self.port)).await;
50
51        Ok(())
52    }
53
54    /// 设置文件监听器
55    ///
56    /// # 返回值
57    /// 返回 Result,成功时为 (),失败时为错误信息
58    fn setup_file_watcher(&self) -> Result<(), Box<dyn std::error::Error>> {
59        let (tx, rx) = channel();
60        let mut watcher: RecommendedWatcher = notify::recommended_watcher(move |res| match res {
61            Ok(event) => tx.send(event).unwrap(),
62            Err(e) => println!("Error: {:?}", e),
63        })?;
64
65        // 监听当前目录下的 Markdown 文件
66        watcher.watch(Path::new("."), RecursiveMode::Recursive)?;
67
68        let config = self.config.clone();
69        let output_dir = self.output_dir.clone();
70
71        // 启动线程处理文件变化
72        std::thread::spawn(move || {
73            for event in rx {
74                if let Event { kind: EventKind::Modify(_), paths, .. } = event {
75                    for path in paths {
76                        if path.extension().map_or(false, |ext| ext == "md" || ext == "markdown") {
77                            println!("File changed: {:?}", path);
78                            let mut generator = Generator::new(config.clone());
79                            if let Err(e) = generator.generate(".", &output_dir) {
80                                eprintln!("Error regenerating documentation: {}", e);
81                            }
82                        }
83                    }
84                }
85            }
86        });
87
88        Ok(())
89    }
90
91    /// 设置路由
92    ///
93    /// # 返回值
94    /// 返回路由过滤器
95    fn setup_routes(&self) -> impl Filter<Extract = impl warp::Reply, Error = warp::Rejection> + Clone {
96        // 创建 output_dir 的克隆,使其生命周期与路由相同
97        let output_dir = self.output_dir.clone();
98
99        // 提供静态文件
100        let static_files = warp::fs::dir(output_dir);
101
102        // 根路径重定向到 index.html
103        let index = warp::path::end().and_then(|| async move { Ok::<_, warp::Rejection>(warp::redirect::temporary("/index.html".parse::<Uri>().unwrap())) });
104
105        // 组合路由
106        index.or(static_files)
107    }
108}