systemprompt_cli/commands/core/content/
popular.rs1use super::types::{ContentSummary, PopularOutput};
7use crate::cli_settings::CliConfig;
8use crate::shared::CommandOutput;
9use anyhow::{Result, anyhow};
10use clap::Args;
11use systemprompt_content::ContentRepository;
12use systemprompt_database::DbPool;
13use systemprompt_identifiers::SourceId;
14
15use crate::context::CommandContext;
16
17fn parse_duration(s: &str) -> Result<i64> {
18 let s = s.trim().to_lowercase();
19 if s.ends_with('d') {
20 s[..s.len() - 1]
21 .parse::<i64>()
22 .map_err(|_e| anyhow!("Invalid duration format: {}", s))
23 } else if s.ends_with('w') {
24 s[..s.len() - 1]
25 .parse::<i64>()
26 .map(|w| w * 7)
27 .map_err(|_e| anyhow!("Invalid duration format: {}", s))
28 } else {
29 s.parse::<i64>().map_err(|_e| {
30 anyhow!(
31 "Invalid duration format: {}. Use '7d', '30d', '1w', etc.",
32 s
33 )
34 })
35 }
36}
37
38#[derive(Debug, Args)]
39pub struct PopularArgs {
40 #[arg(help = "Source ID")]
41 pub source: String,
42
43 #[arg(long, default_value = "30d", help = "Time period (e.g., 7d, 30d, 1w)")]
44 pub since: String,
45
46 #[arg(long, default_value = "10")]
47 pub limit: i64,
48}
49
50pub async fn execute(args: PopularArgs, ctx: &CommandContext) -> Result<CommandOutput> {
51 execute_with_pool(args, &ctx.db_pool().await?, &ctx.cli).await
52}
53
54pub async fn execute_with_pool(
55 args: PopularArgs,
56 pool: &DbPool,
57 _config: &CliConfig,
58) -> Result<CommandOutput> {
59 let repo = ContentRepository::new(pool)?;
60
61 let source = SourceId::new(args.source.clone());
62 let days_i64 = parse_duration(&args.since)?;
63 let days = i32::try_from(days_i64).map_err(|_e| anyhow!("Duration too large"))?;
64
65 let content_ids = repo
66 .get_popular_content_ids(&source, days, args.limit)
67 .await?;
68
69 let mut items = Vec::with_capacity(content_ids.len());
70 for id in content_ids {
71 if let Some(content) = repo.get_by_id(&id).await? {
72 items.push(ContentSummary {
73 id: content.id,
74 slug: content.slug,
75 title: content.title,
76 kind: content.kind,
77 source_id: content.source_id,
78 category_id: content.category_id,
79 published_at: Some(content.published_at),
80 });
81 }
82 }
83
84 let output = PopularOutput {
85 items,
86 source_id: source,
87 days: days_i64,
88 };
89
90 Ok(
91 CommandOutput::table_of(vec!["id", "title", "kind", "published_at"], &output.items)
92 .with_title("Popular Content"),
93 )
94}