Skip to content

Commit 0ba4716

Browse files
committed
Fix
1 parent ee0cc82 commit 0ba4716

File tree

4 files changed

+53
-85
lines changed

4 files changed

+53
-85
lines changed

mdbook-tera-backend/src/main.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,16 @@ mod tera_renderer;
33
use mdbook::renderer::RenderContext;
44
use std::io;
55

6-
use crate::tera_renderer::*;
6+
use crate::tera_renderer::custom_component::TeraRendererConfig;
7+
use crate::tera_renderer::renderer::Renderer;
78

89
fn main() {
910
let mut stdin = io::stdin();
10-
// Get the configs
1111
let ctx = RenderContext::from_json(&mut stdin).unwrap();
1212
let config: TeraRendererConfig = ctx
1313
.config
1414
.get_deserialized_opt("output.tera-backend")
15-
.expect("Failed to get Gaia config")
15+
.expect("Failed to get tera-backend config")
1616
.unwrap();
1717

1818
let (tera_template, components) = config

mdbook-tera-backend/src/tera_renderer/custom_component.rs

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,18 @@
11
use anyhow::Result;
22
use serde::Deserialize;
3-
use std::cell::RefCell;
43
use std::collections::BTreeMap;
54
use std::path::{Path, PathBuf};
65
use tera::Tera;
76

87
pub struct CustomComponent {
98
template: Tera,
109
name: String,
11-
/// Used to generate unique ids for each component to prevent collisions in javascript with query selectors.
12-
counter: RefCell<u32>,
1310
}
1411

1512
impl CustomComponent {
1613
pub fn new(name: &str, template: Tera) -> Result<CustomComponent> {
1714
Ok(CustomComponent {
1815
name: String::from(name),
19-
counter: RefCell::new(0),
2016
template,
2117
})
2218
}
@@ -30,13 +26,15 @@ impl CustomComponent {
3026
tera_context: &tera::Context,
3127
attributes: BTreeMap<String, String>,
3228
) -> Result<String> {
33-
let counter = self.counter.replace_with(|&mut counter| counter + 1);
3429
let mut tera_context = tera_context.clone();
35-
tera_context.insert("count", &counter);
3630
tera_context.insert("attributes", &attributes);
31+
let output = self.template.render(&self.name, &tera_context);
3732

38-
let output = self.template.render(&self.name, &tera_context)?;
39-
Ok(output)
33+
if let Err(err) = &output {
34+
println!("Error rendering component {}: {:?}", self.name, err);
35+
}
36+
37+
Ok(output?)
4038
}
4139

4240
pub fn component_name(&self) -> String {
@@ -45,6 +43,7 @@ impl CustomComponent {
4543
}
4644

4745
#[derive(Deserialize)]
46+
#[serde(untagged)]
4847
pub enum Component {
4948
Named { name: String, path: PathBuf },
5049
Anonymous(PathBuf),
@@ -53,7 +52,7 @@ pub enum Component {
5352
/// Configuration in `book.toml` `[output.tera-renderer]`.
5453
#[derive(Deserialize)]
5554
pub struct TeraRendererConfig {
56-
/// Relative path to the templates directory.
55+
/// Relative path to the templates directory from the `book.toml` directory.
5756
pub templates_dir: PathBuf,
5857
/// Custom HTML components to register.
5958
#[serde(default)]
@@ -74,7 +73,11 @@ impl TeraRendererConfig {
7473
Ok(())
7574
}
7675

77-
fn create_custom_components(&self, tera_template: &Tera) -> Result<Vec<CustomComponent>> {
76+
fn create_custom_components(
77+
&self,
78+
tera_template: &Tera,
79+
current_dir: &Path,
80+
) -> Result<Vec<CustomComponent>> {
7881
self.html_components
7982
.iter()
8083
.map(|component| {
@@ -85,12 +88,14 @@ impl TeraRendererConfig {
8588
CustomComponent::new(name, template)?
8689
}
8790
Component::Anonymous(path) => {
91+
let mut template = tera_template.clone();
8892
let name = path
89-
.file_name()
93+
.file_stem()
9094
.unwrap_or_default()
9195
.to_str()
9296
.unwrap_or_default();
93-
CustomComponent::new(name, tera_template.clone())?
97+
template.add_template_file(current_dir.join(path), Some(name))?;
98+
CustomComponent::new(name, template)?
9499
}
95100
})
96101
})
@@ -106,7 +111,7 @@ impl TeraRendererConfig {
106111
&mut tera_template,
107112
&current_dir.join(&self.templates_dir),
108113
)?;
109-
let components = self.create_custom_components(&tera_template)?;
114+
let components = self.create_custom_components(&tera_template, current_dir)?;
110115

111116
Ok((tera_template, components))
112117
}
Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,2 @@
1-
mod custom_component;
2-
mod renderer;
3-
4-
pub(crate) use custom_component::*;
5-
pub(crate) use renderer::*;
1+
pub(crate) mod custom_component;
2+
pub(crate) mod renderer;

mdbook-tera-backend/src/tera_renderer/renderer.rs

Lines changed: 30 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -1,54 +1,37 @@
1-
use super::CustomComponent;
1+
use super::custom_component::CustomComponent;
22
use anyhow::{anyhow, Result};
33
use lol_html::html_content::ContentType;
44
use lol_html::{element, RewriteStrSettings};
55
use mdbook::renderer::RenderContext;
66
use serde_json::to_value;
77
use std::collections::{BTreeMap, HashMap};
88
use std::fs;
9-
use std::io::{Read, Write};
10-
use std::path::{Path, PathBuf};
9+
use std::io::Write;
10+
use std::path::Path;
1111
use std::sync::Arc;
1212
use tera::Tera;
1313

14-
pub struct RenderingContext<'a> {
15-
pub path: PathBuf,
16-
pub language: Option<String>,
17-
pub serialized_ctx: &'a serde_json::Value,
18-
pub ctx: &'a RenderContext,
19-
}
20-
21-
impl<'a> RenderingContext<'a> {
22-
fn new(
23-
path: PathBuf,
24-
language: Option<String>,
25-
serialized_ctx: &'a serde_json::Value,
26-
ctx: &'a RenderContext,
27-
) -> Result<Self> {
28-
Ok(RenderingContext {
29-
path,
30-
language,
31-
serialized_ctx,
32-
ctx,
33-
})
34-
}
35-
}
36-
3714
pub(crate) struct Renderer {
3815
ctx: Arc<RenderContext>,
3916
serialized_ctx: serde_json::Value,
4017
components: Vec<CustomComponent>,
18+
counter: u64,
4119
tera_template: Tera,
4220
}
4321

4422
impl Renderer {
4523
pub(crate) fn new(ctx: RenderContext, tera_template: Tera) -> Result<Renderer> {
46-
Ok(Renderer {
24+
let mut renderer = Renderer {
4725
serialized_ctx: serde_json::to_value(&ctx)?,
4826
ctx: Arc::new(ctx),
4927
components: Vec::new(),
28+
counter: 0,
5029
tera_template,
51-
})
30+
};
31+
renderer
32+
.tera_template
33+
.register_function("get_context", renderer.create_get_context_function());
34+
Ok(renderer)
5235
}
5336

5437
pub(crate) fn add_component(&mut self, mut component: CustomComponent) {
@@ -57,7 +40,7 @@ impl Renderer {
5740
}
5841

5942
fn create_get_context_function(&self) -> impl tera::Function {
60-
let ctx_rx = Arc::clone(&self.ctx);
43+
let ctx_rc = Arc::clone(&self.ctx);
6144
move |args: &HashMap<String, serde_json::value::Value>| -> tera::Result<tera::Value> {
6245
let key = args
6346
.get("key")
@@ -66,7 +49,7 @@ impl Renderer {
6649
.ok_or_else(|| {
6750
tera::Error::from(format!("Key has invalid type, expected string"))
6851
})?;
69-
let value = ctx_rx
52+
let value = ctx_rc
7053
.config
7154
.get(key)
7255
.ok_or_else(|| tera::Error::from(format!("Could not find key {key} in config")))?;
@@ -76,21 +59,11 @@ impl Renderer {
7659
}
7760

7861
pub(crate) fn render_book(&mut self) -> Result<()> {
79-
let dest_dir = &self
80-
.ctx
81-
.destination
82-
.parent()
83-
.ok_or_else(|| {
84-
anyhow!(
85-
"Destination directory {:?} has no parent",
86-
self.ctx.destination
87-
)
88-
})?
89-
.to_owned();
62+
let dest_dir = &self.ctx.destination.parent().unwrap();
9063
if !dest_dir.is_dir() {
91-
return Err(anyhow!("{:?} is not a directory", dest_dir));
64+
return Err(anyhow!("{dest_dir:?} is not a directory"));
9265
}
93-
self.render_book_directory(&dest_dir)
66+
self.render_book_directory(dest_dir)
9467
}
9568

9669
fn render_book_directory(&mut self, path: &Path) -> Result<()> {
@@ -110,39 +83,32 @@ impl Renderer {
11083
if path.extension().unwrap_or_default() != "html" {
11184
return Ok(());
11285
}
113-
let mut file_content = String::new();
114-
{
115-
let mut file = fs::File::open(path)?;
116-
file.read_to_string(&mut file_content)?;
117-
}
118-
86+
let file_content = std::fs::read_to_string(path)?;
11987
let output = self.render_components(&file_content, path)?;
12088
let mut output_file = fs::File::create(path)?;
12189
output_file.write_all(output.as_bytes())?;
12290
Ok(())
12391
}
12492

125-
fn create_context(&self, rendering_context: &RenderingContext) -> tera::Context {
93+
fn create_context(&mut self, path: &Path) -> tera::Context {
12694
let mut context = tera::Context::new();
127-
context.insert("path", &rendering_context.path);
128-
context.insert("ctx", &rendering_context.serialized_ctx);
129-
context.insert(
130-
"book_dir",
131-
&rendering_context.ctx.destination.parent().unwrap(),
132-
);
95+
context.insert("path", path);
96+
context.insert("ctx", &self.serialized_ctx);
97+
context.insert("book_dir", &self.ctx.destination.parent().unwrap());
98+
context.insert("counter", &self.counter);
99+
context.insert("attributes", &BTreeMap::<String, String>::new());
100+
self.counter += 1;
133101

134102
context
135103
}
136104

137105
fn render_components(&mut self, file_content: &str, path: &Path) -> Result<String> {
138-
let rendering_context = RenderingContext::new(
139-
path.to_owned(),
140-
self.ctx.config.book.language.clone(),
141-
&self.serialized_ctx,
142-
&self.ctx,
143-
)?;
144-
let tera_context = self.create_context(&rendering_context);
145-
let rendered_file = self.tera_template.render_str(file_content, &tera_context)?;
106+
let tera_context = self.create_context(path);
107+
108+
let rendered_file = self
109+
.tera_template
110+
.render_str(file_content, &tera_context)
111+
.map_err(|e| anyhow!("Error rendering file {path:?}: {e:?}"))?;
146112
let custom_components_handlers = self
147113
.components
148114
.iter()

0 commit comments

Comments
 (0)