invoice_cli/commands/
template.rs1use std::path::PathBuf;
2use std::process::Command;
3
4use crate::cli::TemplateCmd;
5use crate::error::{AppError, Result};
6use crate::output::{print_success, Ctx};
7use crate::typst_assets;
8
9pub fn run(cmd: TemplateCmd, ctx: Ctx) -> Result<()> {
10 match cmd {
11 TemplateCmd::List => {
12 let names = typst_assets::list_templates()?;
13 print_success(ctx, &names, |names| {
14 if names.is_empty() {
15 println!("no templates found. run: invoice doctor");
16 }
17 for n in names {
18 println!("{n}");
19 }
20 });
21 Ok(())
22 }
23 TemplateCmd::Preview { name, out } => {
24 typst_assets::ensure_extracted()?;
25 if !typst_assets::has_template(&name)? {
26 return Err(AppError::InvalidInput(format!(
27 "template '{name}' not found. Run: invoice template list"
28 )));
29 }
30 let template_path = typst_assets::template_path(&name)?;
31 let root = typst_assets::project_root()?;
32 let out_path = out
33 .map(PathBuf::from)
34 .unwrap_or_else(|| PathBuf::from(format!("preview-{}.pdf", name)));
35
36 let status = Command::new("typst")
37 .arg("compile")
38 .arg("--root")
39 .arg(&root)
40 .arg(&template_path)
41 .arg(&out_path)
42 .status()
43 .map_err(|e| AppError::Render(format!("typst binary not found: {e}")))?;
44
45 if !status.success() {
46 return Err(AppError::Render(format!(
47 "typst compile exited with {}",
48 status.code().unwrap_or(-1)
49 )));
50 }
51
52 print_success(
53 ctx,
54 &serde_json::json!({"template": name, "path": out_path}),
55 |_| println!("rendered → {}", out_path.display()),
56 );
57 Ok(())
58 }
59 }
60}