spin_templates/
store.rs
1use anyhow::Context;
2use spin_common::data_dir::data_dir;
3use std::path::{Path, PathBuf};
4
5use crate::directory::subdirectories;
6
7pub(crate) struct TemplateStore {
8 root: PathBuf,
9}
10
11lazy_static::lazy_static! {
12 static ref UNSAFE_CHARACTERS: regex::Regex = regex::Regex::new("[^-_a-zA-Z0-9]").expect("Invalid identifier regex");
13}
14
15impl TemplateStore {
16 pub(crate) fn new(root: impl AsRef<Path>) -> Self {
17 Self {
18 root: root.as_ref().to_owned(),
19 }
20 }
21
22 pub(crate) fn try_default() -> anyhow::Result<Self> {
23 Ok(Self::new(data_dir()?.join("templates")))
24 }
25
26 pub(crate) fn get_directory(&self, id: impl AsRef<str>) -> PathBuf {
27 self.root.join(Self::relative_dir(id.as_ref()))
28 }
29
30 pub(crate) fn get_layout(&self, id: impl AsRef<str>) -> Option<TemplateLayout> {
31 let template_dir = self.get_directory(id);
32 if template_dir.exists() {
33 Some(TemplateLayout::new(&template_dir))
34 } else {
35 None
36 }
37 }
38
39 pub(crate) async fn list_layouts(&self) -> anyhow::Result<Vec<TemplateLayout>> {
40 if !self.root.exists() {
41 return Ok(vec![]);
42 }
43
44 let template_dirs = subdirectories(&self.root).with_context(|| {
45 format!(
46 "Failed to read template directories from {}",
47 self.root.display()
48 )
49 })?;
50
51 Ok(template_dirs.iter().map(TemplateLayout::new).collect())
52 }
53
54 fn relative_dir(id: &str) -> impl AsRef<Path> {
55 let id_sha256 = spin_common::sha256::hex_digest_from_bytes(id);
60 format!("{}_{}", UNSAFE_CHARACTERS.replace_all(id, "_"), id_sha256)
61 }
62}
63
64pub(crate) struct TemplateLayout {
65 template_dir: PathBuf,
66}
67
68const METADATA_DIR_NAME: &str = "metadata";
69const CONTENT_DIR_NAME: &str = "content";
70const SNIPPETS_DIR_NAME: &str = "snippets";
71
72const MANIFEST_FILE_NAME: &str = "spin-template.toml";
73
74const INSTALLATION_RECORD_FILE_NAME: &str = ".install.toml";
75
76impl TemplateLayout {
77 pub fn new(template_dir: impl AsRef<Path>) -> Self {
78 Self {
79 template_dir: template_dir.as_ref().to_owned(),
80 }
81 }
82
83 pub fn metadata_dir(&self) -> PathBuf {
84 self.template_dir.join(METADATA_DIR_NAME)
85 }
86
87 pub fn manifest_path(&self) -> PathBuf {
88 self.metadata_dir().join(MANIFEST_FILE_NAME)
89 }
90
91 pub fn content_dir(&self) -> PathBuf {
92 self.template_dir.join(CONTENT_DIR_NAME)
93 }
94
95 pub fn snippets_dir(&self) -> PathBuf {
96 self.metadata_dir().join(SNIPPETS_DIR_NAME)
97 }
98
99 pub fn installation_record_file(&self) -> PathBuf {
100 self.template_dir.join(INSTALLATION_RECORD_FILE_NAME)
101 }
102}