systemprompt_generator/prerender/
list.rs1use std::path::Path;
2
3use anyhow::{Context, Result};
4use systemprompt_database::DbPool;
5use systemprompt_models::{ContentConfigRaw, FullWebConfig, ParentRoute};
6use systemprompt_template_provider::{ComponentContext, PageContext};
7use systemprompt_templates::TemplateRegistry;
8use tokio::fs;
9
10use crate::prerender::utils::{merge_json_data, render_components};
11
12pub struct RenderListParams<'a> {
13 pub items: &'a [serde_json::Value],
14 pub config: &'a ContentConfigRaw,
15 pub web_config: &'a FullWebConfig,
16 pub list_config: &'a ParentRoute,
17 pub source_name: &'a str,
18 pub template_registry: &'a TemplateRegistry,
19 pub dist_dir: &'a Path,
20 pub index_content: Option<&'a serde_json::Value>,
21 pub db_pool: &'a DbPool,
22}
23
24impl std::fmt::Debug for RenderListParams<'_> {
25 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
26 f.debug_struct("RenderListParams")
27 .field("source_name", &self.source_name)
28 .field("items_count", &self.items.len())
29 .field("has_index_content", &self.index_content.is_some())
30 .finish_non_exhaustive()
31 }
32}
33
34pub async fn render_list_route(params: RenderListParams<'_>) -> Result<()> {
35 let RenderListParams {
36 items,
37 config,
38 web_config,
39 list_config,
40 source_name,
41 template_registry,
42 dist_dir,
43 index_content,
44 db_pool,
45 } = params;
46
47 let list_content_type = format!("{source_name}-list");
48
49 let template_name = template_registry
50 .find_template_for_content_type(&list_content_type)
51 .ok_or_else(|| anyhow::anyhow!("No template for: {list_content_type}"))?;
52
53 let mut list_data = serde_json::json!({
54 "HAS_INDEX_CONTENT": index_content.is_some(),
55 });
56
57 let mut page_ctx =
58 PageContext::new(&list_content_type, web_config, config, db_pool).with_all_items(items);
59
60 if let Some(item) = index_content {
61 page_ctx = page_ctx.with_content_item(item);
62 }
63
64 for provider in template_registry.page_providers_for(&list_content_type) {
65 let data = provider
66 .provide_page_data(&page_ctx)
67 .await
68 .map_err(|e| anyhow::anyhow!("Provider {} failed: {e}", provider.provider_id()))?;
69 merge_json_data(&mut list_data, &data);
70 }
71
72 let component_ctx = ComponentContext::for_list(web_config, items);
73 render_components(
74 template_registry,
75 &list_content_type,
76 &component_ctx,
77 &mut list_data,
78 )
79 .await;
80
81 let list_html = template_registry
82 .render(template_name, &list_data)
83 .context("Failed to render list route")?;
84
85 let list_dir = dist_dir.join(list_config.url.trim_start_matches('/'));
86 fs::create_dir_all(&list_dir).await?;
87 fs::write(list_dir.join("index.html"), &list_html).await?;
88
89 tracing::debug!(path = %list_config.url, "Generated list route");
90 Ok(())
91}