//! Templates API handler.
use axum::{extract::Query, http::StatusCode, response::IntoResponse, Json};
use serde::{Deserialize, Serialize};
use utoipa::ToSchema;
use crate::llm::templates;
/// Query parameters for listing templates
#[derive(Debug, Deserialize, ToSchema)]
pub struct ListTemplatesQuery {
/// Filter by contract phase (research, specify, plan, execute, review)
pub phase: Option<String>,
}
/// Template summary for API response
#[derive(Debug, Serialize, ToSchema)]
#[serde(rename_all = "camelCase")]
pub struct TemplateSummary {
/// Template identifier
pub id: String,
/// Display name
pub name: String,
/// Contract phase this template is designed for
pub phase: String,
/// Brief description
pub description: String,
/// Number of body elements in the template
pub element_count: usize,
}
/// Response for listing templates
#[derive(Debug, Serialize, ToSchema)]
pub struct ListTemplatesResponse {
pub templates: Vec<TemplateSummary>,
}
/// List available file templates
#[utoipa::path(
get,
path = "/api/v1/templates",
params(
("phase" = Option<String>, Query, description = "Filter by contract phase")
),
responses(
(status = 200, description = "Templates retrieved successfully", body = ListTemplatesResponse)
),
tag = "templates"
)]
pub async fn list_templates(
Query(query): Query<ListTemplatesQuery>,
) -> impl IntoResponse {
let template_list = match query.phase.as_deref() {
Some(phase) => templates::templates_for_phase(phase),
None => templates::all_templates(),
};
let summaries: Vec<TemplateSummary> = template_list
.iter()
.map(|t| TemplateSummary {
id: t.id.clone(),
name: t.name.clone(),
phase: t.phase.clone(),
description: t.description.clone(),
element_count: t.suggested_body.len(),
})
.collect();
(
StatusCode::OK,
Json(ListTemplatesResponse {
templates: summaries,
}),
)
.into_response()
}
/// Get a specific template by ID
#[utoipa::path(
get,
path = "/api/v1/templates/{id}",
params(
("id" = String, Path, description = "Template ID")
),
responses(
(status = 200, description = "Template retrieved successfully", body = templates::FileTemplate),
(status = 404, description = "Template not found")
),
tag = "templates"
)]
pub async fn get_template(
axum::extract::Path(id): axum::extract::Path<String>,
) -> impl IntoResponse {
let all = templates::all_templates();
let template = all.into_iter().find(|t| t.id == id);
match template {
Some(t) => (StatusCode::OK, Json(serde_json::json!(t))).into_response(),
None => (
StatusCode::NOT_FOUND,
Json(serde_json::json!({
"error": format!("Template '{}' not found", id)
})),
)
.into_response(),
}
}