diff --git a/Cargo.lock b/Cargo.lock index 9df375c..9bba48f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -234,6 +234,31 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "crossbeam-deque" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" + [[package]] name = "crypto-common" version = "0.1.6" @@ -371,6 +396,19 @@ dependencies = [ "wasi", ] +[[package]] +name = "globset" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57da3b9b5b85bd66f31093f8c408b90a74431672542466497dcbdfdc02034be1" +dependencies = [ + "aho-corasick", + "bstr", + "log", + "regex-automata", + "regex-syntax", +] + [[package]] name = "handlebars" version = "5.1.2" @@ -439,6 +477,22 @@ dependencies = [ "unicode-normalization", ] +[[package]] +name = "ignore" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b46810df39e66e925525d6e38ce1e7f6e1d208f72dc39757880fcb66e2c58af1" +dependencies = [ + "crossbeam-deque", + "globset", + "log", + "memchr", + "regex-automata", + "same-file", + "walkdir", + "winapi-util", +] + [[package]] name = "indexmap" version = "2.2.6" @@ -563,6 +617,7 @@ dependencies = [ "env_logger", "genawaiter", "html5gum", + "ignore", "insta", "log", "mdbook", @@ -584,7 +639,6 @@ dependencies = [ "tracing-log", "tracing-subscriber", "ureq", - "walkdir", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 0373c31..7d19314 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,6 +19,7 @@ anyhow = "1.0.47" env_logger = "0.11.0" genawaiter = { version = "0.99.1", default-features = false } html5gum = "0.5.7" +ignore = "0.4" log = "0.4.0" mdbook = { version = "0.4.35", default-features = false } normpath = "1.0.0" @@ -33,7 +34,6 @@ serde_yaml = "0.9.0" tempfile = "3.0.0" toml = "0.8.0" ureq = "2.0.0" -walkdir = "2.0.0" # Increase minimum version requirements of transitive dependencies thiserror = "1.0.2" diff --git a/src/preprocess.rs b/src/preprocess.rs index 569c0d1..dd55e70 100644 --- a/src/preprocess.rs +++ b/src/preprocess.rs @@ -14,6 +14,7 @@ use std::{ use aho_corasick::AhoCorasick; use anyhow::{anyhow, Context as _}; +use ignore::gitignore::{Gitignore, GitignoreBuilder}; use log::log; use mdbook::{ book::{BookItems, Chapter}, @@ -23,9 +24,9 @@ use normpath::PathExt; use once_cell::sync::Lazy; use pulldown_cmark::{CodeBlockKind, CowStr, HeadingLevel, LinkType}; use regex::Regex; -use walkdir::WalkDir; use crate::{ + book::Book, latex, pandoc::{self, OutputFormat, RenderContext}, }; @@ -97,19 +98,8 @@ impl<'book> Preprocessor<'book> { } fs::create_dir_all(&preprocessed)?; - for entry in WalkDir::new(&ctx.book.source_dir).follow_links(true) { - let entry = entry?; - let src = entry.path(); - let dest = preprocessed.join(src.strip_prefix(&ctx.book.source_dir).unwrap()); - if entry.file_type().is_dir() { - fs::create_dir_all(&dest) - .with_context(|| format!("Unable to create directory '{}'", dest.display()))? - } else { - fs::copy(src, &dest).with_context(|| { - format!("Unable to copy '{}' -> '{}'", src.display(), dest.display()) - })?; - } - } + let ignore = build_ignore(&ctx.book)?; + copy_recursive(&ctx.book.source_dir, &preprocessed, &ignore)?; let mut chapters = HashMap::new(); for section in ctx.book.book.iter() { @@ -1275,6 +1265,77 @@ impl fmt::Debug for IndexedChapter<'_> { } } +fn copy_recursive(src: &Path, dst: &Path, ignore: &Gitignore) -> anyhow::Result<()> { + for e in src + .read_dir() + .with_context(|| format!("Unable to read directory {src:#?}"))? + { + let cur = e?.path(); + + log::info!("Walk: {cur:#?}"); + + if ignore.matched(&cur, cur.is_dir()).is_ignore() { + log::info!(" Ignore"); + + continue; + } + + if cur.is_dir() { + let dir = dst.join(cur.strip_prefix(&src).unwrap()); + + fs::create_dir_all(&dir) + .with_context(|| format!("Unable to create directory {dir:#?}"))?; + + copy_recursive(&cur, &dir, ignore)?; + } else { + let dst = dst.join(cur.strip_prefix(&src).unwrap()); + + log::info!(" Copy to: {dst:#?}"); + + fs::copy(cur, &dst).with_context(|| { + format!("Unable to copy '{}' -> '{}'", src.display(), dst.display()) + })?; + } + } + + Ok(()) +} + +fn build_ignore(book: &Book) -> anyhow::Result { + let root = book.root.canonicalize()?; + let mut src = book.source_dir.canonicalize()?; + + let mut builder = GitignoreBuilder::new(&src); + + let mdbook_ignore = src.join(".mdbookignore"); + if mdbook_ignore.exists() { + if let Some(err) = builder.add(mdbook_ignore) { + log::warn!("Unable to load '.mdbookignore' file: {}", err); + } + } + + loop { + let git_ignore = src.join(".gitignore"); + if git_ignore.exists() { + if let Some(err) = builder.add(git_ignore) { + log::warn!("Unable to load '.gitignore' file: {}", err); + } + } + + if src == root { + break; + } + + let Some(parent) = src.parent() else { + break; + }; + + src = parent.canonicalize()?; + } + + Ok(builder.build()?) +} + #[cfg(test)] mod tests { use super::Preprocessor;