Skip to main content

systemprompt_cli/commands/core/content/
delete.rs

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