systemprompt-content 0.2.2

Markdown content management, sources, and event tracking for systemprompt.io AI governance dashboards. Governed publishing pipeline for the MCP governance platform.
Documentation
use crate::error::ContentError;
use crate::models::{SearchRequest, SearchResponse, SearchResult};
use crate::repository::{ContentRepository, SearchRepository};
use systemprompt_database::DbPool;
use systemprompt_identifiers::CategoryId;

const DEFAULT_SEARCH_LIMIT: i64 = 10;

#[derive(Debug)]
pub struct SearchService {
    search_repo: SearchRepository,
    content_repo: ContentRepository,
}

impl SearchService {
    pub fn new(db: &DbPool) -> Result<Self, ContentError> {
        Ok(Self {
            search_repo: SearchRepository::new(db)?,
            content_repo: ContentRepository::new(db)?,
        })
    }

    pub async fn search(&self, request: &SearchRequest) -> Result<SearchResponse, ContentError> {
        let limit = request.limit.unwrap_or(DEFAULT_SEARCH_LIMIT);

        let results = if let Some(filters) = &request.filters {
            if let Some(category_id) = &filters.category_id {
                self.search_repo
                    .search_by_category(category_id, limit)
                    .await?
            } else {
                vec![]
            }
        } else {
            let content_list = self.content_repo.list_all(limit, 0).await?;
            content_list
                .into_iter()
                .map(Self::content_to_search_result)
                .collect()
        };

        Ok(SearchResponse {
            total: results.len(),
            results,
        })
    }

    pub async fn search_by_category(
        &self,
        category_id: &CategoryId,
        limit: i64,
    ) -> Result<Vec<SearchResult>, ContentError> {
        Ok(self
            .search_repo
            .search_by_category(category_id, limit)
            .await?)
    }

    fn content_to_search_result(content: crate::models::Content) -> SearchResult {
        SearchResult {
            id: content.id,
            title: content.title,
            slug: content.slug,
            description: content.description,
            image: content.image,
            view_count: 0,
            source_id: content.source_id,
            category_id: content.category_id,
        }
    }
}