#![allow(missing_docs)]
use std::sync::OnceLock;
use mdbook_preprocessor::{
Preprocessor, PreprocessorContext,
book::{Book, BookItem, Chapter},
errors::{self, Error},
};
use sections::{Section, SectionData};
mod argument_visitor;
mod markdown;
mod sections;
#[derive(Debug)]
struct Options {}
impl From<&PreprocessorContext> for Options {
fn from(_context: &PreprocessorContext) -> Self {
Options {}
}
}
const LAD_EXTENSION: &str = "lad.json";
static OPTIONS: OnceLock<Options> = OnceLock::new();
pub struct LADPreprocessor;
impl LADPreprocessor {
fn is_lad_file(chapter: &Chapter) -> bool {
chapter
.source_path
.as_ref()
.and_then(|a| a.file_name())
.map(|s| s.to_string_lossy().ends_with(LAD_EXTENSION))
.unwrap_or(false)
}
fn process_lad_chapter(
_context: &PreprocessorContext,
chapter: &Chapter,
parent: Option<&Chapter>,
chapter_index: usize,
) -> Result<Chapter, Error> {
let chapter_title = chapter.name.trim_end_matches(".lad.json").to_owned();
let ladfile = ladfile::parse_lad_file(&chapter.content)
.map_err(|e| Error::new(e).context("Failed to parse LAD file"))?;
log::debug!(
"Parsed LAD file: {}",
serde_json::to_string_pretty(&ladfile).unwrap_or_default()
);
let parent_path = parent
.and_then(|p| p.path.clone())
.unwrap_or_default()
.with_extension("");
log::debug!("Parent path: {parent_path:?}");
let new_chapter = Section::new(
parent_path,
&ladfile,
SectionData::Summary {
title: Some(chapter_title),
},
)
.into_chapter(parent, chapter_index);
log::debug!(
"New chapter: {}",
serde_json::to_string_pretty(&new_chapter).unwrap_or_default()
);
Ok(new_chapter)
}
}
impl Preprocessor for LADPreprocessor {
fn name(&self) -> &str {
"lad-preprocessor"
}
fn run(&self, context: &PreprocessorContext, mut book: Book) -> errors::Result<Book> {
let mut errors = Vec::new();
let options = Options::from(context);
log::debug!("Options: {options:?}");
OPTIONS
.set(options)
.map_err(|_| errors::Error::msg("could not initialize options"))?;
book.for_each_mut(|item| {
if let BookItem::Chapter(parent) = item {
let replacements: Vec<(usize, Chapter)> = parent
.sub_items
.iter()
.enumerate()
.filter_map(|(idx, item)| {
if let BookItem::Chapter(chapter) = item
&& LADPreprocessor::is_lad_file(chapter)
{
match LADPreprocessor::process_lad_chapter(
context,
chapter,
Some(parent),
idx,
) {
Ok(new_chapter) => return Some((idx, new_chapter)),
Err(e) => {
errors.push(e);
return None;
}
}
}
None
})
.collect();
for (idx, new_chapter) in replacements {
if let BookItem::Chapter(chapter) = &mut parent.sub_items[idx] {
*chapter = new_chapter;
}
}
}
});
book.for_each_mut(|item| {
if let BookItem::Chapter(chapter) = item {
if !LADPreprocessor::is_lad_file(chapter) {
return;
}
let new_chapter = match LADPreprocessor::process_lad_chapter(
context,
chapter,
None,
chapter
.number
.clone()
.and_then(|n| n.last().map(|v| (*v) as usize))
.unwrap_or_default(),
) {
Ok(new_chapter) => new_chapter,
Err(e) => {
errors.push(e);
return;
}
};
*chapter = new_chapter;
}
});
log::debug!(
"Book after LAD processing: {}",
serde_json::to_string_pretty(&book).unwrap_or_default()
);
if !errors.is_empty() {
if let Some(error) = errors.into_iter().next() {
log::error!("{error}");
Err(error)?;
}
}
Ok(book)
}
}