Skip to main content

systemprompt_cli/commands/web/templates/
delete.rs

1//! `web templates delete` command.
2//!
3//! Copyright (c) systemprompt.io — Business Source License 1.1.
4//! See <https://systemprompt.io> for licensing details.
5
6use anyhow::{Context, Result, anyhow};
7use clap::Args;
8use std::fs;
9use std::path::Path;
10
11use crate::CliConfig;
12use crate::interactive::{Prompter, require_confirmation, resolve_required};
13use crate::shared::CommandOutput;
14use systemprompt_logging::CliService;
15
16use super::super::paths::WebPaths;
17use super::super::types::{TemplateDeleteOutput, TemplatesConfig};
18use super::selection::prompt_template_selection;
19
20#[derive(Debug, Args)]
21pub struct DeleteArgs {
22    #[arg(help = "Template name")]
23    pub name: Option<String>,
24
25    #[arg(short = 'y', long, help = "Skip confirmation prompts")]
26    pub yes: bool,
27
28    #[arg(long, help = "Also delete the .html file")]
29    pub delete_file: bool,
30}
31
32pub(super) fn execute(
33    args: DeleteArgs,
34    prompter: &dyn Prompter,
35    config: &CliConfig,
36) -> Result<CommandOutput> {
37    execute_in_dir(args, prompter, config, &WebPaths::resolve()?.templates)
38}
39
40pub fn execute_in_dir(
41    args: DeleteArgs,
42    prompter: &dyn Prompter,
43    config: &CliConfig,
44    templates_dir: &Path,
45) -> Result<CommandOutput> {
46    let templates_yaml_path = templates_dir.join("templates.yaml");
47
48    let yaml_content = fs::read_to_string(&templates_yaml_path).with_context(|| {
49        format!(
50            "Failed to read templates config at {}",
51            templates_yaml_path.display()
52        )
53    })?;
54
55    let mut templates_config: TemplatesConfig =
56        serde_yaml::from_str(&yaml_content).with_context(|| {
57            format!(
58                "Failed to parse templates config at {}",
59                templates_yaml_path.display()
60            )
61        })?;
62
63    let name = resolve_required(args.name, "name", config, || {
64        prompt_template_selection(prompter, &templates_config, "Select template to delete")
65    })?;
66
67    if !templates_config.templates.contains_key(&name) {
68        return Err(anyhow!("Template '{}' not found", name));
69    }
70
71    let confirm_msg = if args.delete_file {
72        format!("Delete template '{}' and its HTML file?", name)
73    } else {
74        format!("Delete template '{}'?", name)
75    };
76
77    require_confirmation(prompter, &confirm_msg, args.yes, config)?;
78
79    templates_config.templates.remove(&name);
80
81    let html_file_path = templates_dir.join(format!("{}.html", name));
82    let file_deleted = if args.delete_file && html_file_path.exists() {
83        fs::remove_file(&html_file_path)
84            .with_context(|| format!("Failed to delete HTML file: {}", html_file_path.display()))?;
85        true
86    } else {
87        false
88    };
89
90    let yaml = serde_yaml::to_string(&templates_config).context("Failed to serialize config")?;
91    fs::write(&templates_yaml_path, yaml).with_context(|| {
92        format!(
93            "Failed to write templates config to {}",
94            templates_yaml_path.display()
95        )
96    })?;
97
98    let message = if file_deleted {
99        format!("Template '{}' deleted (including HTML file)", name)
100    } else if html_file_path.exists() {
101        format!(
102            "Template '{}' deleted. HTML file still exists at {}",
103            name,
104            html_file_path.display()
105        )
106    } else {
107        format!("Template '{}' deleted", name)
108    };
109
110    CliService::success(&message);
111
112    let output = TemplateDeleteOutput {
113        deleted: name,
114        file_deleted,
115        message,
116    };
117
118    Ok(CommandOutput::card_value("Template Deleted", &output))
119}