#![warn(missing_docs)]
use std::{
cmp::Ordering,
num::NonZeroU8,
path::PathBuf,
sync::{Arc, Mutex},
};
use code_to_pdf::{CodeToPdf, DocumentSubset, HighlighterConfig};
use dimensions::Dimensions;
use helpers::ProcessedText;
use ignore::{WalkBuilder, overrides::OverrideBuilder};
use log::{error, trace};
use printpdf::FontId;
use rayon::iter::{ParallelBridge, ParallelIterator};
use text_manipulation::TextWrapper;
use thread_local::ThreadLocal;
pub mod code_to_pdf;
pub mod dimensions;
pub mod font_loader;
pub mod helpers;
pub mod logging;
pub mod text_manipulation;
pub use printpdf::{ParsedFont, PdfDocument, PdfSaveOptions};
impl CodeToPdf {
pub fn run_parallel(
font_id: FontId,
font_bytes: &[u8],
path: PathBuf,
exclusions: Vec<String>,
page_dimensions: Dimensions,
font_size: f32,
page_text: Option<String>,
include_path: bool,
threads: Option<NonZeroU8>,
) -> (Arc<Mutex<DocumentSubset>>, usize) {
let doc_subset = DocumentSubset::default();
let ss = two_face::syntax::extra_newlines();
let ts = two_face::theme::extra();
let walker = WalkBuilder::new(path.clone())
.overrides({
let mut builder = OverrideBuilder::new(path);
for exclusion in exclusions.clone() {
builder.add(&("!".to_string() + &exclusion)).unwrap();
}
builder.build().unwrap()
})
.sort_by_file_path(|x, y| {
{
if x.is_dir() && !y.is_dir() {
Ordering::Less
} else if y.is_dir() && !x.is_dir() {
Ordering::Greater
} else {
Ordering::Equal
}
}
.reverse()
})
.build();
let local_c2pdf = ThreadLocal::<Arc<Mutex<CodeToPdf>>>::new();
let local_highlighter_config = ThreadLocal::<Arc<Mutex<HighlighterConfig>>>::new();
let doc_subset = Arc::new(Mutex::new(doc_subset));
if let Some(threads) = threads {
rayon::ThreadPoolBuilder::new()
.num_threads(u8::from(threads) as usize)
.build_global()
.unwrap();
}
let mut wrapper = TextWrapper::new(font_bytes, font_size);
let additional_text = page_text.and_then(|text| ProcessedText::new(text, &mut wrapper));
walker.enumerate().par_bridge().for_each(|(i, result)| {
let c2pdf_mutex = local_c2pdf.get_or(|| {
Arc::new(Mutex::new(CodeToPdf::new(
doc_subset.clone(),
font_id.clone(),
page_dimensions.clone(),
wrapper.clone(),
additional_text.clone(),
include_path,
)))
});
let highlight_config_mutex = local_highlighter_config.get_or(|| {
Arc::new(Mutex::new(HighlighterConfig::new(
ss.clone(),
ts.get(two_face::theme::EmbeddedThemeName::InspiredGithub)
.clone(),
)))
});
match result {
Ok(entry) => {
if entry.file_type().is_some_and(|f| f.is_file()) {
let path = entry.path();
trace!("Generating pages for {}, index {i}", path.display());
if let Err(err) = c2pdf_mutex.lock().unwrap().process_file(
path,
&highlight_config_mutex.lock().unwrap(),
i,
) {
error!("ERROR: {}", err);
}
}
}
Err(err) => {
error!("ERROR: {}", err);
}
}
});
let mut processed_file_count = 0;
for local in local_c2pdf.iter() {
processed_file_count += local.lock().unwrap().processed_file_count();
}
(doc_subset, processed_file_count)
}
}