Files
alfieking.dev/src/templating.cpp
2025-08-21 23:23:14 +01:00

77 lines
2.7 KiB
C++

#include "templating.hpp"
#include <filesystem>
#include <fstream>
#include <regex>
#include <stdexcept>
Templating::Templating(const std::string& template_dir)
: inja_env(std::filesystem::canonical(template_dir).string()),
template_dir(std::filesystem::canonical(template_dir).string())
{
inja_env.set_search_included_templates_in_files(true);
}
std::string Templating::preprocess_template(const std::string& template_name) {
namespace fs = std::filesystem;
fs::path abs_template_dir = fs::path(template_dir);
fs::path abs_template_file = fs::canonical(abs_template_dir / template_name);
std::ifstream file(abs_template_file);
if (!file.is_open()) {
throw std::runtime_error("Failed to open template file: " + abs_template_file.string());
}
std::string content((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
std::regex extends_regex(R"(\{\%\s*extends\s*(['"])(.+?)\1\s*\%\})");
std::smatch match;
if (std::regex_search(content, match, extends_regex)) {
std::string quote = match[1].str();
std::string original_path = match[2].str();
if (original_path.find("/") == std::string::npos &&
!original_path.empty() &&
original_path.front() != '/') {
fs::path abs_extended_template = fs::canonical(abs_template_dir / original_path);
fs::path rel_path = fs::relative(abs_extended_template, abs_template_file.parent_path());
std::string new_path = rel_path.generic_string();
content = std::regex_replace(content, extends_regex,
"{% extends " + quote + new_path + quote + " %}");
}
}
return content;
}
crow::response Templating::render_template(const std::string& template_name, const inja::json& data) {
try {
std::string preprocessed = preprocess_template(template_name);
inja::Template tpl = inja_env.parse(preprocessed);
std::string rendered = inja_env.render(tpl, data);
return crow::response(rendered);
} catch (const std::exception& e) {
return crow::response(500, e.what());
}
}
crow::response Templating::render_template(const std::string& template_name) {
return render_template(template_name, inja::json{});
}
std::string Templating::render_template_string(const std::string& template_name, const inja::json& data) {
std::string preprocessed = preprocess_template(template_name);
inja::Template tpl = inja_env.parse(preprocessed);
return inja_env.render(tpl, data);
}
std::string Templating::render_template_string(const std::string& template_name) {
return render_template_string(template_name, inja::json{});
}