use anyhow::Result; use gpui::SharedString; use handlebars::Handlebars; use rust_embed::RustEmbed; use serde::Serialize; use std::sync::Arc; #[derive(RustEmbed)] #[folder = "src/templates"] #[include = "*.hbs"] struct Assets; pub struct Templates(Handlebars<'static>); impl Templates { pub fn new() -> Arc { let mut handlebars = Handlebars::new(); handlebars.set_strict_mode(true); handlebars.register_helper("contains", Box::new(contains)); handlebars.register_embed_templates::().unwrap(); Arc::new(Self(handlebars)) } } pub trait Template: Sized { const TEMPLATE_NAME: &'static str; fn render(&self, templates: &Templates) -> Result where Self: Serialize + Sized, { Ok(templates.0.render(Self::TEMPLATE_NAME, self)?) } } #[derive(Serialize)] pub struct SystemPromptTemplate<'a> { #[serde(flatten)] pub project: &'a prompt_store::ProjectContext, pub available_tools: Vec, } impl Template for SystemPromptTemplate<'_> { const TEMPLATE_NAME: &'static str = "system_prompt.hbs"; } /// Handlebars helper for checking if an item is in a list fn contains( h: &handlebars::Helper, _: &handlebars::Handlebars, _: &handlebars::Context, _: &mut handlebars::RenderContext, out: &mut dyn handlebars::Output, ) -> handlebars::HelperResult { let list = h .param(0) .and_then(|v| v.value().as_array()) .ok_or_else(|| { handlebars::RenderError::new("contains: missing or invalid list parameter") })?; let query = h.param(1).map(|v| v.value()).ok_or_else(|| { handlebars::RenderError::new("contains: missing or invalid query parameter") })?; if list.contains(&query) { out.write("true")?; } Ok(()) } #[cfg(test)] mod tests { use super::*; #[test] fn test_system_prompt_template() { let project = prompt_store::ProjectContext::default(); let template = SystemPromptTemplate { project: &project, available_tools: vec!["echo".into()], }; let templates = Templates::new(); let rendered = template.render(&templates).unwrap(); assert!(rendered.contains("## Fixing Diagnostics")); } }