Skip to main content

systemprompt_generator/prerender/
context.rs

1use std::path::PathBuf;
2use std::sync::Arc;
3
4use anyhow::{Context, Result};
5use systemprompt_database::DbPool;
6use systemprompt_extension::ExtensionRegistry;
7use systemprompt_models::{AppPaths, ContentConfigRaw, FullWebConfig};
8use systemprompt_provider_contracts::ContentDataProvider;
9use systemprompt_template_provider::{DynTemplateLoader, DynTemplateProvider, FileSystemLoader};
10use systemprompt_templates::{
11    CoreTemplateProvider, EmbeddedDefaultsProvider, TemplateRegistry, TemplateRegistryBuilder,
12};
13use tokio::fs;
14
15use crate::templates::{get_templates_path, load_web_config};
16
17pub struct PrerenderContext {
18    pub db_pool: DbPool,
19    pub config: ContentConfigRaw,
20    pub web_config: FullWebConfig,
21    pub template_registry: TemplateRegistry,
22    pub dist_dir: PathBuf,
23    pub content_data_providers: Vec<Arc<dyn ContentDataProvider>>,
24}
25
26impl std::fmt::Debug for PrerenderContext {
27    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
28        f.debug_struct("PrerenderContext")
29            .field("config", &self.config)
30            .field("web_config", &self.web_config)
31            .field("template_registry", &self.template_registry)
32            .field("dist_dir", &self.dist_dir)
33            .field(
34                "content_data_providers_count",
35                &self.content_data_providers.len(),
36            )
37            .finish_non_exhaustive()
38    }
39}
40
41pub async fn load_prerender_context(db_pool: DbPool) -> Result<PrerenderContext> {
42    let paths = AppPaths::get().map_err(|e| anyhow::anyhow!("{}", e))?;
43    let config_path = paths.system().content_config();
44
45    let yaml_content = fs::read_to_string(&config_path)
46        .await
47        .context("Failed to read content config")?;
48    let config: ContentConfigRaw =
49        serde_yaml::from_str(&yaml_content).context("Failed to parse content config")?;
50
51    let web_config = load_web_config()
52        .await
53        .context("Failed to load web config")?;
54
55    tracing::debug!(config_path = %config_path.display(), "Loaded config");
56
57    let template_dir = get_templates_path(&web_config);
58    if !template_dir.exists() {
59        return Err(anyhow::anyhow!(
60            "Template directory not found: {}. Configure profile.paths.web_path or \
61             web_config.yaml paths.templates",
62            template_dir.display()
63        ));
64    }
65    let extension_template_path = template_dir;
66
67    let extension_provider = CoreTemplateProvider::discover_with_priority(
68        &extension_template_path,
69        CoreTemplateProvider::EXTENSION_PRIORITY,
70    )
71    .await
72    .context("Failed to discover extension templates")?;
73
74    let embedded_defaults = EmbeddedDefaultsProvider;
75
76    let loader = FileSystemLoader::with_path(&extension_template_path);
77
78    let extension_provider: DynTemplateProvider = Arc::new(extension_provider);
79    let embedded_defaults: DynTemplateProvider = Arc::new(embedded_defaults);
80    let loader: DynTemplateLoader = Arc::new(loader);
81
82    let mut registry_builder = TemplateRegistryBuilder::new()
83        .with_provider(extension_provider)
84        .with_provider(embedded_defaults)
85        .with_loader(loader);
86
87    let extensions = ExtensionRegistry::discover();
88    tracing::debug!(
89        extension_count = extensions.extensions().len(),
90        "Discovered extensions for prerender context"
91    );
92
93    let mut content_data_providers: Vec<Arc<dyn ContentDataProvider>> = Vec::new();
94
95    for ext in extensions.extensions() {
96        let providers = ext.page_data_providers();
97        let prerenderers = ext.page_prerenderers();
98        let content_providers = ext.content_data_providers();
99        tracing::debug!(
100            extension_id = %ext.metadata().id,
101            page_provider_count = providers.len(),
102            page_prerenderer_count = prerenderers.len(),
103            content_data_provider_count = content_providers.len(),
104            component_count = ext.component_renderers().len(),
105            extender_count = ext.template_data_extenders().len(),
106            "Extension providers discovered"
107        );
108
109        for component in ext.component_renderers() {
110            registry_builder = registry_builder.with_component(component);
111        }
112        for extender in ext.template_data_extenders() {
113            registry_builder = registry_builder.with_extender(extender);
114        }
115        for provider in providers {
116            registry_builder = registry_builder.with_page_provider(provider);
117        }
118        for prerenderer in prerenderers {
119            registry_builder = registry_builder.with_page_prerenderer(prerenderer);
120        }
121        content_data_providers.extend(content_providers);
122    }
123
124    content_data_providers.sort_by_key(|p| p.priority());
125
126    let template_registry = registry_builder
127        .build_and_init()
128        .await
129        .context("Failed to initialize template registry")?;
130
131    let dist_dir = AppPaths::get()
132        .map_err(|e| anyhow::anyhow!("{}", e))?
133        .web()
134        .dist()
135        .to_path_buf();
136
137    Ok(PrerenderContext {
138        db_pool,
139        config,
140        web_config,
141        template_registry,
142        dist_dir,
143        content_data_providers,
144    })
145}