1#![doc = include_str!("../README.md")]
2#![warn(clippy::cargo, clippy::pedantic)]
3#![warn(
4 clippy::exit,
5 clippy::expect_used,
6 clippy::panic,
7 clippy::semicolon_inside_block,
8 clippy::str_to_string,
9 clippy::string_to_string,
10 clippy::unnecessary_self_imports,
11 clippy::use_debug
12)]
13#![deny(clippy::print_stderr, clippy::print_stdout)]
14#![allow(clippy::must_use_candidate)]
15
16mod angular;
17pub(crate) mod codeblock;
18pub(crate) mod config;
19mod js;
20mod markdown;
21mod utils;
22
23pub const MDBOOK_ANGULAR_VERSION: &str = env!("CARGO_PKG_VERSION");
25
26pub const EXPECTED_MDBOOK_VERSION: &str = mdbook::MDBOOK_VERSION;
31
32use std::{env, fs};
33
34pub use angular::stop_background_process;
35pub use config::{Builder, Config};
36
37use angular::build;
38use log::debug;
39use log::warn;
40use markdown::process_markdown;
41use markdown::ChapterWithCodeBlocks;
42use mdbook::{
43 renderer::{HtmlHandlebars, RenderContext},
44 BookItem, Renderer,
45};
46
47fn validate_version(ctx: &RenderContext) -> Result<()> {
48 let req = semver::VersionReq::parse(EXPECTED_MDBOOK_VERSION).unwrap();
49
50 if semver::Version::parse(&ctx.version).map_or(false, |version| req.matches(&version)) {
51 Ok(())
52 } else {
53 bail!("Invalid mdbook version {}, expected {}", &ctx.version, req);
54 }
55}
56
57pub(crate) use anyhow::{bail, Context, Error, Result};
58
59pub struct AngularRenderer {}
61
62impl Renderer for AngularRenderer {
63 fn name(&self) -> &str {
64 "angular"
65 }
66
67 #[inline]
73 fn render(&self, ctx: &RenderContext) -> Result<()> {
74 self.render_mut(&mut ctx.clone())
75 }
76}
77
78impl AngularRenderer {
79 pub fn new() -> Self {
80 Self {}
81 }
82
83 #[allow(clippy::missing_errors_doc)]
89 pub fn render_mut(&self, ctx: &mut RenderContext) -> Result<()> {
90 validate_version(ctx)?;
91
92 let config = Config::new(ctx)?;
93 let mut chapters_with_codeblocks = Vec::new();
94 let mut result: Result<()> = Ok(());
95
96 ctx.book.for_each_mut(|item| {
97 if result.is_err() {
98 return;
99 }
100
101 if let BookItem::Chapter(chapter) = item {
102 debug!("Processing chapter {}", &chapter.name);
103 match process_markdown(&config, chapter) {
104 Ok(processed) => {
105 debug!("Processed chapter {}", &chapter.name);
106 if let Some(processed) = processed {
107 chapters_with_codeblocks.push(processed);
108 }
109 }
110 Err(error) => result = Err(error),
111 };
112 }
113 });
114
115 debug!("Processed chapters");
116
117 if let Some(html) = &config.html {
118 ctx.config.set("output.html", html)?;
119 }
120
121 HtmlHandlebars::new().render(ctx)?;
122
123 fs::write(
124 config.target_folder.join("playground-io.min.js"),
125 crate::js::PLAYGROUND_SCRIPT,
126 )?;
127
128 debug!("Finished rendering");
129
130 #[allow(unused_mut)]
131 let mut run_build = !chapters_with_codeblocks.is_empty();
132
133 #[cfg(debug_assertions)]
134 if env::var("MDBOOK_ANGULAR_SKIP_BUILD").is_ok() {
135 run_build = false;
136 }
137
138 if run_build {
139 build(ctx, &config, chapters_with_codeblocks)?;
140 }
141
142 debug!("Finished");
143
144 Ok(())
145 }
146}
147
148impl Default for AngularRenderer {
149 fn default() -> Self {
150 Self::new()
151 }
152}