use handlebars::Handlebars;
use mcp_execution_core::{Error, Result};
use serde::Serialize;
#[derive(Debug)]
pub struct TemplateEngine<'a> {
handlebars: Handlebars<'a>,
}
impl<'a> TemplateEngine<'a> {
pub fn new() -> Result<Self> {
let mut handlebars = Handlebars::new();
handlebars.set_strict_mode(true);
Self::register_progressive_templates(&mut handlebars)?;
Ok(Self { handlebars })
}
fn register_progressive_templates(handlebars: &mut Handlebars<'a>) -> Result<()> {
handlebars
.register_template_string(
"progressive/tool",
include_str!("../templates/progressive/tool.ts.hbs"),
)
.map_err(|e| Error::SerializationError {
message: format!("Failed to register progressive tool template: {e}"),
source: None,
})?;
handlebars
.register_template_string(
"progressive/index",
include_str!("../templates/progressive/index.ts.hbs"),
)
.map_err(|e| Error::SerializationError {
message: format!("Failed to register progressive index template: {e}"),
source: None,
})?;
handlebars
.register_template_string(
"progressive/runtime-bridge",
include_str!("../templates/progressive/runtime-bridge.ts.hbs"),
)
.map_err(|e| Error::SerializationError {
message: format!("Failed to register progressive runtime-bridge template: {e}"),
source: None,
})?;
Ok(())
}
pub fn render<T: Serialize>(&self, template_name: &str, context: &T) -> Result<String> {
self.handlebars
.render(template_name, context)
.map_err(|e| Error::SerializationError {
message: format!("Template rendering failed: {e}"),
source: None,
})
}
pub fn register_template_string(&mut self, name: &str, template: &str) -> Result<()> {
self.handlebars
.register_template_string(name, template)
.map_err(|e| Error::SerializationError {
message: format!("Failed to register template '{name}': {e}"),
source: None,
})
}
}
impl<'a> Default for TemplateEngine<'a> {
fn default() -> Self {
Self::new().expect("Failed to create default TemplateEngine")
}
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
#[test]
fn test_template_engine_creation() {
let engine = TemplateEngine::new();
assert!(engine.is_ok());
}
#[test]
fn test_render_progressive_templates() {
let engine = TemplateEngine::new().unwrap();
let tool_context = json!({
"typescript_name": "testTool",
"description": "Test tool",
"server_id": "test",
"name": "test_tool",
"properties": [],
"has_required_properties": false,
"input_schema": {}
});
let result = engine.render("progressive/tool", &tool_context);
if let Err(e) = &result {
eprintln!("Error rendering template: {}", e);
}
assert!(result.is_ok(), "Failed to render: {:?}", result.err());
assert!(result.unwrap().contains("testTool"));
}
#[test]
fn test_custom_template_registration() {
let mut engine = TemplateEngine::new().unwrap();
engine
.register_template_string("test", "Hello {{name}}")
.unwrap();
let context = json!({"name": "World"});
let result = engine.render("test", &context).unwrap();
assert_eq!(result, "Hello World");
}
#[test]
fn test_render_nonexistent_template() {
let engine = TemplateEngine::new().unwrap();
let context = json!({"name": "test"});
let result = engine.render("nonexistent", &context);
assert!(result.is_err());
}
#[test]
fn test_default_trait() {
let _engine = TemplateEngine::default();
}
}