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