systemprompt_cli/commands/core/content/
show.rs1use super::types::ContentDetailOutput;
2use crate::cli_settings::CliConfig;
3use crate::shared::CommandResult;
4use anyhow::{anyhow, Result};
5use clap::Args;
6use systemprompt_content::ContentRepository;
7use systemprompt_database::DbPool;
8use systemprompt_identifiers::{ContentId, SourceId};
9use systemprompt_runtime::AppContext;
10
11#[derive(Debug, Args)]
12pub struct ShowArgs {
13 #[arg(help = "Content ID or slug")]
14 pub identifier: String,
15
16 #[arg(long, help = "Source ID (required when using slug)")]
17 pub source: Option<String>,
18}
19
20pub async fn execute(
21 args: ShowArgs,
22 config: &CliConfig,
23) -> Result<CommandResult<ContentDetailOutput>> {
24 let ctx = AppContext::new().await?;
25 execute_with_pool(args, ctx.db_pool(), config).await
26}
27
28pub async fn execute_with_pool(
29 args: ShowArgs,
30 pool: &DbPool,
31 _config: &CliConfig,
32) -> Result<CommandResult<ContentDetailOutput>> {
33 let repo = ContentRepository::new(pool)?;
34
35 let content = if args.identifier.starts_with("content_")
36 || args.identifier.contains('-') && args.identifier.len() > 30
37 {
38 let id = ContentId::new(args.identifier.clone());
39 repo.get_by_id(&id)
40 .await?
41 .ok_or_else(|| anyhow!("Content not found: {}", args.identifier))?
42 } else {
43 let source_id = args
44 .source
45 .as_ref()
46 .ok_or_else(|| anyhow!("Source ID required when using slug"))?;
47 let source = SourceId::new(source_id.clone());
48 repo.get_by_source_and_slug(&source, &args.identifier)
49 .await?
50 .ok_or_else(|| {
51 anyhow!(
52 "Content not found: {} in source {}",
53 args.identifier,
54 source_id
55 )
56 })?
57 };
58
59 let keywords: Vec<String> = content
60 .keywords
61 .split(',')
62 .map(|s| s.trim().to_string())
63 .filter(|s| !s.is_empty())
64 .collect();
65
66 let output = ContentDetailOutput {
67 id: content.id,
68 slug: content.slug,
69 title: content.title,
70 description: if content.description.is_empty() {
71 None
72 } else {
73 Some(content.description)
74 },
75 body: content.body,
76 author: if content.author.is_empty() {
77 None
78 } else {
79 Some(content.author)
80 },
81 published_at: Some(content.published_at),
82 keywords,
83 kind: content.kind,
84 image: content.image,
85 category_id: content.category_id,
86 source_id: content.source_id,
87 version_hash: content.version_hash,
88 is_public: content.public,
89 updated_at: content.updated_at,
90 };
91
92 Ok(CommandResult::card(output).with_title("Content Details"))
93}