Skip to main content

systemprompt_cli/commands/web/templates/
delete.rs

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