nargo_document/server/
mod.rs1use 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
6pub struct DevServer {
8 config: Config,
10 generator: Generator,
12 port: u16,
14 output_dir: String,
16}
17
18impl DevServer {
19 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 pub async fn start(&mut self) -> Result<(), Box<dyn std::error::Error>> {
37 println!("Starting development server on port {}", self.port);
38
39 self.generator.generate(".", &self.output_dir)?;
41
42 self.setup_file_watcher()?;
44
45 let routes = self.setup_routes();
47
48 warp::serve(routes).run(([127, 0, 0, 1], self.port)).await;
50
51 Ok(())
52 }
53
54 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 watcher.watch(Path::new("."), RecursiveMode::Recursive)?;
67
68 let config = self.config.clone();
69 let output_dir = self.output_dir.clone();
70
71 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 fn setup_routes(&self) -> impl Filter<Extract = impl warp::Reply, Error = warp::Rejection> + Clone {
96 let output_dir = self.output_dir.clone();
98
99 let static_files = warp::fs::dir(output_dir);
101
102 let index = warp::path::end().and_then(|| async move { Ok::<_, warp::Rejection>(warp::redirect::temporary("/index.html".parse::<Uri>().unwrap())) });
104
105 index.or(static_files)
107 }
108}