tsk/commands/
templates.rs1use 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 let asset_manager = LayeredAssetManager::new_with_standard_layers(
28 project_root.as_deref(),
29 &ctx.xdg_directories(),
30 );
31
32 let templates = asset_manager.list_templates();
34
35 if templates.is_empty() {
36 println!("No templates available");
37 return Ok(());
38 }
39
40 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 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 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 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 Ok("Built-in".to_string())
97}