catapulte_engine/loader/
local.rs

1use super::prelude::Template;
2use serde::Deserialize;
3use serde_json::Value as JsonValue;
4use std::fs::{read_to_string, File};
5use std::io::BufReader;
6use std::path::PathBuf;
7
8#[derive(Clone, Debug, serde::Deserialize)]
9pub struct Config {
10    pub path: PathBuf,
11}
12
13impl Default for Config {
14    fn default() -> Self {
15        Self {
16            path: PathBuf::new().join("template"),
17        }
18    }
19}
20
21impl From<Config> for LocalLoader {
22    fn from(value: Config) -> Self {
23        Self::new(value.path)
24    }
25}
26
27fn default_template_path() -> String {
28    "template.mjml".into()
29}
30
31#[derive(Debug, Deserialize)]
32pub struct LocalMetadata {
33    name: String,
34    description: String,
35    #[serde(default = "default_template_path")]
36    template: String,
37    attributes: JsonValue,
38}
39
40#[derive(Debug, thiserror::Error)]
41pub enum Error {
42    #[error("Unable to open metadata file: {0:?}")]
43    MetadataOpenFailed(std::io::Error),
44    #[error("Unable to deserialize metadata file: {0:?}")]
45    MetadataFormatInvalid(serde_json::Error),
46    #[error("Unable to open template file: {0:?}")]
47    TemplateOpenFailed(std::io::Error),
48}
49
50#[derive(Debug)]
51pub struct LocalLoader {
52    root: PathBuf,
53}
54
55impl LocalLoader {
56    pub fn new(root: PathBuf) -> Self {
57        Self { root }
58    }
59}
60
61impl LocalLoader {
62    pub(super) async fn find_by_name(&self, name: &str) -> Result<Template, Error> {
63        tracing::debug!("loading template {}", name);
64        let path = self.root.join(name).join("metadata.json");
65        let metadata_file = File::open(path).map_err(|err| {
66            metrics::counter!("template_provider_error", "reason" => "metadata_not_found")
67                .increment(1);
68            tracing::debug!("template provider error: metadata not found ({:?})", err);
69            Error::MetadataOpenFailed(err)
70        })?;
71        let metadata_reader = BufReader::new(metadata_file);
72        let metadata: LocalMetadata = serde_json::from_reader(metadata_reader).map_err(|err| {
73            metrics::counter!("template_provider_error", "reason" => "metadata_invalid")
74                .increment(1);
75            tracing::debug!("template provider error: metadata invalid ({:?})", err);
76            Error::MetadataFormatInvalid(err)
77        })?;
78        let template_path = self.root.join(name).join(metadata.template);
79        let content = read_to_string(template_path).map_err(|err| {
80            metrics::counter!("template_provider_error", "reason" => "template_not_found")
81                .increment(1);
82            tracing::debug!("template provider error: template not found ({:?})", err);
83            Error::TemplateOpenFailed(err)
84        })?;
85        Ok(Template {
86            name: metadata.name,
87            description: metadata.description,
88            content,
89            attributes: metadata.attributes,
90        })
91    }
92}