tsk/commands/
templates.rs

1use super::Command;
2use crate::assets::{AssetManager, layered::LayeredAssetManager};
3use crate::context::AppContext;
4use crate::repo_utils::find_repository_root;
5use async_trait::async_trait;
6use std::error::Error;
7use std::path::Path;
8use tabled::settings::Style;
9use tabled::{Table, Tabled};
10
11pub struct TemplatesCommand;
12
13#[derive(Tabled)]
14struct TemplateRow {
15    #[tabled(rename = "Name")]
16    name: String,
17    #[tabled(rename = "Source")]
18    source: String,
19}
20
21#[async_trait]
22impl Command for TemplatesCommand {
23    async fn execute(&self, ctx: &AppContext) -> Result<(), Box<dyn Error>> {
24        let project_root = find_repository_root(Path::new(".")).ok();
25
26        // Create asset manager on-demand
27        let asset_manager = LayeredAssetManager::new_with_standard_layers(
28            project_root.as_deref(),
29            &ctx.xdg_directories(),
30        );
31
32        // List all available templates
33        let templates = asset_manager.list_templates();
34
35        if templates.is_empty() {
36            println!("No templates available");
37            return Ok(());
38        }
39
40        // Determine source for each template
41        let mut rows = Vec::new();
42        for template in &templates {
43            let source = determine_template_source(template, project_root.as_deref(), ctx)?;
44            rows.push(TemplateRow {
45                name: template.to_string(),
46                source,
47            });
48        }
49
50        let table = Table::new(rows).with(Style::modern()).to_string();
51        println!("Available Templates:");
52        println!("{table}");
53
54        // Print additional information
55        println!("\nTemplate locations (in priority order):");
56        if let Some(root) = &project_root {
57            println!("  1. Project: {}/.tsk/templates/", root.display());
58        }
59        println!(
60            "  2. User: {}/templates/",
61            ctx.xdg_directories().config_dir().display()
62        );
63        println!("  3. Built-in: Embedded in TSK binary");
64
65        Ok(())
66    }
67}
68
69fn determine_template_source(
70    template_name: &str,
71    project_root: Option<&Path>,
72    ctx: &AppContext,
73) -> Result<String, Box<dyn Error>> {
74    // Check project level first
75    if let Some(root) = project_root {
76        let project_template = root
77            .join(".tsk")
78            .join("templates")
79            .join(format!("{template_name}.md"));
80        if project_template.exists() {
81            return Ok("Project".to_string());
82        }
83    }
84
85    // Check user level
86    let user_template = ctx
87        .xdg_directories()
88        .config_dir()
89        .join("templates")
90        .join(format!("{template_name}.md"));
91    if user_template.exists() {
92        return Ok("User".to_string());
93    }
94
95    // Must be built-in
96    Ok("Built-in".to_string())
97}