Skip to main content

systemprompt_cli/commands/core/content/
delete.rs

1//! `core content delete` command.
2//!
3//! Copyright (c) systemprompt.io — Business Source License 1.1.
4//! See <https://systemprompt.io> for licensing details.
5
6use super::types::DeleteOutput;
7use crate::context::CommandContext;
8use crate::interactive::require_confirmation;
9use crate::shared::CommandOutput;
10use anyhow::{Result, anyhow};
11use clap::Args;
12use systemprompt_content::ContentRepository;
13use systemprompt_identifiers::{ContentId, LocaleCode, SourceId};
14use systemprompt_logging::CliService;
15
16#[derive(Debug, Args)]
17pub struct DeleteArgs {
18    #[arg(help = "Content ID or slug")]
19    pub identifier: String,
20
21    #[arg(long, help = "Source ID (required when using slug)")]
22    pub source: Option<String>,
23
24    #[arg(short = 'y', long, help = "Skip confirmation")]
25    pub yes: bool,
26
27    #[arg(long, help = "Preview deletion without executing")]
28    pub dry_run: bool,
29}
30
31pub async fn execute(args: DeleteArgs, ctx: &CommandContext) -> Result<CommandOutput> {
32    let pool = ctx.db_pool().await?;
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 (use --source)"))?;
47        let source = SourceId::new(source_id.clone());
48        repo.get_by_source_and_slug(&source, &args.identifier, &LocaleCode::new("en"))
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    if args.dry_run {
60        let output = DeleteOutput {
61            deleted: false,
62            content_id: content.id.clone(),
63            message: Some(format!(
64                "[DRY-RUN] Would delete content '{}' ({})",
65                content.title, content.id
66            )),
67        };
68        return Ok(CommandOutput::card_value(
69            "Content Delete (Dry Run)",
70            &output,
71        ));
72    }
73
74    if ctx.cli.is_interactive() && !args.yes {
75        CliService::warning(&format!(
76            "This will permanently delete content: {}",
77            args.identifier
78        ));
79    }
80
81    require_confirmation(
82        ctx.prompter(),
83        "Are you sure you want to continue?",
84        args.yes,
85        &ctx.cli,
86    )?;
87
88    repo.delete(&content.id).await?;
89
90    let output = DeleteOutput {
91        deleted: true,
92        content_id: content.id.clone(),
93        message: None,
94    };
95
96    Ok(CommandOutput::card_value("Content Deleted", &output))
97}