Skip to main content

systemprompt_content/repository/content/
mod.rs

1//! Content repository.
2//!
3//! [`ContentRepository`] is the data-access surface for the `markdown_content`
4//! table, splitting read paths (queries) from write paths (mutations) and
5//! routing each to the read or write pool accordingly.
6
7mod mutations;
8mod queries;
9
10use crate::error::ContentError;
11use crate::models::{Content, CreateContentParams, UpdateContentParams};
12use sqlx::PgPool;
13use std::sync::Arc;
14use systemprompt_database::DbPool;
15use systemprompt_identifiers::{CategoryId, ContentId, LocaleCode, SourceId};
16
17#[derive(Debug)]
18pub struct ContentRepository {
19    pool: Arc<PgPool>,
20    write_pool: Arc<PgPool>,
21}
22
23impl ContentRepository {
24    pub fn new(db: &DbPool) -> Result<Self, ContentError> {
25        let pool = db
26            .pool_arc()
27            .map_err(|e| ContentError::InvalidRequest(format!("Database pool error: {e}")))?;
28        let write_pool = db
29            .write_pool_arc()
30            .map_err(|e| ContentError::InvalidRequest(format!("Database write pool error: {e}")))?;
31        Ok(Self { pool, write_pool })
32    }
33
34    pub async fn create(&self, params: &CreateContentParams) -> Result<Content, sqlx::Error> {
35        mutations::create(&self.write_pool, params).await
36    }
37
38    pub async fn get_by_id(&self, id: &ContentId) -> Result<Option<Content>, sqlx::Error> {
39        queries::get_by_id(&self.pool, id).await
40    }
41
42    pub async fn get_by_slug(
43        &self,
44        slug: &str,
45        locale: &LocaleCode,
46    ) -> Result<Option<Content>, sqlx::Error> {
47        queries::get_by_slug(&self.pool, slug, locale).await
48    }
49
50    pub async fn get_by_source_and_slug(
51        &self,
52        source_id: &SourceId,
53        slug: &str,
54        locale: &LocaleCode,
55    ) -> Result<Option<Content>, sqlx::Error> {
56        queries::get_by_source_and_slug(&self.pool, source_id, slug, locale).await
57    }
58
59    pub async fn list(&self, limit: i64, offset: i64) -> Result<Vec<Content>, sqlx::Error> {
60        queries::list(&self.pool, limit, offset).await
61    }
62
63    pub async fn list_by_source(
64        &self,
65        source_id: &SourceId,
66        locale: &LocaleCode,
67    ) -> Result<Vec<Content>, sqlx::Error> {
68        queries::list_by_source(&self.pool, source_id, locale).await
69    }
70
71    pub async fn list_by_source_limited(
72        &self,
73        source_id: &SourceId,
74        locale: &LocaleCode,
75        limit: i64,
76    ) -> Result<Vec<Content>, sqlx::Error> {
77        queries::list_by_source_limited(&self.pool, source_id, locale, limit).await
78    }
79
80    pub async fn find_sources_by_slug(
81        &self,
82        slug: &str,
83        locale: &LocaleCode,
84    ) -> Result<Vec<SourceId>, sqlx::Error> {
85        queries::find_sources_by_slug(&self.pool, slug, locale).await
86    }
87
88    pub async fn list_slugs_with_locales_by_source(
89        &self,
90        source_id: &SourceId,
91    ) -> Result<Vec<(String, LocaleCode)>, sqlx::Error> {
92        queries::list_slugs_with_locales_by_source(&self.pool, source_id).await
93    }
94
95    pub async fn update(&self, params: &UpdateContentParams) -> Result<Content, sqlx::Error> {
96        mutations::update(&self.write_pool, params).await
97    }
98
99    pub async fn category_exists(&self, category_id: &CategoryId) -> Result<bool, sqlx::Error> {
100        queries::category_exists(&self.pool, category_id).await
101    }
102
103    pub async fn delete(&self, id: &ContentId) -> Result<(), sqlx::Error> {
104        mutations::delete(&self.write_pool, id).await
105    }
106
107    pub async fn delete_by_source(&self, source_id: &SourceId) -> Result<u64, sqlx::Error> {
108        mutations::delete_by_source(&self.write_pool, source_id).await
109    }
110
111    pub async fn list_all(&self, limit: i64, offset: i64) -> Result<Vec<Content>, sqlx::Error> {
112        queries::list_all(&self.pool, limit, offset).await
113    }
114
115    pub async fn get_popular_content_ids(
116        &self,
117        source_id: &SourceId,
118        days: i32,
119        limit: i64,
120    ) -> Result<Vec<ContentId>, sqlx::Error> {
121        queries::get_popular_content_ids(&self.pool, source_id, days, limit).await
122    }
123}