about summary refs log tree commit diff
path: root/splorm/src/handler/templated.rs
blob: 5eec0f8b35812cafe86aa9957b4a56eb93dd3c30 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
//! templated file handling
//!
//! Reads files from the filesystem, processes them as a template, and prepares
//! a `Response` for them.
//!
//! See [`templated`] for how templates are handled

use axum::{body::Body, response::Response};
use bempline::{Document, Options, options::IncludeMethod};
use paige::Templated;

use crate::{
	RuntimeError,
	config::Settings,
	fs::{self, RequestPath, SafePathBuf},
	state::SplormState,
	templated::TemplateVariables,
};

pub async fn send_file(
	path: RequestPath,
	state: SplormState,
	settings: &Settings,
) -> Result<Response, RuntimeError> {
	let safe = path.system_path().to_owned();
	let string = fs::read_file_to_string(path.system_path()).await?;
	let template = Templated::parse(&string)
		.map_err(|_| RuntimeError::not_a_template(path.system_path().to_path_buf()))?;
	let tvars = TemplateVariables::build(safe.as_path(), &template.frontmatter);
	tracing::trace!(
		"page {} has features {:?}",
		path.web_path(),
		tvars.features()
	);

	let pt_path = match tvars.template() {
		Some(name) => {
			let path = RequestPath::verify_safe(settings.template_dir(), &format!("{name}.html"))?;
			path.system_path().to_owned()
		}
		None => {
			tracing::trace!(
				"using default template: {}",
				settings.default_template_name()
			);

			settings.default_template_path()
		}
	};

	let mut page_template = get_template(pt_path).await?;
	tvars.apply_title(&mut page_template);
	tvars.apply_styles(&mut page_template);
	tvars.apply_published(&mut page_template);
	tvars.apply_pathnav(&mut page_template, &path);
	tvars.apply_opengraph(&mut page_template, &settings, &path);

	let content = if !tvars.features().is_empty() {
		let with_features = tvars.apply_features(template.content, &state).await;
		tvars.apply_markup(&mut page_template, &with_features)
	} else {
		tvars.apply_markup(&mut page_template, template.content)
	};

	page_template.set("main", content);

	Response::builder()
		.status(200)
		.header("content-type", "text/html")
		.body(Body::from(page_template.compile()))
		.map_err(|_| RuntimeError::Unknown)
}

async fn get_template(path: SafePathBuf) -> Result<Document, RuntimeError> {
	let options = Options::new().include_path(IncludeMethod::Path(path.as_std_path().to_owned()));
	let str = fs::read_file_to_string(path.as_path()).await?;

	Document::from_str(str, options)
		.map_err(|perr| RuntimeError::template_parse_error(perr, path.to_path_buf()))
}