Skip to main content

systemprompt_cli/commands/core/content/
delete.rs

1use super::types::DeleteOutput;
2use crate::context::CommandContext;
3use crate::interactive::require_confirmation;
4use crate::shared::CommandOutput;
5use anyhow::{Result, anyhow};
6use clap::Args;
7use systemprompt_content::ContentRepository;
8use systemprompt_identifiers::{ContentId, LocaleCode, SourceId};
9use systemprompt_logging::CliService;
10
11#[derive(Debug, Args)]
12pub struct DeleteArgs {
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    #[arg(short = 'y', long, help = "Skip confirmation")]
20    pub yes: bool,
21
22    #[arg(long, help = "Preview deletion without executing")]
23    pub dry_run: bool,
24}
25
26pub async fn execute(args: DeleteArgs, ctx: &CommandContext) -> Result<CommandOutput> {
27    let pool = ctx.db_pool().await?;
28    let repo = ContentRepository::new(&pool)?;
29
30    let content = if args.identifier.starts_with("content_")
31        || args.identifier.contains('-') && args.identifier.len() > 30
32    {
33        let id = ContentId::new(args.identifier.clone());
34        repo.get_by_id(&id)
35            .await?
36            .ok_or_else(|| anyhow!("Content not found: {}", args.identifier))?
37    } else {
38        let source_id = args
39            .source
40            .as_ref()
41            .ok_or_else(|| anyhow!("Source ID required when using slug (use --source)"))?;
42        let source = SourceId::new(source_id.clone());
43        repo.get_by_source_and_slug(&source, &args.identifier, &LocaleCode::new("en"))
44            .await?
45            .ok_or_else(|| {
46                anyhow!(
47                    "Content not found: {} in source {}",
48                    args.identifier,
49                    source_id
50                )
51            })?
52    };
53
54    if args.dry_run {
55        let output = DeleteOutput {
56            deleted: false,
57            content_id: content.id.clone(),
58            message: Some(format!(
59                "[DRY-RUN] Would delete content '{}' ({})",
60                content.title, content.id
61            )),
62        };
63        return Ok(CommandOutput::card_value(
64            "Content Delete (Dry Run)",
65            &output,
66        ));
67    }
68
69    if ctx.cli.is_interactive() && !args.yes {
70        CliService::warning(&format!(
71            "This will permanently delete content: {}",
72            args.identifier
73        ));
74    }
75
76    require_confirmation("Are you sure you want to continue?", args.yes, &ctx.cli)?;
77
78    repo.delete(&content.id).await?;
79
80    let output = DeleteOutput {
81        deleted: true,
82        content_id: content.id.clone(),
83        message: None,
84    };
85
86    Ok(CommandOutput::card_value("Content Deleted", &output))
87}