#![deny(missing_docs)]
mod args;
mod asset;
mod docket;
mod doctree;
mod error;
mod highlight;
mod render;
mod search;
mod toc;
mod utils;
use std::{
error::Error,
path::{Path, PathBuf},
};
use crate::docket::Docket;
use error::Result;
use log::{info, warn};
#[derive(PartialEq, Copy, Clone)]
#[cfg_attr(not(feature = "watch"), allow(dead_code))]
enum OnError {
Ignore,
Exit,
}
fn main() {
init_logging();
let args = args::from_command_line();
let source = utils::path_or_default(args.flag_source, ".");
let target = utils::path_or_default(args.flag_target, "build/");
handle_err(
if args.flag_watch {
watch_and_build(&target, &source)
} else {
build(&source, &target)
},
OnError::Exit,
)
}
fn watch_and_build(target: &PathBuf, source: &PathBuf) -> Result<()> {
#[cfg(feature = "watch")]
{
use crate::error::ResultExt;
use notify::{watcher, RecursiveMode, Watcher};
use std::{fs, sync::mpsc::channel, time::Duration};
fs::create_dir_all(target).annotate_err("Error creating target directory")?;
let (tx, rx) = channel();
let mut watcher =
watcher(tx, Duration::from_secs(2)).annotate_err("could not create file watcher")?;
watcher
.watch(source, RecursiveMode::Recursive)
.annotate_err("Error watching source directory")?;
let _ = watcher.unwatch(target);
handle_err(build(source, target), OnError::Ignore);
println!("Build complete. Watching for changes.");
loop {
match rx.recv() {
Ok(s) => match s {
notify::DebouncedEvent::NoticeWrite(_)
| notify::DebouncedEvent::NoticeRemove(_) => (),
notify::DebouncedEvent::Error(e, path) => {
warn!("Watcher error at path {path:?}: {e}")
}
_ => {
println!("Rebuilding...");
handle_err(build(source, target), OnError::Ignore);
println!("Rebuild complete. Watching for changes.");
}
},
Err(e) => eprintln!("Watcher error: {}", e),
}
}
}
#[cfg(not(feature = "watch"))]
{
eprintln!("Watch not supported. Performing on off build.");
build(source, target)
}
}
fn handle_err(err: Result<()>, on_err: OnError) {
if let Err(err) = err {
eprintln!("docket: error: {}", err);
if let Some(source) = err.source() {
warn!("Error caused by {}", source);
}
if on_err == OnError::Exit {
std::process::exit(-1);
}
}
}
fn build(source: &Path, target: &Path) -> Result<()> {
info!("Rendering documenation from {:?} => {:?}", &source, &target);
Docket::open(source)?.render(target)
}
fn init_logging() {
use env_logger::*;
builder()
.target(Target::Stdout)
.parse_env(
Env::new()
.filter("DOCKET_LOG")
.write_style("DOCKET_LOG_STYLE"),
)
.init();
}