invoice_cli/
typst_assets.rs1use rust_embed::RustEmbed;
8use std::path::Path;
9
10use crate::config;
11use crate::error::Result;
12
13#[derive(RustEmbed)]
14#[folder = "typst/"]
15#[prefix = ""]
16pub struct Assets;
17
18pub fn ensure_extracted() -> Result<()> {
19 let root = config::assets_path()?;
20 std::fs::create_dir_all(&root)?;
21 for path in Assets::iter() {
22 let file = Assets::get(&path).expect("embedded asset");
23 let dest = root.join(path.as_ref());
24 if let Some(parent) = dest.parent() {
25 std::fs::create_dir_all(parent)?;
26 }
27 let needs_write = match std::fs::read(&dest) {
29 Ok(existing) => existing != file.data.as_ref(),
30 Err(_) => true,
31 };
32 if needs_write {
33 std::fs::write(&dest, file.data.as_ref())?;
34 }
35 }
36 Ok(())
37}
38
39pub fn template_dir() -> Result<std::path::PathBuf> {
40 Ok(config::assets_path()?.join("templates"))
41}
42
43pub fn shared_dir() -> Result<std::path::PathBuf> {
44 Ok(config::assets_path()?.join("shared"))
45}
46
47pub fn template_path(name: &str) -> Result<std::path::PathBuf> {
48 Ok(template_dir()?.join(format!("{name}.typ")))
49}
50
51pub fn list_templates() -> Result<Vec<String>> {
52 let dir = template_dir()?;
53 let mut names = Vec::new();
54 if dir.exists() {
55 for entry in std::fs::read_dir(&dir)? {
56 let entry = entry?;
57 let path = entry.path();
58 if path.extension().and_then(|s| s.to_str()) == Some("typ") {
59 if let Some(name) = path.file_stem().and_then(|s| s.to_str()) {
60 if !name.ends_with("-stress") {
62 names.push(name.to_string());
63 }
64 }
65 }
66 }
67 }
68 names.sort();
69 Ok(names)
70}
71
72pub fn has_template(name: &str) -> Result<bool> {
73 Ok(template_path(name)?.exists())
74}
75
76pub fn project_root() -> Result<std::path::PathBuf> {
77 config::assets_path()
78}
79
80pub fn is_within_root(path: &Path) -> Result<bool> {
81 let root = project_root()?;
82 Ok(path.starts_with(&root))
83}