Skip to content

feat: create mdbook backend for LAD files #287

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Feb 14, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,5 @@ Cargo.lock
**/doc
assets/scripts/tlconfig.lua
**.log
**build/
**build/
.html
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ members = [
"crates/xtask",
"crates/script_integration_test_harness",
"crates/bevy_mod_scripting_derive",
"crates/ladfile",
"crates/ladfile", "crates/lad_backends/mdbook_lad_preprocessor",
]
resolver = "2"
exclude = ["crates/bevy_api_gen", "crates/macro_tests"]
Expand Down
32 changes: 32 additions & 0 deletions crates/lad_backends/mdbook_lad_preprocessor/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
[package]
name = "mdbook_lad_preprocessor"
version = "0.1.0"
edition = "2021"
authors = ["Maksymilian Mozolewski <makspl17@gmail.com>"]
license = "MIT OR Apache-2.0"
description = "Language Agnostic Declaration (LAD) file format for the bevy_mod_scripting crate"
repository = "https://github.com/makspll/bevy_mod_scripting"
homepage = "https://github.com/makspll/bevy_mod_scripting"
keywords = ["bevy", "gamedev", "scripting", "documentation", "generator"]
categories = ["game-development", "development-tools"]
include = ["readme.md", "/src"]
readme = "readme.md"

[dependencies]
clap = "4"
mdbook = "0.4"
ladfile = { path = "../../ladfile", version = "0.1.1" }
env_logger = "0.11"
log = "0.4"
serde_json = "1.0"

[dev-dependencies]
assert_cmd = "2.0"
pretty_assertions = "1.4.1"

[lints]
workspace = true

[[bin]]
name = "mdbook-lad-preprocessor"
path = "src/main.rs"
25 changes: 25 additions & 0 deletions crates/lad_backends/mdbook_lad_preprocessor/readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# LAD Preprocessor for mdbook

This is a preprocessor for `mdbook` that allows you to include `LAD` files in your markdown files.

## Usage

Add the following to your `book.toml`:

```toml
[preprocessor.lad_preprocessor]
```

Then any files with the `.lad.json` extension will be processed by the preprocessor.

So for example if you have the following structure:

```markdown
- [Normal file](normal_file.md)
- [LAD file](lad_file.lad.json)
```

The `lad_file.lad.json` will be processed by the preprocessor, and appropriate nested markdown will be generated from there on out using the `LAD file` chapter as the parent page.

If the file is not found

77 changes: 77 additions & 0 deletions crates/lad_backends/mdbook_lad_preprocessor/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
//! The library crate for the mdbook LAD preprocessor.
#![allow(missing_docs)]

use mdbook::{errors::Error, preprocess::Preprocessor};
mod markdown;
mod sections;

const LAD_EXTENSION: &str = "lad.json";

pub struct LADPreprocessor;

impl Preprocessor for LADPreprocessor {
fn name(&self) -> &str {
"lad-preprocessor"
}

fn run(
&self,
_ctx: &mdbook::preprocess::PreprocessorContext,
mut book: mdbook::book::Book,
) -> mdbook::errors::Result<mdbook::book::Book> {
let mut errors = Vec::default();

book.for_each_mut(|item| {
if let mdbook::BookItem::Chapter(chapter) = item {
let is_lad_chapter = chapter
.source_path
.as_ref()
.and_then(|a| a.file_name())
.is_some_and(|a| a.to_string_lossy().ends_with(LAD_EXTENSION));

if !is_lad_chapter {
log::debug!("Skipping non-LAD chapter: {:?}", chapter.source_path);
return;
}

let chapter_title = chapter.name.clone();

let lad = match ladfile::parse_lad_file(&chapter.content) {
Ok(lad) => lad,
Err(e) => {
log::debug!("Failed to parse LAD file: {:?}", e);
errors.push(Error::new(e).context("Failed to parse LAD file"));
return;
}
};

log::debug!("Parsed LAD file: {:?}", lad);

let sections = sections::lad_file_to_sections(&lad, Some(chapter_title));

let new_chapter = sections::section_to_chapter(
sections,
Some(chapter),
chapter.parent_names.clone(),
chapter.number.clone(),
None,
None,
);

log::debug!("New chapter: {:?}", new_chapter);

*chapter = new_chapter;
}
});

if !errors.is_empty() {
// return on first error
for error in errors {
log::error!("{}", error);
Err(error)?;
}
}

Ok(book)
}
}
65 changes: 65 additions & 0 deletions crates/lad_backends/mdbook_lad_preprocessor/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
#![allow(missing_docs)]
use clap::{Arg, Command};
use env_logger::Builder;
use log::LevelFilter;
use mdbook::preprocess::{CmdPreprocessor, Preprocessor};
use mdbook_lad_preprocessor::LADPreprocessor;
use std::{env, fs::File, io, process::exit};

// use mdbook_lad_preprocessor::LADPreprocessor;

fn init_logger() {
let mut builder = Builder::new();

if let Ok(var) = env::var("RUST_LOG") {
builder.parse_filters(&var);
} else {
builder.filter(None, LevelFilter::Info);
}

// target lad.log file in current directory
// print pwd
if let Ok(file) = File::create("./lad.log") {
let target = Box::new(file);
builder.target(env_logger::Target::Pipe(target));
}

builder.init();

log::debug!("Debug logging enabled");
}

pub fn make_app() -> Command {
Command::new("nop-preprocessor")
.about("A mdbook preprocessor which does precisely nothing")
.subcommand(
Command::new("supports")
.arg(Arg::new("renderer").required(true))
.about("Check whether a renderer is supported by this preprocessor"),
)
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
init_logger();
let matches = make_app().get_matches();
if let Some(sub_args) = matches.subcommand_matches("supports") {
let renderer = match sub_args.get_one::<String>("renderer") {
Some(r) => r,
None => {
log::error!("No renderer specified");
exit(1)
}
};

if LADPreprocessor.supports_renderer(renderer) {
exit(0)
} else {
exit(1)
}
} else {
let (ctx, book) = CmdPreprocessor::parse_input(io::stdin())?;
let processed_book = LADPreprocessor.run(&ctx, book)?;
serde_json::to_writer(io::stdout(), &processed_book)?;
exit(0)
}
}
Loading
Loading