templates.rs

  1use anyhow::Result;
  2use gpui::SharedString;
  3use handlebars::Handlebars;
  4use rust_embed::RustEmbed;
  5use serde::Serialize;
  6use std::sync::Arc;
  7
  8#[derive(RustEmbed)]
  9#[folder = "src/templates"]
 10#[include = "*.hbs"]
 11struct Assets;
 12
 13pub struct Templates(Handlebars<'static>);
 14
 15impl Templates {
 16    pub fn new() -> Arc<Self> {
 17        let mut handlebars = Handlebars::new();
 18        handlebars.set_strict_mode(true);
 19        handlebars.register_helper("contains", Box::new(contains));
 20        handlebars.register_embed_templates::<Assets>().unwrap();
 21        Arc::new(Self(handlebars))
 22    }
 23}
 24
 25pub trait Template: Sized {
 26    const TEMPLATE_NAME: &'static str;
 27
 28    fn render(&self, templates: &Templates) -> Result<String>
 29    where
 30        Self: Serialize + Sized,
 31    {
 32        Ok(templates.0.render(Self::TEMPLATE_NAME, self)?)
 33    }
 34}
 35
 36#[expect(
 37    dead_code,
 38    reason = "Marked as unused by Rust 1.89 and left as is as of 07 Aug 2025 to let AI team address it."
 39)]
 40#[derive(Serialize)]
 41pub struct GlobTemplate {
 42    pub project_roots: String,
 43}
 44
 45impl Template for GlobTemplate {
 46    const TEMPLATE_NAME: &'static str = "glob.hbs";
 47}
 48
 49#[derive(Serialize)]
 50pub struct SystemPromptTemplate<'a> {
 51    #[serde(flatten)]
 52    pub project: &'a prompt_store::ProjectContext,
 53    pub available_tools: Vec<SharedString>,
 54}
 55
 56impl Template for SystemPromptTemplate<'_> {
 57    const TEMPLATE_NAME: &'static str = "system_prompt.hbs";
 58}
 59
 60/// Handlebars helper for checking if an item is in a list
 61fn contains(
 62    h: &handlebars::Helper,
 63    _: &handlebars::Handlebars,
 64    _: &handlebars::Context,
 65    _: &mut handlebars::RenderContext,
 66    out: &mut dyn handlebars::Output,
 67) -> handlebars::HelperResult {
 68    let list = h
 69        .param(0)
 70        .and_then(|v| v.value().as_array())
 71        .ok_or_else(|| {
 72            handlebars::RenderError::new("contains: missing or invalid list parameter")
 73        })?;
 74    let query = h.param(1).map(|v| v.value()).ok_or_else(|| {
 75        handlebars::RenderError::new("contains: missing or invalid query parameter")
 76    })?;
 77
 78    if list.contains(&query) {
 79        out.write("true")?;
 80    }
 81
 82    Ok(())
 83}
 84
 85#[cfg(test)]
 86mod tests {
 87    use super::*;
 88
 89    #[test]
 90    fn test_system_prompt_template() {
 91        let project = prompt_store::ProjectContext::default();
 92        let template = SystemPromptTemplate {
 93            project: &project,
 94            available_tools: vec!["echo".into()],
 95        };
 96        let templates = Templates::new();
 97        let rendered = template.render(&templates).unwrap();
 98        assert!(rendered.contains("## Fixing Diagnostics"));
 99    }
100}