systemprompt_cli/commands/core/content/
list.rs1use super::types::{ContentListOutput, ContentSummary};
2use crate::cli_settings::CliConfig;
3use crate::context::CommandContext;
4use crate::shared::CommandOutput;
5use anyhow::Result;
6use clap::Args;
7use systemprompt_content::ContentRepository;
8use systemprompt_database::DbPool;
9use systemprompt_identifiers::{LocaleCode, SourceId};
10
11#[derive(Debug, Args)]
12pub struct ListArgs {
13 #[arg(long, help = "Filter by source ID")]
14 pub source: Option<String>,
15
16 #[arg(long, help = "Filter by category ID")]
17 pub category: Option<String>,
18
19 #[arg(long, default_value = "20")]
20 pub limit: i64,
21
22 #[arg(long, default_value = "0")]
23 pub offset: i64,
24}
25
26pub async fn execute(args: ListArgs, ctx: &CommandContext) -> Result<CommandOutput> {
27 execute_with_pool(args, &ctx.db_pool().await?, &ctx.cli).await
28}
29
30pub async fn execute_with_pool(
31 args: ListArgs,
32 pool: &DbPool,
33 _config: &CliConfig,
34) -> Result<CommandOutput> {
35 let repo = ContentRepository::new(pool)?;
36
37 let items = match &args.source {
38 Some(source_id) => {
39 let source = SourceId::new(source_id.clone());
40 repo.list_by_source(&source, &LocaleCode::new("en")).await?
41 },
42 None => repo.list(args.limit, args.offset).await?,
43 };
44
45 let summaries: Vec<ContentSummary> = items
46 .into_iter()
47 .filter(|c| {
48 args.category.as_ref().is_none_or(|cat| {
49 c.category_id
50 .as_ref()
51 .is_some_and(|cid| cid.as_str() == cat)
52 })
53 })
54 .map(|c| ContentSummary {
55 id: c.id,
56 slug: c.slug,
57 title: c.title,
58 kind: c.kind,
59 source_id: c.source_id,
60 category_id: c.category_id,
61 published_at: Some(c.published_at),
62 })
63 .collect();
64
65 let total = summaries.len() as i64;
66
67 let output = ContentListOutput {
68 items: summaries,
69 total,
70 limit: args.limit,
71 offset: args.offset,
72 };
73
74 Ok(CommandOutput::table_of(
75 vec!["id", "title", "kind", "source_id", "published_at"],
76 &output.items,
77 )
78 .with_title("Content"))
79}