use crate::{config::Config, generator::Generator};
use notify::{Event, EventKind, RecommendedWatcher, RecursiveMode, Watcher};
use std::{path::Path, sync::mpsc::channel};
use warp::{http::Uri, Filter};
pub struct DevServer {
config: Config,
generator: Generator,
port: u16,
output_dir: String,
}
impl DevServer {
pub fn new(config: Config, port: u16, output_dir: &str) -> Self {
Self { config: config.clone(), generator: Generator::new(config), port, output_dir: output_dir.to_string() }
}
pub async fn start(&mut self) -> Result<(), Box<dyn std::error::Error>> {
println!("Starting development server on port {}", self.port);
self.generator.generate(".", &self.output_dir)?;
self.setup_file_watcher()?;
let routes = self.setup_routes();
warp::serve(routes).run(([127, 0, 0, 1], self.port)).await;
Ok(())
}
fn setup_file_watcher(&self) -> Result<(), Box<dyn std::error::Error>> {
let (tx, rx) = channel();
let mut watcher: RecommendedWatcher = notify::recommended_watcher(move |res| match res {
Ok(event) => tx.send(event).unwrap(),
Err(e) => println!("Error: {:?}", e),
})?;
watcher.watch(Path::new("."), RecursiveMode::Recursive)?;
let config = self.config.clone();
let output_dir = self.output_dir.clone();
std::thread::spawn(move || {
for event in rx {
if let Event { kind: EventKind::Modify(_), paths, .. } = event {
for path in paths {
if path.extension().map_or(false, |ext| ext == "md" || ext == "markdown") {
println!("File changed: {:?}", path);
let mut generator = Generator::new(config.clone());
if let Err(e) = generator.generate(".", &output_dir) {
eprintln!("Error regenerating documentation: {}", e);
}
}
}
}
}
});
Ok(())
}
fn setup_routes(&self) -> impl Filter<Extract = impl warp::Reply, Error = warp::Rejection> + Clone {
let output_dir = self.output_dir.clone();
let static_files = warp::fs::dir(output_dir);
let index = warp::path::end().and_then(|| async move { Ok::<_, warp::Rejection>(warp::redirect::temporary("/index.html".parse::<Uri>().unwrap())) });
index.or(static_files)
}
}