systemprompt_content/
list_branding_provider.rs1use std::sync::Arc;
2
3use anyhow::Result;
4use async_trait::async_trait;
5use serde_json::Value;
6use systemprompt_models::ContentConfigRaw;
7use systemprompt_provider_contracts::{PageContext, PageDataProvider};
8
9#[derive(Debug, Clone, Copy, Default)]
10pub struct DefaultListBrandingProvider;
11
12#[async_trait]
13impl PageDataProvider for DefaultListBrandingProvider {
14 fn provider_id(&self) -> &'static str {
15 "default-list-branding"
16 }
17
18 async fn provide_page_data(&self, ctx: &PageContext<'_>) -> Result<Value> {
19 let Some(source_name) = ctx.page_type.strip_suffix("-list") else {
20 return Ok(serde_json::json!({}));
21 };
22
23 let content_config = ctx
24 .content_config::<ContentConfigRaw>()
25 .ok_or_else(|| anyhow::anyhow!("ContentConfigRaw not available in PageContext"))?;
26
27 let source = content_config.content_sources.get(source_name);
28 let org = &content_config.metadata.structured_data.organization;
29 let language = &content_config.metadata.language;
30 let branding = &ctx.web_config.branding;
31
32 let source_branding = source.and_then(|s| s.branding.as_ref());
33
34 let blog_name = source_branding
35 .and_then(|b| b.name.as_deref())
36 .unwrap_or(&branding.name);
37
38 let blog_description = source_branding
39 .and_then(|b| b.description.as_deref())
40 .unwrap_or(&branding.description);
41
42 let blog_image = source_branding
43 .and_then(|b| b.image.as_deref())
44 .map_or_else(String::new, |img| format!("{}{}", org.url, img));
45
46 let blog_keywords = source_branding
47 .and_then(|b| b.keywords.as_deref())
48 .unwrap_or("");
49
50 Ok(serde_json::json!({
51 "BLOG_NAME": blog_name,
52 "BLOG_DESCRIPTION": blog_description,
53 "BLOG_IMAGE": blog_image,
54 "BLOG_KEYWORDS": blog_keywords,
55 "BLOG_URL": format!("{}/{}", org.url, source_name),
56 "BLOG_LANGUAGE": language,
57 }))
58 }
59}
60
61pub fn default_list_branding_provider() -> Arc<dyn PageDataProvider> {
62 Arc::new(DefaultListBrandingProvider)
63}