pub mod convert;
pub mod visit;
use {
anyhow::{Context, Error, Result},
futures::executor::block_on,
std::{fs, path::Path},
};
use crate::{
book::{walk::walk_book_async, BookStructure},
build::visit::AdocBookVisitor,
};
pub fn build_book(book: &BookStructure) -> Result<()> {
{
let site = book.site_dir_path();
if !site.exists() {
fs::create_dir(&site).with_context(|| {
format!("Failed to create site directory at: {}", site.display())
})?;
}
}
let tmp_dir = book.site_dir_path().join("__tmp__");
if !self::validate_out_dir(&tmp_dir)? {
println!("Stopped building adbook directory");
return Ok(());
}
let (mut v, errors) = AdocBookVisitor::from_book(book, &tmp_dir);
crate::utils::print_errors(&errors, "while creating AdocBookVisitor");
block_on(walk_book_async(&mut v, &book.toc));
info!("===> Copying output files to site directory");
{
let mut errors = Vec::with_capacity(10);
let res = self::overwrite_site_with_temporary_outputs(book, &tmp_dir, &mut errors);
crate::utils::print_errors(&errors, "while copying temporary files to site directory");
res?;
}
info!(
"===> Removing the temporary output directory: {}",
tmp_dir.display()
);
fs::remove_dir_all(&tmp_dir).with_context(|| {
format!(
"Unexpected error when removing the temporary output directory at: {}",
tmp_dir.display()
)
})?;
Ok(())
}
fn validate_out_dir(out_dir: &Path) -> Result<bool> {
if out_dir.exists() {
ensure!(
out_dir.is_dir(),
"There's something that prevents `adbook` from making a temporary output directory at: {}",
out_dir.display()
);
println!("-----------------------------------------------------------");
println!("There's already a directory where `adbook` wants to output temporary files:");
println!("{}", out_dir.display());
println!("Is it OK to clear all files in that directory and use it as a temporary output directory?");
match rprompt::prompt_reply_stdout("> [y/n]: ")?.as_str() {
"y" | "yes" => {}
_ => {
return Ok(false);
}
}
trace!(
"Creating the temporary output directory at: {}",
out_dir.display()
);
fs::remove_dir_all(&out_dir).with_context(|| {
format!(
"Unexpected error while clearing an output directory for `adbook`: {}",
out_dir.display()
)
})?;
}
assert!(
!out_dir.exists(),
"Fatal error: adbook must have ensured that temporary output directory doesn't exist at: {}",
out_dir.display()
);
fs::create_dir(&out_dir).with_context(|| {
format!(
"Failed to temporary output directory at: {}",
out_dir.display()
)
})?;
trace!(
"Created a new temporary output directly at: {}",
out_dir.display()
);
Ok(true)
}
fn overwrite_site_with_temporary_outputs(
book: &BookStructure,
out_dir: &Path,
errors: &mut Vec<Error>,
) -> Result<()> {
let site_dir = book.site_dir_path();
trace!("remove files in site directory");
crate::utils::clear_directory_items(&site_dir, |path| {
if path == out_dir {
return true;
}
let name = match path.file_name().and_then(|s| s.to_str()) {
Some(name) => name,
None => return false,
};
name.starts_with(".")
})?;
for rel_path in &book.book_ron.includes {
if !rel_path.is_relative() {
errors.push(anyhow!(
"Non-relative path in `book.ron` includes: {}",
rel_path.display()
));
continue;
}
let src_path = book.src_dir_path().join(rel_path);
let dst_path = book.site_dir_path().join(rel_path);
if !src_path.exists() {
errors.push(anyhow!(
"Not a valid relative path from the source directroy in `book.ron` includes: {}",
rel_path.display()
));
continue;
}
if src_path.is_file() {
let dir = src_path.parent().unwrap();
if !dir.exists() {
fs::create_dir_all(dir).with_context(|| {
format!(
"Unable to create parent directory of included file: {}",
src_path.display(),
)
})?;
}
fs::copy(&src_path, &dst_path).with_context(|| {
format!(
"Unable to copy source included file `{}` to `{}`",
src_path.display(),
dst_path.display()
)
})?;
} else if src_path.is_dir() {
if !dst_path.exists() {
fs::create_dir_all(&dst_path).with_context(|| {
format!(
"Unable to create parent directory:\nsrc: {}\ndst: {}",
src_path.display(),
dst_path.display(),
)
})?;
}
crate::utils::copy_items_rec(&src_path, &dst_path).with_context(|| {
format!(
"Unable to copy included directory:\nsrc: {}\ndst: {}",
src_path.display(),
dst_path.display(),
)
})?;
} else {
errors.push(anyhow!(
"Unexpected kind of file to include in `book.ron`: {}",
src_path.display()
));
}
}
for entry in fs::read_dir(&out_dir).context("Unable to `read_dir` the tmp directory")? {
let entry = entry.context("Unable to read some entry when reading tmp directory item")?;
let src_path = entry.path();
let rel_path = src_path.strip_prefix(out_dir).unwrap();
let dst_path = site_dir.join(rel_path);
if src_path.is_file() {
trace!("* `{}` -> `{}`", src_path.display(), dst_path.display());
fs::copy(&src_path, &dst_path)?;
} else if src_path.is_dir() {
crate::utils::copy_items_rec(&src_path, &dst_path)?;
} else {
errors.push(anyhow!(
"Unexpected kind of file in temporary output directory: `{}`",
src_path.display(),
));
}
}
Ok(())
}