From fc917bf829c8a7cbf950754abc7085ffb00dbf17 Mon Sep 17 00:00:00 2001 From: Lars Wirzenius Date: Thu, 8 Apr 2021 08:24:40 +0300 Subject: feat! add support for tera templates for new journal entries There is a hardcoded default template, plus one can be overridden per journal by adding a .config/templates/new_entry file in the journal. --- src/error.rs | 8 ++++++++ src/journal.rs | 18 +++++++++++++++--- src/lib.rs | 1 + src/template.rs | 45 +++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 69 insertions(+), 3 deletions(-) create mode 100644 src/template.rs (limited to 'src') diff --git a/src/error.rs b/src/error.rs index 199d1e4..a6f239e 100644 --- a/src/error.rs +++ b/src/error.rs @@ -61,4 +61,12 @@ pub enum JournalError { /// Editor failed. #[error("editor {0} failed: {1}")] EditorFailed(PathBuf, String), + + /// Template not found. + #[error("template not found: {0}")] + TemplateNotFound(String), + + /// Failed to render a Tera template. + #[error("template {0} failed to render: {1}")] + TemplateRender(String, #[source] tera::Error), } diff --git a/src/journal.rs b/src/journal.rs index f58e49d..e5a692e 100644 --- a/src/journal.rs +++ b/src/journal.rs @@ -1,13 +1,16 @@ use crate::error::JournalError; +use crate::template::Templates; use chrono::Local; use std::path::{Path, PathBuf}; use std::process::Command; +use tera::Context; const MAX_DRAFT_COUNT: usize = 1000; pub struct Journal { dirname: PathBuf, entries: PathBuf, + templates: Templates, } impl Journal { @@ -23,6 +26,7 @@ impl Journal { Ok(Self { dirname: path.to_path_buf(), entries: entries.to_path_buf(), + templates: Templates::new(path)?, }) } @@ -30,7 +34,12 @@ impl Journal { if Self::is_journal(path, entries) { let dirname = path.to_path_buf(); let entries = entries.to_path_buf(); - Ok(Self { dirname, entries }) + let templates = Templates::new(path)?; + Ok(Self { + dirname, + entries, + templates, + }) } else { Err(JournalError::NotAJournal(path.display().to_string())) } @@ -55,9 +64,12 @@ impl Journal { .map_err(|err| JournalError::CreateDirectory(drafts.to_path_buf(), err))?; } + let mut context = Context::new(); + context.insert("title", title); + let pathname = self.pick_file_id(&drafts)?; - let text = format!(r#"[[!meta title="{}"]]"#, title); - std::fs::write(&pathname, format!("{}\n\n", text)) + let text = self.templates.new_draft(&context)?; + std::fs::write(&pathname, text) .map_err(|err| JournalError::WriteEntry(pathname.to_path_buf(), err))?; self.edit(editor, &pathname)?; Ok(()) diff --git a/src/lib.rs b/src/lib.rs index e988c12..d9b0801 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,3 +2,4 @@ pub mod config; pub mod error; pub mod journal; pub mod opt; +pub mod template; diff --git a/src/template.rs b/src/template.rs new file mode 100644 index 0000000..1ad8a54 --- /dev/null +++ b/src/template.rs @@ -0,0 +1,45 @@ +use crate::error::JournalError; +use std::path::Path; +use tera::{Context, Tera}; + +const NEW_ENTRY: &str = r#"[[!meta title="{{ title }}"]] + +This is the default template. +"#; + +pub struct Templates { + tera: Tera, +} + +impl Templates { + pub fn new(dirname: &Path) -> Result { + let glob = format!("{}/.config/templates/*", dirname.display()); + let mut tera = Tera::new(&glob).expect("Tera::new"); + add_default_template(&mut tera, "new_entry", NEW_ENTRY); + Ok(Self { tera }) + } + + pub fn new_draft(&self, context: &Context) -> Result { + self.render("new_entry", &context) + } + + fn render(&self, name: &str, context: &Context) -> Result { + match self.tera.render(name, &context) { + Ok(s) => Ok(s), + Err(e) => match e.kind { + tera::ErrorKind::TemplateNotFound(x) => Err(JournalError::TemplateNotFound(x)), + _ => Err(JournalError::TemplateRender(name.to_string(), e)), + }, + } + } +} + +fn add_default_template(tera: &mut Tera, name: &str, template: &str) { + let context = Context::new(); + if let Err(err) = tera.render(name, &context) { + if let tera::ErrorKind::TemplateNotFound(_) = err.kind { + tera.add_raw_template(name, template) + .expect("Tera::add_raw_template"); + } + } +} -- cgit v1.2.1